Artificial Artificial - 1 month ago 6
Java Question

How to convert JSON String to Map

sorry for duplicating the question, but my problem is other.
I have JSON parser method where I parse from json-string to map. But json-string has a value which is json-string too. Something like that:

{
"status_code":"255",
"data":"{\"user\":{\"idpolzovatel\":1,\"id_poluch_tip\":1,\"fio_polzovatel\":\"Andrew Artificial\",\"login\":\"imi\",\"parol\":\"698d51a19d8a121ce581499d7b701668\",\"key\":null,\"nachalnik\":1,\"buhgalter\":0,\"delopr\":1},\"token\":\"230047517dd122c8f8116a6fa591a704\"}",
"message":"Successfull!"
}


So, my parse-method:

public Map<String, String> convertToMapFromJSON(String res){
ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> response = new HashMap<String, String>();
try {
response = objectMapper.readValue(res, new TypeReference<Map<String, String>>);
int t = 0;
} catch (IOException e) {
e.printStackTrace();
}
return response;
}


I get response in client:

ResponseEntity<String> responseEntity = restTemplate.postForEntity(REST_SERVICE_URI + "/auth/", data, String.class);


get body

String res = responseEntity.getBody();//получаем тело запроса в формате JSON


then use those method:

Map<String, String> response = convertToMapFromJSON(res);

Map<String, String> data1 = convertToMapFromJSON(response.get("data"));

Map<String, String> userDetailes = convertToMapFromJSON(data1.get("user"));


but, when I use last method
data1.get("user");
I get exception:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to java.lang.String


ok, got it. So,
data1.get("user")
isn't a string, it's linkedHashMap. So, I could do this then:

Map<String, String> userDetailes = data1.get("user");


? But then I get the error, where IDE say me, that
data1.get("user")
is a string.

Screenshot from debugger:
enter image description here

So, how can I get this LinkedHashMap with my userdata? Sorry, for my english. Thank you.

Answer

Java apply type erasure for generics. It checks type correctness at compile time and then remove generic signature in compile code (ByteCode). Therefore, there's no check at runtime.

See this example which have same behaviour as your JSON library:

/** Returns a generic map which all keys are string but not values **/
T <T extends Map> raw(Class<T> clazz) {
    Map object = new LinkedHashMap();
    object.put("string", "This is a String");
    object.put("map"   , new LinkedHashMap());
    return (T) object;
}

Here is your code:

/** codes you try to execute/write **/
void withStringValues() {
    Map<String,String> object = raw(Map<String,String>.class);
    String string = object.get("string"); // Ok
    String map    = object.get("map");    // ClassCastException
    Map    map    = object.get("map");    // Doesn't compile
}

As you can see the call to raw is considered valid as compiled code don't check for generics. But it makes an invalid and implicit cast from Map to Map<String,String> which actually doesn't occured in compiled code.

Generics are remove and is the compiled version:

void withTypeErasure() {
    Map object = raw(Map.class);
    String string = (String) object.get("string");
    String map    = (String) object.get("map");
}

As you can see, Java compiler has removed all generic and adds necessary casts. You can see what's going wrong here.

Your real code must look like this:

void withRealValues() {
    Map<String,Object> object = raw(Map<String,Object>.class);
    String             string = (String) object.get("string"); // Ok
    Map<String,Object> map    = (Map) object.get("map");       // Ok
}