Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 3, 2026

Description

JsonNode.GetPath() produces invalid JSON Path syntax when property names contain characters requiring escaping. Two issues:

  1. Property names starting with $ (e.g., $defs, $ref) used dot notation ($.$defs) instead of bracket notation ($['$defs'])
  2. Single quotes and backslashes in property names were not escaped in bracket notation

Before:

JsonNode.Parse("""{"$defs":{"foo['bar":"baz"}}""")["$defs"]["foo['bar"].GetPath();
// Returns: $.$defs['foo['bar']  (invalid JSON Path)

After:

// Returns: $['$defs']['foo[\'bar']  (valid JSON Path)

Changes:

  • Add $ to special characters triggering bracket notation in JsonReaderHelper.cs
  • Add AppendEscapedPropertyName helper methods that escape '\' and \\\, optimized using IndexOfAny to append prefix slices directly instead of per-character iteration
  • Add StringBuilder.Append(ReadOnlySpan<char>) polyfill in Common/src/System/Text/StringBuilderExtensions.cs for .NET Standard 2.0 and .NET Framework support
  • Update JsonObject.GetPath, WriteStack.AppendPropertyName, and ReadStack.AppendPropertyName to use escaping
  • Update test expectations to reflect corrected JSON Path syntax

Customer Impact

Users receiving invalid JSON Path strings from GetPath() that cannot be parsed or used with JSON Path tools.

Regression

No. This behavior has existed since the feature was introduced.

Testing

  • Added new test cases covering $-prefixed names, single quotes, backslashes, and combinations
  • Added reproduction case from issue
  • All 49,808 System.Text.Json tests pass

Risk

Low. The change makes output spec-compliant. Existing consumers expecting the old (invalid) format may need adjustment.

Package authoring no longer needed in .NET 9

IMPORTANT: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older versions.

Original prompt

This section details on the original issue you should resolve

<issue_title>JsonNode.GetPath() doesn't consider character escaping</issue_title>
<issue_description>### Description

The JsonNode.GetPath() generally does well for C#-valid property names, but any string is valid as a key in JSON, and when a key has characters that need escaping, nothing happens and you get an invalid (unparsable) JSON Path as a result.

Reproduction Steps

JsonNode.Parse("""{"$defs":{"foo['bar":"baz"}}""")["$defs"]["foo['bar"].GetPath();

Expected behavior

Returns $.$defs['foo[\'bar'].

Returns $['$defs']['foo[\'bar'].

  • The single quote needs to be escaped.
  • $defs is not valid as a shorthand property name, so it needs to be in bracketed syntax.

Actual behavior

Returns $.$defs['foo['bar'].

Regression?

Unknown

Known Workarounds

None.

Configuration

Seems to happen everywhere. dotnetfiddle, Linux.

Will update with more details in a comment.

Other information

Originally reported json-everything/json-everything#406.
</issue_description>

Comments on the Issue (you are @copilot in this section)

@ Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis See info in [area-owners.md](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md) if you want to be subscribed.
Issue Details

Description

The JsonNode.GetPath() generally does well for C#-valid property names, but any string is valid as a key in JSON, and when a key has characters that need escaping, nothing happens and you get an invalid (unparsable) JSON Path as a result.

Reproduction Steps

JsonNode.Parse("""{"$defs":{"foo['bar":"baz"}}""")["$defs"]["foo['bar"].GetPath();

Expected behavior

Returns $.$defs['foo[\'bar'].

Actual behavior

Returns $.$defs['foo['bar'].

Regression?

Unknown

Known Workarounds

None.

Configuration

Seems to happen everywhere. dotnetfiddle, Linux.

Will update with more details in a comment.

Other information

Originally reported json-everything/json-everything#406.

Author: gregsdennis
Assignees: -
Labels:

area-System.Text.Json, untriaged

Milestone: -

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits January 3, 2026 22:22
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix JsonNode.GetPath method for character escaping Fix JsonNode.GetPath() to properly escape characters in property names Jan 3, 2026
Copilot AI requested a review from stephentoub January 3, 2026 22:36
@stephentoub stephentoub marked this pull request as ready for review January 4, 2026 00:32
Copilot AI review requested due to automatic review settings January 4, 2026 00:32
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes JsonNode.GetPath() to produce valid JSON Path syntax when property names contain special characters requiring escaping. The fix addresses two specific issues: (1) property names starting with $ now use bracket notation instead of invalid dot notation, and (2) single quotes and backslashes in property names are now properly escaped within bracket notation.

Key Changes:

  • Added $ to the set of special characters that trigger bracket notation in JSON Path
  • Implemented AppendEscapedPropertyName helper methods to escape ' as \' and \ as \\ within bracket notation
  • Updated all JSON Path generation sites (JsonObject.GetPath, WriteStack.AppendPropertyName, ReadStack.AppendPropertyName) to use the new escaping

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
JsonReaderHelper.cs Added $ to special characters set; implemented two overloads of AppendEscapedPropertyName for escaping quotes and backslashes
JsonObject.cs Updated GetPath to call AppendEscapedPropertyName when using bracket notation
WriteStack.cs Updated AppendPropertyName to call AppendEscapedPropertyName when using bracket notation
ReadStack.cs Updated AppendPropertyName to call AppendEscapedPropertyName when using bracket notation
ParentPathRootTests.cs Added comprehensive test cases covering $-prefixed names, quotes, backslashes, and nested scenarios
PolymorphicTests.CustomTypeHierarchies.cs Updated test expectations from dot notation (e.g., $.$type) to bracket notation (e.g., $['$type'])
ReferenceHandlerTests.Deserialize.cs Updated test expectations from dot notation to bracket notation for $-prefixed metadata properties
ConstructorTests.Exceptions.cs Updated test expectations from dot notation to bracket notation for $-prefixed properties

…acter iteration for prefix

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
…thod

Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Copilot AI requested a review from eiriktsarpalis January 12, 2026 13:11
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI requested a review from stephentoub January 13, 2026 13:21
@stephentoub stephentoub enabled auto-merge (squash) January 13, 2026 13:29
@stephentoub stephentoub merged commit 7e4d8ce into main Jan 13, 2026
91 checks passed
@eiriktsarpalis eiriktsarpalis deleted the copilot/fix-jsonnode-getpath-escaping branch January 15, 2026 09:23
akoeplinger added a commit to dotnet/aspnetcore that referenced this pull request Jan 15, 2026
This was an intentional behavioral change to System.Text.Json, see dotnet/runtime#122841
dotnet-maestro bot added a commit to dotnet/aspnetcore that referenced this pull request Jan 15, 2026
[main] Source code updates from dotnet/dotnet


 - Update .devcontainer/devcontainer.json

 - Fix more 10.0 references

 - Fix more 10.0 references

 - Suppress EF8001 for now, see #65018

 - Update SDK

 - Fix JSON

 - Merge branch 'main' into darc-main-62ab566a-e4e9-4ec8-b67d-2074a5ad3974

 - Merge branch 'main' into darc-main-62ab566a-e4e9-4ec8-b67d-2074a5ad3974

# Conflicts:
#	src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs
#	src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Data/SqlLite/ApplicationDbContextModelSnapshot.cs
#	src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs
#	src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Data/SqlServer/ApplicationDbContextModelSnapshot.cs

 - Fix JsonFormatter_EscapedKeys_SingleQuote test

This was an intentional behavioral change to System.Text.Json, see dotnet/runtime#122841
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JsonNode.GetPath() doesn't consider character escaping

3 participants