简体   繁体   中英

How to detect trailing garbage using Jackson ObjectMapper

Say I have a class

class A {
    public int x;
}

Then a valid json can be parsed as follows:

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

Is there a way to have the parser fail if the string contains more data than necessary to parse the object?

For example I would like the following to fail (which succeeds)

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

I tried it using an InputStream with JsonParser.Feature.AUTO_CLOSE_SOURCE=false and checking whether the stream has been consumed completely, but that does not work:

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

that is,

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

still succeeds, probably because the parser consumes more from the stream than strictly necessary.

One solution that works is to verify that the parsing fails when dropping the last charachter from the string:

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

but I'm looking for a more efficient solution.

The main thing to do is to create a JsonParser first, separately, then call ObjectMapper.readValue() passing that parser, and THEN call nextToken() once more and verify it returns null (instead of non-null value, or throw exception).

So, something like

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

Note: This is for Jackson 2.x. For Jackson 1.x, use getJsonFactory().createJsonParser() instead of getFactory().createParser() .

As of Jackson version 2.9, there is now DeserializationFeature.FAIL_ON_TRAILING_TOKENS which can be used to achieve that:

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

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

You can have something like below. Basically loop through the tokens before you readValue . JsonParser#nextToken() validates if your String is a proper JSON.

Here is the code:

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

console output:

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM