Как сериализовать класс, который расширяет TreeSet с помощью Jackson?

#java #json #jackson

#java #json #джексон

Вопрос:

Класс A выглядит так:

 @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;
    }
}
  

Класс B:

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

Когда я сериализую объект класса A с помощью Jackson:

 jsonString = objectMapper.writeValueAsString(class_a_object);
  

Я получаю массив json, подобный этому:

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

но переменные-члены a, b, c отсутствуют. Есть ли способ включить их в строку json?

Комментарии:

1. Где бы вы хотели, чтобы они появлялись? Как бы выглядел ваш идеальный выходной JSON?

2. Я хочу, чтобы они отображались перед массивом.

3. Это не очень понятно. Jackson создает JSON, поэтому он должен быть действительным. Вы могли бы изменить свой A класс, чтобы иметь TreeSet поле вместо подкласса TreeSet . Затем Jackson создаст объект JSON с каждым из ваших 3 полей. TreetSet Полем будет ваш массив JSON, как у вас в вашем вопросе.

4. Все типы коллекций Java обрабатываются особым образом: карты, наборы, списки. Для наборов и списков Jackson просто получает итератор и записывает его элементы в массив JSON.

5. Возможно, вы сможете достичь этого, написав пользовательский сериализатор, но я не могу вспомнить, есть ли способ зарегистрировать его перед теми, которые обрабатывают типы коллекций.

Ответ №1:

Jackson распознает класс A как коллекцию и регистрируется CollectionSerializer для A сериализации экземпляров. Мы можем изменить сериализатор по умолчанию и предоставить пользовательский сериализатор. Мы можем использовать BeanSerializerModifier для этого и повторно использовать сериализатор коллекции в пользовательской реализации. Чтобы сгенерировать valid JSON , вам необходимо указать имя свойства для заданных значений.

Пример:

 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;
    }
}
  

Приведенный выше код печатает:

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