In Java 8, I want to convert a JSON string to a map, and apply "complex" transformations to the keys. As an example, that "complex" transformation will simply be a lower case transformation.
Values in the input JSON could be strings or nested JSON objects.
My code is actually working, but I struggle to fix an unchecked cast warning.
Example
Example JSON input (String) :
{
"Key1": "value1",
"Key2": {
"Key2.1": "value2.1"
}
}
Desired output (Map) :
"key1" -> "value1"
"key2" ->
"key2.1" -> "value2.1"
1. JSON String -> Map
For that part, I used Jackson (2.9.8) and defined the following function :
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
Map<String, Object> convertJsonStringToMap(String json) throws IOException {
ObjectMapper mapper = new ObjectMapper();
TypeReference type = new TypeReference<Map<String, Object>>(){};
return mapper.readValue(json, type);
}
Since values can be strings or JSON objects, the return type of that function is Map<String, Object>. Note also that
readValue (in ObjectMapper class) uses generics, its signature is :
<T> T readValue(String content, TypeReference valueTypeRef)
2. Map -> Map with transformed keys (example : lower case)
I defined the following function :
Map<String, Object> transformKeys(Map<String, Object> map) {
Map<String, Object> result = new HashMap<>(map.size());
for (Map.Entry<String, Object> entry : map.entrySet()) {
Object value = entry.getValue();
if (value instanceof Map) {
value = transformKeys((Map<String, Object>) value);
}
// toLowerCase() is the transformation here (example), but I could have used something else
result.put(entry.getKey().toLowerCase(), value);
}
return result;
}
To handle nested maps, this function is recursive. But since it takes a Map<String, Object> as parameter,
I must cast value to Map<String, Object> to call the method recursively.
3. Putting all together
String json = "{\"Key1\": \"value1\", \"Key2\": { \"Key2.1\": \"value2.1\" }}";
Map<String, Object> initialMap = convertJsonStringToMap(json);
Map transformedMap = transformKeys(initialMap);
System.out.println(transformedMap);
This code works, and prints, as expected :
{key1=value1, key2={key2.1=value2.1}}
But this line in transformKeys function :
value = transformKeys((Map<String, Object>) value);
produces a warning :
[WARNING] App.java:[29,74] unchecked cast
required: java.util.Map<java.lang.String,java.lang.Object>
found: java.lang.Object
The warning is clear and I understand it (the compiler cannot know if value is really an instance of Map<String, Object>), but is there a way to get rid of it?
(No @SuppressWarnings please, nor -Xlint:none) :D
I feel like returning a Map<String, Object> from convertJsonStringToMap is
not the cleanest way to convert JSON String to a Map, but I can't find an other way to do it with Jackson.
You need to use
ObjectasMapvalue because it could be anotherMap,Listorprimitive(String,Integer, etc.).Jacksonallows also to manipulateJSONusingJsonNodetypes. We need to traverseJSON objectbut alsoJSON array(you forgot about it). In that case we need to:JSONtoJsonNode.JsonNodeAPI.MapSimple implementation:
Above code prints:
We got rid of unsafe casting and our implementation is more concise.