Skip to content

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

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):
  }
   ^

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions