2017-09-25 31 views
0

我需要将JsonArray反序列化为布尔值。如果数组存在且不为空,则该值应设置为“true”。 问题是,我的自定义反序列化器在功能上打破了其余字段的反序列化 - 它们被设置为null。Jackson定制解串器打破其他字段的反序列化

对象:

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; 
    } 
} 

测试

@Test 
public void test() throws JsonParseException, IOException { 
    Boolean result = deserialize(); 
} 

private Boolean deserialize() throws IOException, JsonParseException, 
     JsonProcessingException { 
    TestObject testObject = mapper.readValue("{\n" + 
        " \"arr\": [\n" + 
        "  {\"value\": \"New\"}\n" + 
        " ],\n" + 
        " \"name\": \"name\",\n" + 
        " \"logins\": 36" + 
        "}", 
        TestObject.class); 

    System.out.println(testObject.toString()); 

    return testObject.getExists(); 
} 

如果我删除了 “ARR” 阵列或将其移动到JSON,底部一切都很好。如果我把它留在顶部 - TestObject{name='null', exists=true, logins=null}

还有类似的问题(Jackson Custom Deserializer breaks default ones),但不幸的是它没有答案。 因为当我重新排列Json时,代码可以正常工作,所以它看起来并不像自定义反序列化器用于所有字段,而是Jackson在执行自定义反序列化器时停止反序列化。

+1

我的建议是不要将数组反序列化为布尔值。将数组反序列化为一个数组/列表/集合并使'getExists'返回'arr!= null'或其他类型。 – Paul

+0

@Paul这真的有用,感谢这个想法,不知何故,我没有想到这一点。问题是它看起来有点丑陋和晦涩,但如果没有其他办法,我会这样做。 –

+0

有时,你必须选择两个丑陋的解决方案的漂亮:)我很高兴我可以帮助...我会让我的评论一个答案。 – Paul

回答

1

你解串器可能不感兴趣的数组的内容但仍然必须推进解析器上的读取标记。

@Override 
public Boolean deserialize(JsonParser jp, DeserializationContext ctxt) 
     throws IOException { 
    JsonNode node = jp.getCodec().readTree(jp); 
    return node.size() != 0; 
} 

决定集合的大小,而不是在一个集合的存在

你可以根据集合的大小读取arr值一次,并决定所有这一切都是必需的,因为您的字符串化对象包含一个ARR或从未执行Deserializer.deserialize()。在这种情况下,物业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; 
} 
+0

这是适当的解决方案。 'JsonDeserializer' javadoc提到你必须在从解串器返回之前将读标记移动到下一个元素,以便Jackson可以开始处理下一个元素。 在我的情况下,它是一个数组,所以移动一次,就像我尝试的那样,还不够,在达到'END_ARRAY'之前,您必须多次移动它。 'readTree',据我可以告诉从javadoc,读取整个数组,建立一棵树,并提高阅读标记在一个包。 –

1

我的建议是不要将数组反序列化为布尔值。将数组反序列化为一个数组/列表/集合,并且有getExists返回arr != null或其他。

例如,如果JSON你要反序列化看起来是这样的:

{ 
    "arr": [{"value": "New"}], 
    "name": "name", 
    "logins": 36 
} 

这样写你的实体:

private static class TestObject 
{ 
    private List<Map<String, String>> arr; 

    // other getters/setters removed for brevity 

    public boolean getExists() 
    { 
    return arr !=null && !arr.isEmpty(); 
    } 
} 
0

其实还有第三个办法做到这一点,适合我的情况 - JsonArray总是包含当它不缺,所以我需要的是适当超前的读取标记,然后返回true至少一个元素:

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

除非你不需要数组的内容进行一些额外的工作,只是移动标记比readTree快几毫秒。

相关问题