I am trying to use Jackson's ObjectMapper class to serialize an object that looks like this:
TreeMap<String, String> mappings = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
but when the object is serialized, it ends up looking like this:
{"mappings": {"key": "value"}}
When deserializing, it loses the case insensitive property of the map. Does anyone know how to resolve this, or possibly a type of case insensitive map class which I can use to serialize and deserialize? Is there a Jackson mapper property that I can use to fix this issue?
Here is some sample code:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) throws JsonProcessingException {
TreeMap<String, String> mappings = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
mappings.put("Test3", "3");
mappings.put("test1", "1");
mappings.put("Test2", "2");
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(mappings);
System.out.println(json);
TreeMap<String, String> deserMappings = objectMapper.readValue(json, TreeMap.class);
System.out.println("Deserialized map case insensitive test: " + deserMappings.get("test3"));
}
}
And sample output:
{
"test1" : "1",
"Test2" : "2",
"Test3" : "3"
}
Deserialized map case insensitive test: null
After reading the comments and seeing how generic objects are deserialized, I think I can provide an answer that better addresses your problems:
String.CASE_INSENSITIVE_ORDERComparator (from post).String.CASE_INSENSITIVE_ORDERComparator (from comment section).Deserializing using the Map interface
When you don't use a specific implementation but an interface when deserializing Maps, the default implementation used by Jackson is a HashMap. This is because an interface is not a concrete class, while Jackson needs an actual Map implementation to deserialize the Json values somewhere. This is what was happening to you, judging from the initial problem described in the comments:
Deserializing generic objects
When deserializing generic objects, you should use an instance of the abstract class
TypeReferencethat subclasses the generic type you're trying to read. In your case:new TypeReference<TreeMap<String, String>>() {}. Like so, the argument type of theTypeReference(TreeMap<String, String>) is used by Jackson to reconstruct the json values ("test1", "1", "Test2", "2", ...) into the argument types (StringandString) of the generic type subclassed by theTypeReference. In your snippet, the code is still working only by coincidence, because in the absence of aTypeReference, Jackson reads simple values as String, while complex values (objects) asLinkedHashMap. Using aTypeReferencefor generic types is very important in general, but even more when deserializing complex objects (seeMyBeanexample below). You should always use aTypeReferencefor generic types.Sorting/retrieving elements with
String.CASE_INSENSITIVE_ORDERComparatorIn your code
deserMappings.get("test3"), I'm assuming but I'm not sure, that there might be a misunderstanding in the way you expect theComparatorparameter to work. The ComparatorString.CASE_INSENSITIVE_ORDERsupplied to the constructor is only used to establish an ordering between the elements of the TreeMap, it is not used to treat those elements in a case insensitive way. Therefore, when you try to retrieve an element with the key "test3", nothing is returned because there is no value identified by "test3", but there is by "Test3". The two strings "test3" and "Test3" are still different strings, because the givenComparatoris only used to define the order of how elements should be stored within theTreeMap.Maintaining
String.CASE_INSENSITIVE_ORDERComparatorThis is related to the comment:
The reason why the TreeMap read with the
ObjectMapperis not maintaining the original case insensitive order is because you're creating a whole new instance with a default order, which, in the case of String keys, is a case sensitive ordering. In your code, there is no point where you've defined a custom comparator for the secondTreeMapinstance (deserMappings).Recap Example
Here, I'm sharing an example that binds together the previous explanation with some code. Here is also a live version on OneCompiler.com: https://onecompiler.com/java/426cmfucg