如何使用Jackson ObjectMapper检测尾随垃圾

时间:2014-09-23 19:27:02

标签: java json parsing jackson

说我有一个班级

class A {
    public int x;
}

然后可以按如下方式解析有效的json:

ObjectMapper mapper = new ObjectMapper();
A a = mapper.readValue("{\"x\" : 3}", A.class);

如果字符串包含的数据超出解析对象所需的数据,是否有办法让解析器失败?

例如,我希望以下内容失败(成功)

A a = mapper.readValue("{\"x\" : 3} trailing garbage", A.class);

我尝试使用带有JsonParser.Feature.AUTO_CLOSE_SOURCE = false的InputStream并检查流是否已被完全消耗,但这不起作用:

A read(String s) throws JsonParseException, JsonMappingException, IOException {
    JsonFactory f = new MappingJsonFactory();
    f.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false);
    ObjectMapper mapper = new ObjectMapper(f);
    InputStream is = new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8));
    try {
        A a = mapper.readValue(is, A.class);
        if(is.available() > 0) {
            throw new RuntimeException();
        }
        return a;
    } finally {
        is.close();
    }
}

read("{\"x\" : 3} trailing garbage");

仍然成功,可能是因为解析器从流中消耗的数量超过了严格必要的数量。

一个有效的解决方案是在从字符串中删除最后一个字符串时验证解析失败:

A read(String s) throws JsonParseException, JsonMappingException, IOException {
    ObjectMapper mapper = new ObjectMapper();
    A a = mapper.readValue(s, A.class);

    if (s.length() > 0) {
        try {
            mapper.readValue(s.substring(0, s.length()-1), A.class);
            throw new RuntimeException();
        } catch (JsonParseException e) {
        }
    }

    return a;
}

但我正在寻找更有效的解决方案。

3 个答案:

答案 0 :(得分:6)

要做的主要是首先单独创建JsonParser,然后调用ObjectMapper.readValue()传递该解析器,然后再次调用nextToken()并验证它返回null(而不是非空值,或抛出异常)。

所以,像

JsonParser jp = mapper.getFactory().createParser(jsonSource);
try {
    Value v = mapper.readValue(jp, Value.class);
    if (jp.nextToken() != null) {
        //throw some exception: trailing garbage detected
    }
    return v;
} finally {
    jp.close();
}

注意:这是针对Jackson 2.x.对于Jackson 1.x,请使用getJsonFactory().createJsonParser()代替getFactory().createParser()

答案 1 :(得分:5)

截至Jackson版本2.9,现在有DeserializationFeature.FAIL_ON_TRAILING_TOKENS可以用来实现:

ObjectMapper objectMapper =
        new ObjectMapper().enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);

请参阅  https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.9https://medium.com/@cowtowncoder/jackson-2-9-features-b2a19029e9ff

答案 2 :(得分:0)

您可以拥有以下内容。 基本上在你readValue之前循环遍历标记。 JsonParser#nextToken()验证您的String是否是正确的JSON。

以下是代码:

public static void main(String[] args) throws JsonParseException, IOException  {

        ObjectMapper mapper = new ObjectMapper();

        String str = "{\"x\" : 3} garbage";

        JsonParser x = mapper.getJsonFactory().createJsonParser(str);
        while(x.nextToken()!=null){

        }
    A a =   mapper.readValue(str, A.class);

        System.out.println(a);
    }

控制台输出:

Exception in thread "main" org.codehaus.jackson.JsonParseException: Unexpected character ('g' (code 103)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
 at [Source: java.io.StringReader@65faba46; line: 1, column: 12]
    at org.codehaus.jackson.JsonParser._constructError(JsonParser.java:1432)
    at org.codehaus.jackson.impl.JsonParserMinimalBase._reportError(JsonParserMinimalBase.java:385)
    at org.codehaus.jackson.impl.JsonParserMinimalBase._reportUnexpectedChar(JsonParserMinimalBase.java:306)
    at org.codehaus.jackson.impl.ReaderBasedParser._handleUnexpectedValue(ReaderBasedParser.java:1192)
    at org.codehaus.jackson.impl.ReaderBasedParser.nextToken(ReaderBasedParser.java:479)
    at example.JsonParse.main(JsonParse.java:21)