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
Object
asMap
value because it could be anotherMap
,List
orprimitive
(String
,Integer
, etc.).Jackson
allows also to manipulateJSON
usingJsonNode
types. We need to traverseJSON object
but alsoJSON array
(you forgot about it). In that case we need to:JSON
toJsonNode
.JsonNode
API
.Map
Simple implementation:
Above code prints:
We got rid of unsafe casting and our implementation is more concise.