4

I have a POJO with a field or property, containing collection of objects, something like this:

public class Box {

    public List<Items> items;

}

By default, value of items is null, and I do not want to initialize it with empty list.

Now, if I try to serialize it with Jackson, I get NullPointerException. Is there a simple way to make Jackson not break on such value and serialize it as an empty collection: [ ]?

Note. This class is just a simplified example. In reality, there are a hundred of classes and a number of fields with different names in each of them, which are occasionally set to null sometimes somewhere in the code, breaking serialization in runtime.

Mikhail Batcer
  • 1,938
  • 7
  • 37
  • 57
  • 1
    According to this: https://github.com/FasterXML/jackson-databind/issues/347 there is no easy way. – user3707125 Feb 16 '16 at 12:14
  • 1
    I had the same case. We had lots of classes we serialized and many of them had null values for lists. This ended up those fields being null or missing in the produced json, which was problematic on client using the json. Here is answer that helped me to get those serialized as empty arrays: https://stackoverflow.com/a/63484301/2170968 – Raipe Aug 20 '20 at 06:24

2 Answers2

1

Have you considered making this class a JavaBean? In that case, you would be able to give a default value in the getter:

public class Box {
    private List<Items> items;
    public List<Items> getItems() {
        if(null == items) {
            return Collections.emptyList();
        }
        return this.items;
    }
    //Setter here
}

This approach would prevent a lot of trouble related to Jackson's assumptions.

Update: Based on clarification... You could implement a custom serializer for the list type (and/or any other desired customization). Please note that :

public class ListSerializer extends JsonSerializer<List> {
    @Override
    public void serialize(List value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
            JsonProcessingException {

        if (null == value) {
            provider.defaultSerializeValue(new ArrayList<Object>(), jgen);
        } else {
            provider.defaultSerializeValue(value, jgen);
        }
    }
}

//Then your code could set the serializer on the object mapper as follows:
objectMapper.addSerializer(List.class, new ListSerializer());

Repeat for all such customization.

Code was inspired by this article: http://www.baeldung.com/jackson-custom-serialization

ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • Considering that this class is used not only by Jackson, your code has a bad design - caller may add an item to the returned array, and it will disappear. And if you change that code to store `items` before returning, then you will violate questioner's condition: "I do not want to initialize it with empty list". – user3707125 Feb 16 '16 at 12:18
  • Agreed. But my assumption is that the class is to be used only for Jackson binding. Ideal is to simply make it a valid javabean. – ernest_k Feb 16 '16 at 12:21
  • @ErnestKiwele Perhaps I oversimplified the question, please see the note I've added in the end of it. – Mikhail Batcer Feb 16 '16 at 13:42
  • Edited answer, assuming that you can't change all list field declarations and that you're ok to impose the behavior on objectMapper instance. – ernest_k Feb 16 '16 at 14:19
1

If you do not want to change the contract of your POJO class, think about the possibility to define custom Jackson serializer / deserializer which extend JsonSerializer<T> and JsonDeserializer<T> respectively. E.g.:

 public class CountryDeserializer extends JsonDeserializer<CountryCode> {
     @Override
     public CountryCode deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException {
         return CountryCode.getByCode(jp.getText());
     }
 }

and then

 @JsonDeserialize(using=CountryDeserializer.class)
 private CountryCode country;

You can check whether your field is null and act accordingly, in both directions (serialization / deserialization).

m c
  • 1,104
  • 1
  • 12
  • 21