3

I am trying to deserialize a json context using Jackson 2.6.3. I have null literal in json content as below. I am trying to avoid that being added to the deserialized collection. See the output below. I tried various configuration options on the mapper. However I am unable to avoid null object in the collection. Any pointers to resolve the issue welcome.

I have seen many similar questions in stackoverflow like the one below, but all of them related to custom deserializing individual attributes in a class, but not an element in a collection.

How to deserialize JSON null to a NullNode instead of Java null?

JSON Content (Data.txt)

{
  "myList": [
  {
     "type": "1"
  },
  {
     "type": "2"
  },
  null
  ]
}

RootObject.java

@JsonInclude(Include.NON_NULL)
public class RootObject {

private List<Types> myList;

public List<Types> getMyList() {
    return myList;
}

public void setMyList(List<Types> myList) {
    this.myList = myList;
}

public String toString(){
    return new ReflectionToStringBuilder(this).toString();
}
}

Types.java

@JsonInclude(Include.NON_NULL)
public class Types {

private String type;

public String getType() {
    return type;
}

public void setType(String type) {
    this.type = type;
}

public String toString(){
    return new ReflectionToStringBuilder(this).toString();
}
}

JacksonTester

public class JacksonTester2 {

    public static void main(String[] args) {
        ObjectMapper mapper = new ObjectMapper();
        mapper.getSerializationConfig().withSerializationInclusion(Include.NON_EMPTY);
        mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
        RootObject rootObject = null;
        try {
            rootObject = mapper.readValue(new File("Data.txt"), RootObject.class);
        } catch (Exception e) {
            e.printStackTrace();
        } 
        System.out.println(rootObject);
    }
    }

Output

RootObject@1a74681a[myList=[Types@ace10d9[type=1], Types@5eb41c19[type=2], null]]
Community
  • 1
  • 1
Hari
  • 117
  • 2
  • 15

3 Answers3

1

I got an inspiration from the following post

Jackson: Ignore whitespace in empty @XmlWrapperElement collection

Here is the solution specific to the question above

public class NullCollectionHandler extends SimpleModule {
    private static class CustomizedCollectionDeserialiser extends CollectionDeserializer {

        public CustomizedCollectionDeserialiser(CollectionDeserializer src) {
            super(src);
        }

        private static final long serialVersionUID = 1L;

        @Override
        public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            Collection<Object> oldCol = super.deserialize(jp, ctxt);
            Collection<Object> newCol = new ArrayList<Object>();
            if(CollectionUtils.isNotEmpty(oldCol)){
                for(Object obj : oldCol){
                    if(obj != null)
                    newCol.add(obj);
                }
            }
            return newCol;
        }

        @Override
        public CollectionDeserializer createContextual(DeserializationContext ctxt,
                BeanProperty property) throws JsonMappingException {
            return new CustomizedCollectionDeserialiser(super.createContextual(ctxt, property));
        }
    }

    private static final long serialVersionUID = 1L;

    @Override
    public void setupModule(SetupContext context) {
        super.setupModule(context);
        context.addBeanDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<?> modifyCollectionDeserializer(
                    DeserializationConfig config, CollectionType type,
                    BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
                if (deserializer instanceof CollectionDeserializer) {
                    return new CustomizedCollectionDeserialiser(
                        (CollectionDeserializer) deserializer);
                } else {
                    return super.modifyCollectionDeserializer(config, type, beanDesc,
                        deserializer);
                }
            }
        });
    }
}
Community
  • 1
  • 1
Hari
  • 117
  • 2
  • 15
  • Thanks, just what I was looking for. I just needed minimal tweaking to call trimToSize() on deserialized ArrayList instances to save memory when deserializing big json data. – Daniel K. Dec 13 '19 at 20:19
0

There is no way to directly modify things in that way for array deserialization. But a relatively simple way may be to define a Converter that takes Collection (or List) of expected type, returns the same, and use:

@JsonDeserialize(converter=MyConverter.class)

to indicate that it should be used as part of deserialization. What this would do is let Jackson deserialize Collection using default logic, and then your converter can iterate through it and remove null entries (or create a new Collection if you prefer).

Alternatively you could create a custom Collection/List subtype, and override its add() method to silently drop nulls, but pass other values via super.add(value). This would be the most efficient method, and perhaps even simplest. You could even declare property type to still be simple Collection, but specify subtype to actually use with:

@JsonDeserialize(as=NoNullsCollection.class)
public List<String> names;
StaxMan
  • 113,358
  • 34
  • 211
  • 239
-1

You can configure your ObjectMapper to ignore NULLs like this:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(Include.NON_NULL);

Or you can make use of the following annotation:

@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
Alex Rashkov
  • 9,833
  • 3
  • 32
  • 58