Java 如何使用Jackson ObjectMapper检测跟踪垃圾

Java 如何使用Jackson ObjectMapper检测跟踪垃圾,java,json,parsing,jackson,Java,Json,Parsing,Jackson,说我有课 class A { public int x; } 然后可以按如下方式解析有效的json: ObjectMapper mapper = new ObjectMapper(); A a = mapper.readValue("{\"x\" : 3}", A.class); 如果字符串包含的数据超过了解析对象所需的数据量,是否有办法使解析器失败 例如,我希望以下操作失败(成功) 我尝试使用带有JsonParser.Feature.AUTO\u CLOSE\u SOURCE=fa

说我有课

class A {
    public int x;
}
然后可以按如下方式解析有效的json:

ObjectMapper mapper = new ObjectMapper();
A a = mapper.readValue("{\"x\" : 3}", A.class);
如果字符串包含的数据超过了解析对象所需的数据量,是否有办法使解析器失败

例如,我希望以下操作失败(成功)

我尝试使用带有JsonParser.Feature.AUTO\u CLOSE\u 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;
}

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

您可以使用下面的方法。 基本上,在您
readValue
之前,循环遍历令牌。 验证字符串是否为正确的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)

要做的主要事情是首先分别创建一个
JsonParser
,然后调用
ObjectMapper.readValue()
传递该解析器,然后再次调用
nextToken()
并验证它返回null(而不是非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()
从Jackson 2.9版开始,现在有了反序列化功能.FAIL_ON_training_标记,可用于实现以下目标:

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

解决方案对您有效吗?谢谢您的回答,这确实有效。我接受了另一个答案,它使用相同的技术,但只需要扫描输入一次。
ObjectMapper objectMapper =
        new ObjectMapper().enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);