【问题标题】:How to serialise Enums as both Object Shape and default string?如何将枚举序列化为对象形状和默认字符串?
【发布时间】:2020-07-31 13:32:43
【问题描述】:

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

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"

【问题讨论】:

    标签: java json serialization enums jackson


    【解决方案1】:

    上面的问题类似,已经回答过了。 Jackson ObjectMapper set JsonFormat.Shape.ARRAY without annotation.

    您可以使用特定于 Enum 的自定义对象映射器和其他类的不同对象映射器。

    【讨论】:

      【解决方案2】:

      因为,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 实例,其中一个配置您自己的注释内省器,第二个保留默认值。如果您真的想以动态方式使用它,您可以为每个可用的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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多