Search code examples
javaserializationjacksonguava

How can I preserve type info when serializing ImmutableMap using Jackson?


When I serialize HashMap, I can preserve the type info so that I can deserialize it to the corresponding type, but this doesn't work when serializing com.google.common.collect.ImmutableMap. How can I preserve type info?

public class ImmutableMapJacksonTest {

    public static void main(String[] args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper()
                .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        Map<String, String> map = new HashMap<>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        // ["java.util.HashMap",{"k1":"v1","k2":"v2"}]
        // preserve type info
        System.out.println(objectMapper.writeValueAsString(map));

        ImmutableMap<String, String> immutableMap = ImmutableMap.of("k1", "v1", "k2", "v2");
        // {"k1":"v1","k2":"v2"}
        // miss type info
        System.out.println(objectMapper.writeValueAsString(immutableMap));
    }
}

Solution

  • Add the following dependencies in pom.xml.

    <properties>
        <jackson.version>2.11.1</jackson.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-guava</artifactId>
            <version>${jackson.version}</version>
        </dependency>
    </dependencies>
    

    Then create ObjectMapper instance like below, you will get what you want.

    // https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization
    PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
            .allowIfSubType(Object.class)
            .build();
    ObjectMapper objectMapper = JsonMapper.builder()
            .addModule(new GuavaModule()) // https://github.com/FasterXML/jackson-datatypes-collections
            .activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING)
            .build();
    
    Map<String, String> map = new HashMap<>();
    map.put("k1", "v1");
    map.put("k2", "v2");
    // ["java.util.HashMap",{"k1":"v1","k2":"v2"}]
    System.out.println(objectMapper.writeValueAsString(map));
    
    ImmutableMap<String, String> immutableMap = ImmutableMap.of("k1", "v1", "k2", "v2");
    // ["com.google.common.collect.RegularImmutableMap",{"k1":"v1","k2":"v2"}]
    System.out.println(objectMapper.writeValueAsString(immutableMap));