Search code examples
javajsonjacksonfasterxmljackson2

Jackson global settings to deserialise array to custom list implementation


By default, Jackson is using java.util.ArrayList to deserialise a JSON array. Instead of this, I want to use a custom implementation. For example, Guava ImmutableList if value is present, or Collection.emptyList() if JSON array is empty or null.

I want to configure this globally for ObjectMapper. Is there an easy way to do this?

PS: My Jackson version is 2.9.7


Solution

  • General solution is to use custom module. You can define classes you would like to use for collections. For Guava there is a Maven module:

    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-guava</artifactId>
        <version>x.y.z</version>
    </dependency>
    

    Now, you can register your new module:

    ObjectMapper mapper = new ObjectMapper();
    // register module with object mapper
    mapper.registerModule(new GuavaModule());
    

    Now, you can define in your POJO you want to have immutable implementation of the list.

    class Pojo {
    
        private ImmutableList<Integer> ints;
    
        public ImmutableList<Integer> getInts() {
            return ints;
        }
    
        public void setInts(ImmutableList<Integer> ints) {
            this.ints = ints;
        }
    
        @Override
        public String toString() {
            return "Pojo{" +
                    "ints=" + ints + " " + ints.getClass() + '}';
        }
    }
    

    and below example:

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new GuavaModule());
    
    String json = "{\"ints\":[1,2,3,4]}";
    
    System.out.println(mapper.readValue(json, Pojo.class));
    

    prints:

    Pojo{ints=[1, 2, 3, 4] class com.google.common.collect.RegularImmutableList}
    

    If you do not want to tie your POJO classes with List implementation you need to add some extra configuration using SimpleModule class. So, your POJO looks like below:

    class Pojo {
    
        private List<Integer> ints;
    
        public List<Integer> getInts() {
            return ints;
        }
    
        public void setInts(List<Integer> ints) {
            this.ints = ints;
        }
    
        @Override
        public String toString() {
            return "Pojo{" +
                    "ints=" + ints + " " + ints.getClass() + '}';
        }
    }
    

    and your example looks like this:

    SimpleModule useImmutableList = new SimpleModule("UseImmutableList");
    useImmutableList.addAbstractTypeMapping(List.class, ImmutableList.class);
    
    GuavaModule module = new GuavaModule();
    
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(module);
    mapper.registerModule(useImmutableList);
    
    String json = "{\"ints\":[1,2,3,4]}";
    
    System.out.println(mapper.readValue(json, Pojo.class));
    

    Above code prints:

    Pojo{ints=[1, 2, 3, 4] class com.google.common.collect.RegularImmutableList}
    

    When you remove extra SimpleModule above code prints:

    Pojo{ints=[1, 2, 3, 4] class java.util.ArrayList}
    

    I do not see any point to use Collections.emptyList() in case it is empty. Guava's module uses RegularImmutableList for not empty and empty arrays.

    For converting null -> empty see this question:

    1. Jackson deserializer - change null collection to empty one

    but I recommend to set it to empty in POJO like below:

    private List<Integer> ints = Collections.emptyList();