@JsonSerialize - 如何在运行时创建包装器并为对象字段使用默认序列化?
@JsonSerialize - How to create a wrapper at runtime and use default serialization for the object fields?
我想添加一个在运行时确定命名的包装器,因为它取决于 class 名称(我可以使用 @JsonRootName 但我不想这样做,因为我必须在每个子class,效率不高。
我想我应该使用 @JsonSerialize
来覆盖默认的序列化程序,但我只想创建包装器;我不想自己序列化对象字段(我也是抽象的class,所以我什至不知道子class的字段!)。我不关心它们,我只关心包装纸!所以我希望默认序列化程序为我处理这些字段,或者类似的东西。
@JsonSerialize(using = CustomSerializer.class)
public abstract class Request {
public static class CustomSerializer extends JsonSerializer<Request > {
@Override
public void serialize(Request request, JsonGenerator jgen, SerializerProvider provider) throws IOException {
// Doing my stuff to determine the wrapper name based on request.class.getSimpleName()
// Then what should I wright to serialize the fields?
// Basically I just want a function to generate the same json that the default serializer would generate!
// I tried the following, but obviously it gives a com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion
jgen.writeObject(value);
// Same error for the function below
provider.defaultSerializeValue(value, jgen);
}
}
要创建包装序列化程序,您需要使用 com.fasterxml.jackson.databind.ser.BeanSerializerModifier
class。您可以使用 com.fasterxml.jackson.databind.module.SimpleModule
注册它。下面的示例显示了端到端解决方案如何做到这一点:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
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.util.NameTransformer;
import java.io.IOException;
import java.util.UUID;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
SimpleModule wrappersModule = new SimpleModule("requestWrapper");
wrappersModule.setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
if (Request.class.isAssignableFrom(beanDesc.getBeanClass())) {
return new RequestWrapperJsonSerializer(serializer);
}
return serializer;
}
});
ObjectMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.addModule(wrappersModule)
.build();
System.out.println(mapper.writeValueAsString(new Request1(1, "POST")));
System.out.println(mapper.writeValueAsString(new Request2(2, UUID.randomUUID())));
}
}
class RequestWrapperJsonSerializer extends JsonSerializer<Request> {
private final JsonSerializer baseSerializer;
public RequestWrapperJsonSerializer(JsonSerializer baseSerializer) {
this.baseSerializer = baseSerializer;
}
@Override
public void serialize(Request value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeFieldName(value.getClass().getSimpleName() + "Wrapper");
gen.writeStartObject();
baseSerializer.unwrappingSerializer(NameTransformer.NOP).serialize(value, gen, serializers);
gen.writeEndObject();
gen.writeEndObject();
}
}
abstract class Request {
private int id;
//constructor, getters, setters, toString
}
class Request1 extends Request {
private String body;
//constructor, getters, setters, toString
}
class Request2 extends Request {
private UUID uuid;
//constructor, getters, setters, toString
}
以上代码打印:
{
"Request1Wrapper" : {
"id" : 1,
"body" : "POST"
}
}
{
"Request2Wrapper" : {
"id" : 2,
"uuid" : "dd4cccb5-1cf5-4dd4-8bc7-97cb101e5d7d"
}
}
您可以使用 serialize
方法代替 unwrappingSerializer
方法并删除额外的包装调用。
尽管我接受的解决方案是正确的,但我提出了另一个从 获得的解决方案,它依赖于一个技巧:使 class 自定义- 序列化另一个class(纯粹用于包装)的字段并在该字段上使用@JsonSerialize
。代码更简单,但您必须创建和操作包装器 class。见下文:
public class RequestWrapper {
@JsonUnwrapped // Prevent the field from being wrap with its name: "request"
@JsonSerialize(using = RequestSerializer.class)
private final Request request;
private RequestWrapper(Request request) {
this.request= request;
}
public Request getRequest() {
return request;
}
public static class RequestSerializer extends JsonSerializer<Request> {
@Override
public boolean isUnwrappingSerializer() {
return true; // Indicates that we are creating an "unwrapping" serializer, because we added @JsonUnwrapped
}
@Override
public void serialize(Request request, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeObjectField(request.getClass().getSimpleName(), value);
}
}
}
我想添加一个在运行时确定命名的包装器,因为它取决于 class 名称(我可以使用 @JsonRootName 但我不想这样做,因为我必须在每个子class,效率不高。
我想我应该使用 @JsonSerialize
来覆盖默认的序列化程序,但我只想创建包装器;我不想自己序列化对象字段(我也是抽象的class,所以我什至不知道子class的字段!)。我不关心它们,我只关心包装纸!所以我希望默认序列化程序为我处理这些字段,或者类似的东西。
@JsonSerialize(using = CustomSerializer.class)
public abstract class Request {
public static class CustomSerializer extends JsonSerializer<Request > {
@Override
public void serialize(Request request, JsonGenerator jgen, SerializerProvider provider) throws IOException {
// Doing my stuff to determine the wrapper name based on request.class.getSimpleName()
// Then what should I wright to serialize the fields?
// Basically I just want a function to generate the same json that the default serializer would generate!
// I tried the following, but obviously it gives a com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion
jgen.writeObject(value);
// Same error for the function below
provider.defaultSerializeValue(value, jgen);
}
}
要创建包装序列化程序,您需要使用 com.fasterxml.jackson.databind.ser.BeanSerializerModifier
class。您可以使用 com.fasterxml.jackson.databind.module.SimpleModule
注册它。下面的示例显示了端到端解决方案如何做到这一点:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
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.util.NameTransformer;
import java.io.IOException;
import java.util.UUID;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
SimpleModule wrappersModule = new SimpleModule("requestWrapper");
wrappersModule.setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
if (Request.class.isAssignableFrom(beanDesc.getBeanClass())) {
return new RequestWrapperJsonSerializer(serializer);
}
return serializer;
}
});
ObjectMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.addModule(wrappersModule)
.build();
System.out.println(mapper.writeValueAsString(new Request1(1, "POST")));
System.out.println(mapper.writeValueAsString(new Request2(2, UUID.randomUUID())));
}
}
class RequestWrapperJsonSerializer extends JsonSerializer<Request> {
private final JsonSerializer baseSerializer;
public RequestWrapperJsonSerializer(JsonSerializer baseSerializer) {
this.baseSerializer = baseSerializer;
}
@Override
public void serialize(Request value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeFieldName(value.getClass().getSimpleName() + "Wrapper");
gen.writeStartObject();
baseSerializer.unwrappingSerializer(NameTransformer.NOP).serialize(value, gen, serializers);
gen.writeEndObject();
gen.writeEndObject();
}
}
abstract class Request {
private int id;
//constructor, getters, setters, toString
}
class Request1 extends Request {
private String body;
//constructor, getters, setters, toString
}
class Request2 extends Request {
private UUID uuid;
//constructor, getters, setters, toString
}
以上代码打印:
{
"Request1Wrapper" : {
"id" : 1,
"body" : "POST"
}
}
{
"Request2Wrapper" : {
"id" : 2,
"uuid" : "dd4cccb5-1cf5-4dd4-8bc7-97cb101e5d7d"
}
}
您可以使用 serialize
方法代替 unwrappingSerializer
方法并删除额外的包装调用。
尽管我接受的解决方案是正确的,但我提出了另一个从 @JsonSerialize
。代码更简单,但您必须创建和操作包装器 class。见下文:
public class RequestWrapper {
@JsonUnwrapped // Prevent the field from being wrap with its name: "request"
@JsonSerialize(using = RequestSerializer.class)
private final Request request;
private RequestWrapper(Request request) {
this.request= request;
}
public Request getRequest() {
return request;
}
public static class RequestSerializer extends JsonSerializer<Request> {
@Override
public boolean isUnwrappingSerializer() {
return true; // Indicates that we are creating an "unwrapping" serializer, because we added @JsonUnwrapped
}
@Override
public void serialize(Request request, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeObjectField(request.getClass().getSimpleName(), value);
}
}
}