Skip to content

BasicJsonParser mishandles braces within string values - #52182

Open
zhoubiao188 wants to merge 1 commit into
spring-projects:mainfrom
zhoubiao188:fix-basic-json-parser-nesting-in-strings
Open

zhoubiao188 wants to merge 1 commit into
spring-projects:mainfrom
zhoubiao188:fix-basic-json-parser-nesting-in-strings

Conversation

@zhoubiao188

Copy link
Copy Markdown
Contributor

BasicJsonParser.tokenize() tracks the nesting of objects and lists while it splits the JSON on the commas that are not nested, so that a comma inside a string does not separate entries. The tracking of '{', '}', '[' and ']' was applied unconditionally, including for characters that appear within a string literal. An opening brace or bracket in a string value therefore incremented the nesting count, and the comma that follows the entry was no longer treated as a separator. The entries were merged into a single token and silently dropped.

For example:

  • {"foo":"{","bar":"baz"} parsed as a single entry foo with the value {","bar":"baz, instead of foo -> { and bar -> baz
  • ["{","baz"] parsed as a single element {","baz
  • {"foo":"[","bar":"baz"} parsed as a single entry foo with the value [","bar":"baz

A closing brace or bracket within a string has the opposite effect: it drives the nesting count negative, which hides the comma that separates the entries of a sibling object.

  • {"a":"}","b":{"c":"d","e":"f"}} mapped b to {c=d} and promoted e to an entry of the enclosing object, instead of a -> }, b -> {c=d, e=f}

Strings containing commas are already supported and covered by stringContainingComma, so braces and brackets within a string need to be handled in the same way. The tokenizer already tracks the in-string state in Tracked.VALUE; this commit uses it to only update the nesting count when the character is not part of a string.

GsonJsonParser and JacksonJsonParser both return the expected result for all of the inputs above.

The four new tests fail on main and pass with the change:

BasicJsonParserTests > stringContainingOpeningBrace() FAILED
BasicJsonParserTests > stringContainingOpeningBracket() FAILED
BasicJsonParserTests > listWithStringContainingOpeningBracket() FAILED
BasicJsonParserTests > stringContainingClosingBraceWithNestedMap() FAILED

To make sure the fix does not trade one set of inputs for another, the two parsers were exhaustively compared with GsonJsonParser over every combination of the characters {, }, [, ], , and a placed in the two string values of {"a":"X","b":"Y"} and ["X","Y"]. Of the 3698 inputs that Gson accepts, BasicJsonParser currently returns a wrong result for 1548 of them; with this change it returns the result Gson produces for all 3698, and no input that was previously correct changes its result.

./gradlew :core:spring-boot:checkFormatMain :core:spring-boot:checkFormatTest :core:spring-boot:checkstyleMain :core:spring-boot:checkstyleTest :core:spring-boot:test --tests "org.springframework.boot.json.*" passes.

`BasicJsonParser.tokenize()` tracks the nesting of objects and lists while
it splits the JSON on the commas that are not nested. The tracking of
`'{'`, `'}'`, `'['` and `']'` was applied unconditionally, including when
the character appears within a string literal. An opening brace or bracket
in a string value therefore incremented the nesting count, and the comma
that follows the entry was no longer treated as a separator. The entries
were merged into a single token and silently dropped.

For example, `{"foo":"{","bar":"baz"}` was parsed as a single entry `foo`
with the value `{","bar":"baz` rather than `foo` -> `{` and
`bar` -> `baz`. In the same way `["{","baz"]` produced a single element.

A closing brace within a string had the opposite effect, driving the count
negative and hiding the comma that separates the entries of a sibling
object. `{"a":"}","b":{"c":"d","e":"f"}}` then mapped `b` to `{c=d}` and
turned `e` into an entry of the enclosing object.

This commit only updates the nesting count when the character is not part
of a string literal, which restores the behaviour of the Gson and Jackson
parsers for these inputs. Strings containing commas are already supported
and tested, so braces and brackets within a string must be handled in the
same way.

Signed-off-by: zhoubiao188 <32508238+zhoubiao188@users.noreply.github.com>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Oct 13, 2026
@wilkinsona wilkinsona changed the title Fix BasicJsonParser nesting tracking for braces within strings BasicJsonParser mishandles braces within string values Oct 14, 2026
@wilkinsona wilkinsona added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Oct 14, 2026
@wilkinsona wilkinsona added this to the 4.0.x milestone Oct 14, 2026

This branch has not been deployed

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

Labels

type: bug A general bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants