问题描述
我尝试编写自定义杰克逊解串器.我想看"一个字段并执行自动反序列化以分类,请参见下面的示例:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.mypackage.MyInterface;
import com.mypackage.MyFailure;
import com.mypackage.MySuccess;
import java.io.IOException;
public class MyDeserializer extends JsonDeserializer<MyInterface> {
@Override
public MyInterface deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectCodec codec = jp.getCodec();
JsonNode node = codec.readTree(jp);
if (node.has("custom_field")) {
return codec.treeToValue(node, MyFailure.class);
} else {
return codec.treeToValue(node, MySuccess.class);
}
}
}
Pojos:
public class MyFailure implements MyInterface {}
public class MySuccess implements MyInterface {}
@JsonDeserialize(using = MyDeserializer.class)
public interface MyInterface {}
我得到了StackOverflowError
.在理解codec.treeToValue
调用相同的反序列化器.是否可以在自定义脱盐器中使用codec.treeToValue
或ObjectMapper.readValue(String,Class<T>)
?
当前的问题似乎是@JsonDeserialize(using=...)
正在为您的MyInterface以及MyInterface本身的实现所采用:因此是无止境的循环. >
您可以通过在每个实现中覆盖的设置来解决此问题:
@JsonDeserialize(using=JsonDeserializer.None.class)
public static class MySuccess implements MyInterface {
}
或者通过使用模块而不是注释来配置反序列化(并从MyInterface中删除注释):
mapper.registerModule(new SimpleModule() {{
addDeserializer(MyInterface.class, new MyDeserializer());
}});
在旁注中,您可能还考虑扩展StdNodeBasedDeserializer
以实现基于JsonNode的反序列化.例如:
@Override
public MyInterface convert(JsonNode root, DeserializationContext ctxt) throws IOException {
java.lang.reflect.Type targetType;
if (root.has("custom_field")) {
targetType = MyFailure.class;
} else {
targetType = MySuccess.class;
}
JavaType jacksonType = ctxt.getTypeFactory().constructType(targetType);
JsonDeserializer<?> deserializer = ctxt.findRootValueDeserializer(jacksonType);
JsonParser nodeParser = root.traverse(ctxt.getParser().getCodec());
nodeParser.nextToken();
return (MyInterface) deserializer.deserialize(nodeParser, ctxt);
}
对此自定义反序列化器有很多改进,特别是在跟踪反序列化的上下文等方面,但这应该提供您所要求的功能.
I try to write custom jackson deserializer. I want "look" at one field and perform auto deserialization to class, see example below:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.mypackage.MyInterface;
import com.mypackage.MyFailure;
import com.mypackage.MySuccess;
import java.io.IOException;
public class MyDeserializer extends JsonDeserializer<MyInterface> {
@Override
public MyInterface deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectCodec codec = jp.getCodec();
JsonNode node = codec.readTree(jp);
if (node.has("custom_field")) {
return codec.treeToValue(node, MyFailure.class);
} else {
return codec.treeToValue(node, MySuccess.class);
}
}
}
Pojos:
public class MyFailure implements MyInterface {}
public class MySuccess implements MyInterface {}
@JsonDeserialize(using = MyDeserializer.class)
public interface MyInterface {}
And I got StackOverflowError
. In understand that codec.treeToValue
call same deserializer. Is there a way to use codec.treeToValue
or ObjectMapper.readValue(String,Class<T>)
inside custome deseralizer?
The immediate problem seems to be that the @JsonDeserialize(using=...)
is being picked up for your implementations of MyInterface as well as MyInterface itself: hence the endless loop.
You can fix this my overriding the setting in each implementation:
@JsonDeserialize(using=JsonDeserializer.None.class)
public static class MySuccess implements MyInterface {
}
Or by using a module instead of an annotation to configure the deserialization (and removing the annotation from MyInterface):
mapper.registerModule(new SimpleModule() {{
addDeserializer(MyInterface.class, new MyDeserializer());
}});
On a side-note, you might also consider extending StdNodeBasedDeserializer
to implement deserialization based on JsonNode. For example:
@Override
public MyInterface convert(JsonNode root, DeserializationContext ctxt) throws IOException {
java.lang.reflect.Type targetType;
if (root.has("custom_field")) {
targetType = MyFailure.class;
} else {
targetType = MySuccess.class;
}
JavaType jacksonType = ctxt.getTypeFactory().constructType(targetType);
JsonDeserializer<?> deserializer = ctxt.findRootValueDeserializer(jacksonType);
JsonParser nodeParser = root.traverse(ctxt.getParser().getCodec());
nodeParser.nextToken();
return (MyInterface) deserializer.deserialize(nodeParser, ctxt);
}
There are a bunch of improvements to make to this custom deserializer, especially regarding tracking the context of the deserialization etc., but this should provide the functionality you're asking for.
这篇关于如何在自定义解串器中使用杰克逊ObjectMapper?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!