Problem
In an existing application, I have to deserialize possibly deeply nested maps, and apply a custom Jackson de-serializer on all keys at all levels.
Since the application handles all kinds of datamodels dynamically, I cannot use an explicit datamodel with clearly typed maps... Instead, I use a Map<String, Object> and checks whether the deserialized Object value itself is a Map.
This causes the custom de-serializer to only apply to the top-level map.
Update: It's important to note that I cannot bind a deserializer to all maps because my datamodel often have more specific maps as well. I typically have fields that are a generic Map<String, Object> for open-ended JSON settings; living together with e.g. Map<EnumType, Double> for more explicit bits of configuration.
Example
To illustrate, I have come up with toy-example below:
ObjectMapper objectMapper = new ObjectMapper().registerModule(new SimpleModule()
.addKeyDeserializer(String.class, new KeyDeserializer() {
@Override
public Object deserializeKey(String s, DeserializationContext deserializationContext) {
return s.toUpperCase();
}
}));
Map<String, Object> value = objectMapper.readValue(
"{\"zero\": 1, \"two\": { \"three\": 4 }}",
new TypeReference<Map<String, Object>>() {}
);
assertThat(value).containsKeys("ZERO", "TWO"); // pass
assertThat((Map) value.get("TWO")).containsKey("THREE"); // fail
The last line of code fails:
java.lang.AssertionError: Expecting: <{"three"=4}> to contain key: <"THREE">
I cannot declare the type as Map<String, Map<String, Object>> because of the int value that is at the same level. If I try, I get:
Cannot deserialize instance of
java.util.LinkedHashMapout of VALUE_NUMBER_INT token
Questions
In this example, what is the best way for me to get upper-cased keys in all nested Maps, even though I need to declare them as Object?
Is there an alternative way to approach this?