Skip to content

JsonLocation consistently off by one character for many invalid JSON parsing cases #1173

@hal7df

Description

@hal7df

Issue

The JsonLocation attached to a JsonProcessingException thrown when parsing an invalid JSON string is consistently one character to the right of the invalid character, except in cases where the error is due to an unexpected EOF. This affects both JsonLocation.getCharOffset() and JsonLocation.getColumnNr() (presumably JsonLocation.getByteOffset() as well, although I haven't tested that explicitly); because this information is used to construct the exception message, that is affected as well.

I first noticed this on Jackson 2.10.0, but the issue persists on Jackson 2.16.0.

Minimally reproducible example

package example;

import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonLocationTest {
    public static void main(String[] args) {
        for (String input : INVALID_JSON) {
            try {
                MAPPER.readTree(input);
            } catch (JsonProcessingException jpe) {
                JsonLocation location = jpe.getLocation();

                System.out.printf(
                    "%s (at line %d, column %d):%n  %s%n  ",
                    jpe.getOriginalMessage(),
                    location.getLineNr(),
                    location.getColumnNr(),
                    input
                );

                // annotate the location of the error on the previous line
                for (int i = 0; i < location.getCharOffset(); ++i) {
                  System.out.print(' ');
                }

                System.out.println('^');
            }
        }
    }

    private static final String[] INVALID_JSON = new String[] {
        "{\"invalid\" \"json\"}",
        "{\"also\", \"invalid\"}",
        "{\"still\":[\"invalid\"[]]}",
        "[\"json\":\"doesn't\",\"work\":\"like\",\"this\"]",
        "{\"really\": \"funky💃\" \"unicode\"}",
        "{",
        "}"
    };
    private static final ObjectMapper MAPPER = new ObjectMapper();
}

Expected output

Unexpected character ('"' (code 34)): was expecting a colon to separate field name and value (at line 1, column 12):
  {"invalid" "json"}
             ^
Unexpected character (',' (code 44)): was expecting a colon to separate field name and value (at line 1, column 8):
  {"also", "invalid"}
         ^
Unexpected character ('[' (code 91)): was expecting comma to separate Array entries (at line 1, column 20):
  {"still":["invalid"[]]}
                     ^
Unexpected character (':' (code 58)): was expecting comma to separate Array entries (at line 1, column 8):
  ["json":"doesn't","work":"like","this"]
         ^
Unexpected character ('"' (code 34)): was expecting comma to separate Object entries (at line 1, column 22):
  {"really": "funky💃" "unicode"}
                       ^
Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1]) (at line 1, column 2):
  {
   ^
Unexpected close marker '}': expected ']' (for root starting at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1]) (at line 1, column 1):
  }
  ^

Actual output

(The unexpected end-of-input case here is correct IMO, just including it to be thorough)

Unexpected character ('"' (code 34)): was expecting a colon to separate field name and value (at line 1, column 13):
  {"invalid" "json"}
              ^
Unexpected character (',' (code 44)): was expecting a colon to separate field name and value (at line 1, column 9):
  {"also", "invalid"}
          ^
Unexpected character ('[' (code 91)): was expecting comma to separate Array entries (at line 1, column 21):
  {"still":["invalid"[]]}
                      ^
Unexpected character (':' (code 58)): was expecting comma to separate Array entries (at line 1, column 9):
  ["json":"doesn't","work":"like","this"]
          ^
Unexpected character ('"' (code 34)): was expecting comma to separate Object entries (at line 1, column 23):
  {"really": "funky💃" "unicode"}
                        ^
Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1]) (at line 1, column 2):
  {
   ^
Unexpected close marker '}': expected ']' (for root starting at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1]) (at line 1, column 2):
  }
   ^

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.17Issues planned (at earliest) for 2.17

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions