Java Jackson自定义反序列化程序中断其他字段的反序列化

Java Jackson自定义反序列化程序中断其他字段的反序列化,java,json,serialization,jackson,Java,Json,Serialization,Jackson,我需要将JsonArray反序列化为布尔值。如果数组存在且不为空,则该值应设置为“true”。 问题是,我的自定义反序列化程序在运行时会中断其余字段的反序列化-它们被设置为null 对象: private static class TestObject { private String name; @JsonProperty("arr") @JsonDeserialize(using = Deserializer.class) private Boolean e

我需要将JsonArray反序列化为布尔值。如果数组存在且不为空,则该值应设置为“true”。 问题是,我的自定义反序列化程序在运行时会中断其余字段的反序列化-它们被设置为null

对象:

private static class TestObject {
    private String name;

    @JsonProperty("arr")
    @JsonDeserialize(using = Deserializer.class)
    private Boolean exists = null;

    private Integer logins;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Boolean getExists() {
        return exists;
    }

    public void setExists(boolean exists) {
        this.exists = exists;
    }

    public Integer getLogins() {
        return logins;
    }

    public void setLogins(Integer logins) {
        this.logins = logins;
    }

    @Override
    public String toString() {
        return "TestObject{" +
                "name='" + name + '\'' +
                ", exists=" + exists +
                ", logins=" + logins +
                '}';
    }
}
反序列化程序:

public class Deserializer extends JsonDeserializer<Boolean> {
    @Override
    public Boolean deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException {
        if (jp.getCurrentToken() == JsonToken.START_ARRAY) {
            return true;
        }
        return false;
    }
}
如果我删除“arr”数组或将其移动到Json的底部,一切都很好。如果我把它放在顶部-
TestObject{name='null',exists=true,logins=null}

有一个类似的问题(),但不幸的是它没有答案。
由于重新排列Json时代码工作正常,因此看起来并非所有字段都使用自定义反序列化程序,而是Jackson在执行自定义反序列化程序时停止反序列化。

我的建议是不要将数组反序列化为布尔值。将数组反序列化为数组/列表/集合,并让
getExists
return
arr!=空值
或类似值

例如,如果要反序列化的JSON如下所示:

{
“arr”:[{“value”:“New”}],
“名称”:“名称”,
“登录”:36
}
按如下方式编写实体:

私有静态类TestObject
{
私人名单;
//为简洁起见,删除了其他getter/setter
公共布尔getExists()
{
返回arr!=null&!arr.isEmpty();
}
}

反序列化程序可能对数组的内容不感兴趣,但仍必须在解析器上提前读取标记

您可以立即读取
arr
的值,并根据集合的大小决定:

@Override
public Boolean deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException {
    JsonNode node = jp.getCodec().readTree(jp);
    return node.size() != 0;
}
决定集合的大小而不是集合的存在是必要的,因为字符串化对象包含arr或
反序列化器。反序列化()从不执行。在这种情况下,属性
exist
将为
null
。因此,唯一可能表达的语义是不存在的空集合

出于好奇,我尝试了第二种更明确的方法来保持解析器正常运行。对于现实世界的使用,上述版本绝对是可取的

@Override
public Boolean deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException {
    if (jp.currentToken() == JsonToken.START_ARRAY) {
        jp.nextToken();
        int recursionLevel = 1;
        while(recursionLevel > 0) {
            switch (jp.currentToken()) {
                case START_ARRAY:
                    // just in case of nested arrays
                    recursionLevel++;
                    break;
                case END_ARRAY:
                    recursionLevel--;
                    break;
            }
            jp.nextToken();
        }
        return true;
    }
    return false;
}

实际上还有第三种方法,适合我的情况——当JsonArray不丢失时,它将始终包含至少一个元素,因此我只需要正确地推进读取标记,然后返回
true

if (jp.getCurrentToken().equals(JsonToken.START_ARRAY)) {
    while (!jp.getCurrentToken().equals(JsonToken.END_ARRAY)){
        jp.nextToken();
    }
}
return true;

除非您不需要数组的内容来做一些额外的工作,否则移动标记比
readTree

快几毫秒。我的建议是不要将数组反序列化为布尔值。将数组反序列化为数组/列表/集合,并让
getExists
return
arr!=null
或类似的东西。@Paul这确实有效,谢谢你的主意,不知怎的,我没有想到这一点。问题是它看起来有点难看和晦涩,但如果没有其他方法,我会这样做。有时你必须从两个难看的解决方案中选择更漂亮的:)我很高兴我能帮上忙…我会回答我的评论。这是正确的解决方案
JsonDeserializer
javadoc提到,在从反序列化程序返回之前,必须将读取标记移动到下一个元素,以便Jackson可以开始处理下一个元素。在我的例子中,它是一个数组,所以像我尝试过的那样移动它一次是不够的,在到达
END\u array
之前,必须移动它多次
readTree
,据我从javadoc所知,它读取整个数组,构建一个树,并在一个包中推进读取标记。
if (jp.getCurrentToken().equals(JsonToken.START_ARRAY)) {
    while (!jp.getCurrentToken().equals(JsonToken.END_ARRAY)){
        jp.nextToken();
    }
}
return true;