-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Change JsonException.Path to be a json-based path #37938
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall I think this looks good. Would like some more tests.
I haven't looked closely at the implementation.
src/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs
Outdated
Show resolved
Hide resolved
{ | ||
try | ||
{ | ||
JsonSerializer.Parse<Dictionary<string, int>>(@"{""Key1"":1, ""Key2"":bad}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if the JSON is @"{""Key1"":1, ""Key2"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if the JSON is @"{""Key1"":1, ""Key2"""
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there is no :
delimiter, it would be $.Key1
I'll add a test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The path should be $
. The error didn't happen at $.Key1
. Key1 just happened to be the last property read. The error happened on attempting to read a the next name/value from $
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Situations like this is why I kept things simple in Json.NET and only included the known parent segments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current Path semantics follow the reader semantics. If we want different semantics we'd likely have to change the reader to not accept a space for the delimiter cc @ahsonkhan
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An error didn't happen parsing $.Key1
. Key1 is done. It's finished and in the past. The state should have been popped off the stack when its value (1
) was finished deserializing. The new error happened when parsing a parameter on the root object: $
. I don't know what the reader semantics have to do with it.
Reading through the JSON, and what the path would be if an error was thrown:
Read: {
Path: $
Read: Key1
Path: $
(because you haven't finished reading Key1)
Read: 1
Path: $.Key1
Read: Key2
Path: $
(Key1 was popped off the stack once its value was read. Now you start reading the next property on the root object)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The serializer currently does not track state separate from the reader. So if the reader says a property name or array element is "read" successfully then the serializer treats that as part of the path. Let's discuss in next meeting.
{ | ||
try | ||
{ | ||
JsonSerializer.Parse<int[]>(@"[1, bad]"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if the JSON is @"[1"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if the JSON is @"[1 /* comment starts but doesn't end"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The semantics are currently determined by the reader on what it treats as delimiters or separators for a given unit.
For [1
it would be $[0]
For [1 /* comment
it would be $[1]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For
[1 /* comment
it would be$[1]
$[1]
isn't correct. There is no second item in [1 /* comment
.
The index shouldn't be increased unless a comma is read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Situations like this is why I kept things simple in Json.NET and only included the known parent segments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same response as above @ahsonkhan
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@steveharter, can you please file an issue to track this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -10,7 +11,9 @@ namespace System.Text.Json.Serialization | |||
[DebuggerDisplay("Current: ClassType.{Current.JsonClassInfo.ClassType}, {Current.JsonClassInfo.Type.Name}")] | |||
internal struct ReadStack | |||
{ | |||
// A fields is used instead of a property to avoid value semantics. | |||
private static readonly char[] SpecialCharacters = { '.', ' ', '\'', '/', '"', '[', ']', '(', ')', '\t', '\n', '\r', '\f', '\b', '\\', '\u0085', '\u2028', '\u2029' }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the same as Json.NET?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes verbatim pending review. cc @ahsonkhan
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'\u0085', '\u2028', '\u2029'
might be concerning, but I am not sure if it this issue applies here: https://github.com/dotnet/corefx/issues/33321
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the performance regression in both serializer/deserializer due to this change? Can you measure and share the results?
https://github.com/dotnet/performance/blob/3439a62de1e17c5daa99086bdd6cda31384ade91/src/benchmarks/micro/corefx/System.Text.Json/Serializer/ReadJson.cs#L11
https://github.com/dotnet/performance/blob/3439a62de1e17c5daa99086bdd6cda31384ade91/src/benchmarks/micro/corefx/System.Text.Json/Serializer/WriteJson.cs#L11
...ystem.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs
Outdated
Show resolved
Hide resolved
@ahsonkhan I previously measured benchmarks -- with case-sensitive the results didn't change (within margin of error). I re-ran locally with threshold of 1% and results are consistent with that:
|
CI failure on |
My feedback?? |
Fixes https://github.com/dotnet/corefx/issues/37768
Currently we escape the property names in the Path.
We need to be clear on what is reported on invalid array element. Do we return the invalid array element, or the "last known good" element. For example does
[1,X
report a Path of$[0]
(last known good) or$[1]
(invalid element even though it's not a valid indexer). Currently the code returns$[1]
for this case. UPDATE: this behavior has been approved.The path is not followed into calls into
JsonDocument
which occurs during deserialization of type(object) and for the overflow[DataExtensionProperty]. The current plan is to create another issue to address this by first adding support withinJsonDocument
to support Path (which would work when not called by the serializer), and then have the serializer catch the appropriateJsonReaderException
thrown byJsonDocument
and prepend the exception's Path with the Path that existed before the call toJsonDocument
and then re-throw asJsonException
. UPDATE: see issue https://github.com/dotnet/corefx/issues/38074