json 如何使用JacksonObjectMapper检测尾随垃圾

ubby3x7f  于 2023-01-06  发布在  其他
关注(0)|答案(3)|浏览(111)

假设我有课

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

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

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

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

wydwbb8l

wydwbb8l1#

从Jackson版本2.9开始,现在有反序列化功能. 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

vjhs03f7

vjhs03f72#

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

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

注:这是针对Jackson2.x的。对于Jackson 1.x,请使用getJsonFactory().createJsonParser()而不是getFactory().createParser()

goqiplq2

goqiplq23#

基本上,在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)

相关问题