如何将枚举序列化为对象形状和默认字符串? [英] How to serialise Enums as both Object Shape and default string?
问题描述
对于具有属性的枚举,例如:
For an enum with attributes, eg:
public enum Thing {
THING_A("a"),
THING_B("b");
private String thing;
private Thing(String thing) {
this.thing = thing;
}
// Getters...
}
Jackson
序列化为值的名称,例如:
Jackson
serializes as the name of the values, eg:
mapper.writeValueAsString(Thing.THING_A)); // "THING_A"
如果添加注释以将序列化视为对象:
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
它将序列化属性:
If we add the annotation to treat serialisation as an object:
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
it will serialize the attributes:
mapper.writeValueAsString(Thing.THING_A)); // "{"thing":"a"}"
我希望能够在序列化期间决定使用哪种方法.因为这涉及大量枚举,所以我不想编辑每个枚举.有什么好方法吗?
I'd like to be able to decide, during serialization, which of these methods to use. Because this spans a large number of enums, I'd rather not edit each one. Is there a good way to do this?
例如:像这样的东西会很棒:
eg: something like this would be great:
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
并为所有枚举返回所需的值.您可以在下面找到一个简单的示例:
Since, com.fasterxml.jackson.annotation.JsonFormat
is an annotation you can implement your own com.fasterxml.jackson.databind.AnnotationIntrospector
and return value you want for all your enums. Simple example you can find below:
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
,并为给定的形状选择所需的一个:
Now, you can create two instances of ObjectMapper
and for one configure your own annotation introspector and second one leave with default. If you really want to use it in dynamic way you can create one ObjectMapper
for each available Shape
value and select required one for a given 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
.
Above code of course is overkill but I wanted to show possibilities we have. We have only 3 different outputs so you can group values with the same output and create maximum 3 different ObjectMappers
.
这篇关于如何将枚举序列化为对象形状和默认字符串?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!