How to serialize a class that extends TreeSet with Jackson?

Tags: , ,



Class A looks like this:

@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
public final class A extends TreeSet<B> {

    private final a;
    private b;
    private c;

    public A(a, b, c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}

Class B:

@Data
@AllArgsConstructor
@EqualsAndHashCode
@ToString
public final class B  {
    private final int x;
    private final double y;
}

When I serialize a class A object using Jackson:

jsonString = objectMapper.writeValueAsString(class_a_object);

I get a json Array like this:

[
  {
    "x": 3,
    "y": 3.23
  },
  {
    "x": 4,
    "y": 2.12
  },...
]

but the member variables a,b,c are missing. Is there a way I can include them into the json string?

Answer

Jackson recognises class A as a collection and register CollectionSerializer to serialise A‘s instances. We can modify default serialiser and provide custom serialiser. We can use BeanSerializerModifier to do that and reuse collection serialiser in custom implementation. To generate valid JSON you need to provide property name for set values.

Example:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.type.CollectionType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

import java.io.File;
import java.io.IOException;
import java.util.TreeSet;

public class ModifyCollectionSerializerApp {
    public static void main(String[] args) throws IOException {
        A a = new A(1, 2);
        a.add(new B(22, 2.2));
        a.add(new B(33, 3.3));

        SimpleModule aModule = new SimpleModule();
        aModule.setSerializerModifier(new ABeanSerializerModifier());
        JsonMapper mapper = JsonMapper.builder()
                .enable(SerializationFeature.INDENT_OUTPUT)
                .addModule(aModule)
                .build();
        String json = mapper.writeValueAsString(a);
        System.out.println(json);
    }
}

class ABeanSerializerModifier extends BeanSerializerModifier {
    @Override
    public JsonSerializer<?> modifyCollectionSerializer(SerializationConfig config, CollectionType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
        return new AJsonSerializer(serializer);
    }
}

class AJsonSerializer extends JsonSerializer<A> {

    private final JsonSerializer valuesSerializer;

    AJsonSerializer(JsonSerializer valuesSerializer) {
        this.valuesSerializer = valuesSerializer;
    }

    @Override
    public void serialize(A value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeStartObject();
        gen.writeNumberField("a", value.getA());
        gen.writeNumberField("b", value.getB());
        gen.writeFieldName("values");
        valuesSerializer.serialize(value, gen, serializers);
        gen.writeEndObject();
    }
}

@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
class A extends TreeSet<B> {

    private final int a;
    private final int b;
}

@Data
@AllArgsConstructor
@EqualsAndHashCode
@ToString
class B implements Comparable<B> {
    private final int x;
    private final double y;

    @Override
    public int compareTo(B o) {
        return this.x - o.x;
    }
}

Above code prints:

{
  "a" : 1,
  "b" : 2,
  "values" : [ {
    "x" : 22,
    "y" : 2.2
  }, {
    "x" : 33,
    "y" : 3.3
  } ]
}


Source: stackoverflow