2

I want to serialize null List to empty array.

So given:

class MyBean { List values; }

And given an instance with null for values, it should be serialized to:

{ "values": [] }

I want this to be global behaviour for all Lists in all classes. I do not want to add any annotations or specific handling for classes.

I have read all related questions I found, and could not come up with anything that works. Seems any custom serializer I try to register for List class is not kicking in.

If you have this working on your project, let me know how did you manage to do that.

Raipe
  • 786
  • 1
  • 9
  • 22

1 Answers1

4

In cases like this you need to customize JacksonAnnotationIntrospector class. Te serialize null-s, by default, Jackson uses com.fasterxml.jackson.databind.ser.std.NullSerializer class. You can extend default introspector class and override findNullSerializer.

See below example:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;
import java.util.List;

public class Main {

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(new EmptyArrayJacksonAnnotationIntrospector());
        mapper.writeValue(System.out, new ListWrapper());
    }
}

class EmptyArrayJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector {

    @Override
    public Object findNullSerializer(Annotated a) {
        if (List.class.isAssignableFrom(a.getRawType())) {
            return ArrayNullSerializer.INSTANCE;
        }
        return super.findNullSerializer(a);
    }
}

final class ArrayNullSerializer extends StdSerializer<Object> {

    public static final ArrayNullSerializer INSTANCE = new ArrayNullSerializer();

    protected ArrayNullSerializer() {
        super(Object.class);
    }

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartArray();
        gen.writeEndArray();
    }
}

class ListWrapper {

    private List values;

    public List getValues() {
        return values;
    }

    public void setValues(List values) {
        this.values = values;
    }
}

Above code prints:

{"values":[]}
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • Worked on the first try! Thanks! I Should probably link this answer to other related questions that did not help me forward. – Raipe Aug 19 '20 at 11:05
  • @Raipe, I'm glad to hear that. If you think it would be helpful for others you can add my answer in comments to other questions you found at the beginning. – Michał Ziober Aug 19 '20 at 12:55
  • if you have any additional information on why this is the solution to use, I would be happy to learn. Doing this through annotation inspectors sounds really strange (works though). – Raipe Aug 20 '20 at 11:46
  • @Raipe, There is nothing strange with this solution. You can define `NullSerialiser` using `@JsonSerialise` annotation but you explicitly wrote, you do not want to do that. So, we can trick `Jackson` and force it to always use our custom implementation for `List` type. `JacksonAnnotationIntrospector` is a class which reads annotations and configures a way how given object is serialized or deserialized. – Michał Ziober Aug 20 '20 at 13:51