java - 如何将枚举序列化为对象形状和默认字符串?

标签 java json serialization enums jackson

对于具有属性的枚举,例如:

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"

最佳答案

因为,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 实例,其中一个配置您自己的注释内省(introspection)器,第二个则保留默认值。如果您确实想以动态方式使用它,您可以为每个可用的 Shape 值创建一个 ObjectMapper 并为给定形状选择所需的值:

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

关于java - 如何将枚举序列化为对象形状和默认字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61281952/

相关文章:

java - 在 GWT 中对齐垂直面板内的水平面板

java - Java SE/EE/ME 之间的区别?

javascript - 使用 JavaScript 遍历 JSON 对象树的所有节点

c# - 使用 XmlDictionaryWriter.CreateBinaryWriter 和 XmlDictionary 编写紧凑的 xml

java - 如何在java中保存非常大的二进制代码

java - Apache Spark 和不可序列化的应用程序上下文

java - List 中的对象具有相同的值 - 最后添加的元素的值

java - 使用多线程等待 AWT 事件调度程序线程线程

.NET ASMX - 返回纯 JSON?

javascript - 如何在jQuery中通过Request Body提交JSON数据?