java - Jackson Deserializer 委托给下一个适用的反序列化器
问题描述
我有一个用于查询一些数据的外部服务。数据将采用以下两种格式之一(第一种是“遗留”,但需要支持):
{
"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
意味着这个反序列化器将在ObjectMapper
spring 通过 JacksonModule
接口注册到 kinda-global):
@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)
)最终都会再次使用相同的自定义反序列化器,从而导致无限递归。这甚至可能吗?
解决方案
归功于 schummar 的回答:如何从 Jackson 的自定义反序列化器中调用默认反序列化器。按照上面的答案,
1.@JsonComponent
应该从自定义序列化器中删除注释,因为我们需要使用默认序列化器来构造自定义序列化器,而@JsonComponent
.
2.使用BeanDeserializerModifierSimpleModule
注册 a并使用我们使用默认序列化器构造的自定义序列化器修改序列化器。
3.在自定义序列化器的方法中,处理特殊情况,正常情况下将序列化委托给默认序列化器。 ObjectMapper
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;
}
}
}
推荐阅读
- sql - 使用空处理求和时间戳差异的 SQL 查询
- c++ - 即使没有继承,dynamic_cast 也能工作吗?
- c# - 尝试将值输入列表的特定索引时出现 ArgumentOutOfRangeException?
- java - 当应用程序中没有流量时如何在 logback 中强制推出
- git - Jenkins Git 参数插件 branchFilter 正则表达式
- flutter - 如何从另一个小部件推送和弹出一个小部件(B 从 A 的转换)
- ruby - errno::eacces: 上传 jpg 时权限被拒绝@rb_sysopen
- flutter - 卡片上的 Flutter Text 叠加层
- c# - c# winforms中的图像单击选择下拉项目
- javascript - 向 Express 发送 AJAX POST 请求