Jackson Deserializer 委托给下一个适用的反序列化器
Posted
技术标签:
【中文标题】Jackson Deserializer 委托给下一个适用的反序列化器【英文标题】:Jackson Deserializer delegate to next applicable deserializer 【发布时间】:2019-03-28 11:12:42 【问题描述】:我有一个用于查询一些数据的外部服务。数据将采用以下两种格式之一(第一种是“遗留”,但需要支持):
"foo": "John Smith"
或
"foo":
"name": "John Smith",
"bar": "baz"
我想映射到以下 POJO:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Outer
private Foo foo;
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Foo
String name;
String bar;
第二种格式的数据(foo
是一个对象)应该像任何其他 POJO 一样反序列化,但是给定第一种格式的数据(foo
是字符串),将其转换为Foo
的实例, 我想打电话给new Foo(<foo>, null)
。为此,我创建了一个自定义反序列化器(@JsonComponent
意味着这个反序列化器将在 spring 之前通过 Jackson Module
接口注册到一个有点全局的ObjectMapper
):
@JsonComponent
public class FooDeserializer extends JsonDeserializer<Outer.Foo>
@Override
public Outer.Foo deserialize(JsonParser parser, DeserializationContext context)
throws IOException
JsonNode node = parser.getCodec().readTree(parser);
if (node.isTextual())
return new Foo(node.asText(), null);
return <delegate to next applicable deserializer>;
我无法弄清楚如何执行“委托给下一个适用的反序列化器”部分,因为我尝试过的每个解决方案(例如 parser.getCodec().treeToValue(node, Outer.Foo.class)
)最终都会再次使用相同的自定义反序列化器,从而导致无限递归。这甚至可能吗?
【问题讨论】:
【参考方案1】:感谢 schummar 的回答:How do I call the default deserializer from a custom deserializer in Jackson。根据上述答案,
1. @JsonComponent
注解应该从自定义序列化器中移除,因为我们需要使用默认序列化器构造自定义序列化器,而@JsonComponent
不支持。
2. 使用BeanDeserializerModifier 将SimpleModule
注册到ObjectMapper
,并使用我们使用默认序列化程序构造的自定义序列化程序修改序列化程序。
3.在自定义序列化器的serialize
方法中,处理特殊情况,正常情况下将序列化委托给默认序列化器。
以下代码演示了如何实现以上几点。
主类
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class DelegateDeserializer
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.setDeserializerModifier(new BeanDeserializerModifier()
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc,
JsonDeserializer<?> deserializer)
if (Outer.Foo.class.isAssignableFrom(beanDesc.getBeanClass()))
return new FooDeserializer(deserializer, beanDesc.getBeanClass());
return deserializer;
);
mapper.registerModule(simpleModule);
Outer outer1 = mapper.readValue(getType1Json(), Outer.class);
Outer outer2 = mapper.readValue(getType2Json(), Outer.class);
System.out.println("deserialize json with object structure:");
System.out.println(outer1.getFoo().getName());
System.out.println(outer1.getFoo().getBar());
System.out.println("deserialize json with string field only:");
System.out.println(outer2.getFoo().getName());
System.out.println(outer2.getFoo().getBar());
private static String getType1Json()
return " "
+ " \"foo\": "
+ " \"name\": \"John Smith\", "
+ " \"bar\": \"baz\" "
+ " "
+ " ";
private static String getType2Json()
return " "
+ " \"foo\": \"John Smith\" "
+ " ";
FooDeserializer 类
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import jackson.Outer.Foo;
public class FooDeserializer extends StdDeserializer<Outer.Foo> implements ResolvableDeserializer
private static final long serialVersionUID = 1L;
private final JsonDeserializer<?> defaultDeserializer;
public FooDeserializer(JsonDeserializer<?> defaultDeserializer, Class<?> clazz)
super(clazz);
this.defaultDeserializer = defaultDeserializer;
@Override
public Outer.Foo deserialize(JsonParser parser, DeserializationContext context) throws IOException
if (parser.getCurrentToken() == JsonToken.VALUE_STRING)
JsonNode node = parser.getCodec().readTree(parser);
if (node.isTextual())
return new Foo(node.asText(), null);
return (Foo) defaultDeserializer.deserialize(parser, context);
@Override
public void resolve(DeserializationContext ctxt) throws JsonMappingException
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
外部类
public class Outer
private Foo foo;
public Foo getFoo()
return foo;
public void setFoo(Foo foo)
this.foo = foo;
public static class Foo
private String bar;
private String name;
public String getName()
return name;
public void setName(String name)
this.name = name;
public String getBar()
return bar;
public void setBar(String bar)
this.bar = bar;
public Foo()
public Foo(String name, String bar)
this.name = name;
this.bar = bar;
【讨论】:
以上是关于Jackson Deserializer 委托给下一个适用的反序列化器的主要内容,如果未能解决你的问题,请参考以下文章
是否有任何选项可以在 Spring Boot 中使用 Jackson 为 java.time.* 包注册一次 Serializer/Deserializer?
如何处理 Spring Boot Rest 和 Jackson Deserializer 的 Javax 验证中的无效数字
iOS 9.2 因 [[CJSONDeserializer deserializer] deserializeAsDictionary 崩溃: