Как сериализовать Enums как объектную форму и строку по умолчанию? - PullRequest
3 голосов
/ 18 апреля 2020

Для перечисления с атрибутами, например:

public enum Thing {
  THING_A("a"),
  THING_B("b");

  private String thing;

  private Thing(String thing) {
    this.thing = thing;
  }

  // Getters...
}

Jackson сериализуется как имя значения, например:

mapper.writeValueAsString(Thing.THING_A)); // "THING_A"

Если мы добавим аннотацию для обработки сериализации как объект:
@JsonFormat(shape = JsonFormat.Shape.OBJECT) он будет сериализовать атрибуты:

mapper.writeValueAsString(Thing.THING_A)); // "{"thing":"a"}"

Я бы хотел иметь возможность при сериализации , какой из этих методов использовать , Поскольку это охватывает большое количество перечислений, я бы не стал редактировать каждое из них. Есть ли хороший способ сделать это?

Например: что-то вроде этого было бы здорово:

mapper.writeValueAsString(Thing.THING_A, JsonFormat.Shape.OBJECT); // "{"thing":"a"}"
mapper.writeValueAsString(Thing.THING_A, JsonFormat.Enum.DEFAULT); // "THING_A"

Ответы [ 2 ]

1 голос
/ 18 апреля 2020

Поскольку, com.fasterxml.jackson.annotation.JsonFormat является аннотацией, вы можете реализовать свой собственный com.fasterxml.jackson.databind.AnnotationIntrospector и возвращаемое значение, которое вы хотите для всех ваших перечислений. Простой пример, который вы можете найти ниже:

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;

public class JsonPathApp {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(AnnotationIntrospector.pair(new DynamicEnumAnnotationIntrospector(), new JacksonAnnotationIntrospector()));

        System.out.println(mapper.writeValueAsString(Thing.THING_A));
    }
}

class DynamicEnumAnnotationIntrospector extends AnnotationIntrospector {

    @Override
    public Version version() {
        return new Version(1, 0, 0, "Dynamic enum object", "your.package", "jackson.dynamic.enum");
    }

    @Override
    public JsonFormat.Value findFormat(Annotated memberOrClass) {
        final Class<?> rawType = memberOrClass.getRawType();
        if (rawType.isEnum() && rawType.getPackage().getName().startsWith("your.package")) {
            return JsonFormat.Value.forShape(JsonFormat.Shape.OBJECT);
        }

        return super.findFormat(memberOrClass);
    }
}

Над отпечатками кода:

{"thing":"a"}

Теперь вы можете создать два экземпляра ObjectMapper и для одного настроить свой собственный интроспектор аннотации, а второй - один оставить по умолчанию. Если вы действительно хотите использовать его в динамическом режиме c, вы можете создать один ObjectMapper для каждого доступного значения Shape и выбрать необходимый для данной фигуры:

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;

import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Objects;

public class JsonPathApp {

    public static void main(String[] args) throws Exception {
        JsonFactory factory = new JsonFactory();

        for (Shape shape : Shape.values()) {
            ObjectMapper mapper = factory.getWithEnumShapeSetTo(shape);
            System.out.println(shape + " => " + mapper.writeValueAsString(Thing.THING_A));
        }
    }
}

class JsonFactory {
    private final AnnotationIntrospector defaultIntrospector = new JacksonAnnotationIntrospector();
    private final EnumMap<Shape, ObjectMapper> instances = new EnumMap<>(Shape.class);

    public JsonFactory() {
        final List<Shape> notAllowed = Arrays.asList(Shape.BOOLEAN, Shape.BINARY);
        Arrays.stream(Shape.values())
                .filter(shape -> !notAllowed.contains(shape))
                .forEach(shape -> instances.put(shape, createNewWithEnumShape(shape)));
    }

    private ObjectMapper createNewWithEnumShape(Shape shape) {
        DynamicEnumAnnotationIntrospector enumIntrospector = new DynamicEnumAnnotationIntrospector(shape);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(AnnotationIntrospector.pair(enumIntrospector, defaultIntrospector));

        return mapper;
    }

    public ObjectMapper getWithEnumShapeSetTo(Shape shape) {
        Objects.requireNonNull(shape);

        final ObjectMapper mapper = instances.get(shape);
        if (mapper == null) {
            return new ObjectMapper();
        }

        return mapper;
    }
}

class DynamicEnumAnnotationIntrospector extends AnnotationIntrospector {

    private final Shape shape;

    public DynamicEnumAnnotationIntrospector(Shape shape) {
        this.shape = Objects.requireNonNull(shape);
    }

    @Override
    public Version version() {
        return new Version(1, 0, 0, "Dynamic enum shape", "your.package", "jackson.dynamic.enum");
    }

    @Override
    public JsonFormat.Value findFormat(Annotated memberOrClass) {
        final Class<?> rawType = memberOrClass.getRawType();
        if (rawType.isEnum() && rawType.getPackage().getName().startsWith("your.package")) {
            return JsonFormat.Value.forShape(shape);
        }

        return super.findFormat(memberOrClass);
    }
}

Выше кода печатается:

ANY => "THING_A"
NATURAL => "THING_A"
SCALAR => "THING_A"
ARRAY => 0
OBJECT => {"thing":"a"}
NUMBER => 0
NUMBER_FLOAT => 0
NUMBER_INT => 0
STRING => "THING_A"
BOOLEAN => "THING_A"
BINARY => "THING_A"

Выше код, конечно, излишний, но я хотел показать возможности, которые у нас есть. У нас есть только 3 разных выхода, поэтому вы можете сгруппировать значения с одним и тем же выходом и создать максимум 3 разных ObjectMappers.

1 голос
/ 18 апреля 2020

Вышеуказанный вопрос похож и уже был дан ответ. Jackson ObjectMapper set JsonFormat.Shape.ARRAY без аннотации .

Вы можете использовать пользовательский указатель сопоставления объектов c для Enum и другой сопоставитель объектов для других классов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...