Java Jackson 2.2.x:递归反序列化失败,为什么?
准确地说,这是杰克逊2.2.3 反序列化过程相当复杂,因为根据我反序列化的JSON值的类型,类会发生变化: 基本抽象类是JsonMergePatch; 当JSON值不是JSON对象时,我反序列化为NonObjectMergePatch; 否则,我将递归地反序列化到ObjectMergePatch。 为了简洁起见,这里省略了代码导入;首先,关于JsonMergePatch: 非ObjectMergePatch: ObjectMergePatch:Java Jackson 2.2.x:递归反序列化失败,为什么?,java,json,recursion,jackson,json-deserialization,Java,Json,Recursion,Jackson,Json Deserialization,准确地说,这是杰克逊2.2.3 反序列化过程相当复杂,因为根据我反序列化的JSON值的类型,类会发生变化: 基本抽象类是JsonMergePatch; 当JSON值不是JSON对象时,我反序列化为NonObjectMergePatch; 否则,我将递归地反序列化到ObjectMergePatch。 为了简洁起见,这里省略了代码导入;首先,关于JsonMergePatch: 非ObjectMergePatch: ObjectMergePatch: 为什么??我怎样才能解决这个问题呢?嗯,当我在谷歌
为什么??我怎样才能解决这个问题呢?嗯,当我在谷歌上搜索时,我偶然发现并想出了一个答案;但我的话很难看 正如异常所提到的,问题在于JsonParser实例没有关联的ObjectCodec。所以,我们必须用一个来喂养它;这里的问题是为什么这不是自动完成的 因此,在自定义反序列化程序中,我声明了一个新的ObjectCodec: 在.deserialize方法中,我执行以下操作:
jp.setCodec(CODEC);
它是有效的
但是很难看。真糟糕。太可怕了。我想要一个更好的解决方案://这应该没有必要,因为这样会很难看。在必须为缓冲令牌创建临时解析器的情况下,数据绑定代码应该在编解码器实例中传播;这听起来像是个虫子。相反,如果自定义反序列化程序使用缓冲,它需要确保为TokenBuffer.Hmm传递JsonParser,因此,我使用2.2.3;我还没有使用2.2.4,那会有什么不同吗?啊。很有可能——类似的问题从那时起就已经解决了。虽然我会尽量使用2.4.3,但如果可能和必要的话,也就是说,如果问题仍然存在,请尝试查看更高版本是否可以修复。2.4.3有我能想到的所有相关修复。
final class NonObjectMergePatch
extends JsonMergePatch
{
private final JsonNode node;
NonObjectMergePatch(@Nonnull final JsonNode node)
{
this.node = Preconditions.checkNotNull(node);
}
@Override
public JsonNode apply(final JsonNode input)
throws JsonPatchException
{
return null; // TODO!
}
@Override
public void serialize(final JsonGenerator jgen,
final SerializerProvider provider)
throws IOException, JsonProcessingException
{
jgen.writeTree(node);
}
@Override
public void serializeWithType(final JsonGenerator jgen,
final SerializerProvider provider, final TypeSerializer typeSer)
throws IOException, JsonProcessingException
{
serialize(jgen, provider);
}
}
final class ObjectMergePatch
extends JsonMergePatch
{
private final Set<String> removedMembers;
private final Map<String, JsonMergePatch> modifiedMembers;
ObjectMergePatch(final Set<String> removedMembers,
final Map<String, JsonMergePatch> modifiedMembers)
{
this.removedMembers = ImmutableSet.copyOf(removedMembers);
this.modifiedMembers = ImmutableMap.copyOf(modifiedMembers);
}
@Override
public JsonNode apply(final JsonNode input)
throws JsonPatchException
{
return null;
}
@Override
public void serialize(final JsonGenerator jgen,
final SerializerProvider provider)
throws IOException, JsonProcessingException
{
jgen.writeStartObject();
/*
* Write removed members as JSON nulls
*/
for (final String member: removedMembers)
jgen.writeNullField(member);
/*
* Write modified members; delegate to serialization for writing values
*/
for (final Map.Entry<String, JsonMergePatch> entry:
modifiedMembers.entrySet()) {
jgen.writeFieldName(entry.getKey());
entry.getValue().serialize(jgen, provider);
}
jgen.writeEndObject();
}
@Override
public void serializeWithType(final JsonGenerator jgen,
final SerializerProvider provider, final TypeSerializer typeSer)
throws IOException, JsonProcessingException
{
serialize(jgen, provider);
}
}
final class JsonMergePatchDeserializer
extends JsonDeserializer<JsonMergePatch>
{
@Override
public JsonMergePatch deserialize(final JsonParser jp,
final DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
/* FAILS HERE */
final JsonNode node = jp.readValueAsTree();
/*
* Not an object: the simple case
*/
if (!node.isObject())
return new NonObjectMergePatch(node);
/*
* The complicated case...
*
* We have to build a set of removed members, plus a map of modified
* members.
*/
final Set<String> removedMembers = Sets.newHashSet();
final Map<String, JsonMergePatch> modifiedMembers = Maps.newHashMap();
final Iterator<Map.Entry<String, JsonNode>> iterator = node.fields();
Map.Entry<String, JsonNode> entry;
while (iterator.hasNext()) {
entry = iterator.next();
if (entry.getValue().isNull())
removedMembers.add(entry.getKey());
else {
final JsonMergePatch value
= deserialize(entry.getValue().traverse(), ctxt);
modifiedMembers.put(entry.getKey(), value);
}
}
return new ObjectMergePatch(removedMembers, modifiedMembers);
}
/*
* This method MUST be overriden... The default is to return null, which is
* not what we want.
*/
@Override
public JsonMergePatch getNullValue()
{
return new NonObjectMergePatch(NullNode.getInstance());
}
}
java.lang.IllegalStateException: No ObjectCodec defined for the parser, can not deserialize JSON into JsonNode tree
at com.fasterxml.jackson.core.JsonParser.readValueAsTree(JsonParser.java:1352)
at com.github.fge.jsonpatch.rfc7386.JsonMergePatchDeserializer.deserialize(JsonMergePatchDeserializer.java:67)
at com.github.fge.jsonpatch.rfc7386.JsonMergePatchDeserializer.deserialize(JsonMergePatchDeserializer.java:92)
at com.github.fge.jsonpatch.rfc7386.JsonMergePatchDeserializer.deserialize(JsonMergePatchDeserializer.java:36)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2034)
at com.github.fge.jsonpatch.rfc7386.SerializationTest.objectSerDeserWorksCorrectly(SerializationTest.java:102)
// JacksonUtils is a class of mine; the method returns an ObjectMapper,
// which extends ObjectCodec
private static final ObjectCodec CODEC = JacksonUtils.newMapper();
jp.setCodec(CODEC);