-
Notifications
You must be signed in to change notification settings - Fork 4.9k
DictionaryKeyPolicy not applied to keys that contain non primitive ty… #41691
DictionaryKeyPolicy not applied to keys that contain non primitive ty… #41691
Conversation
Thank you for your contribution. As announced in dotnet/coreclr#27549 this repository will be moving to dotnet/runtime on November 13. If you would like to continue working on this PR after this date, the easiest way to move the change to dotnet/runtime is:
|
...System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs
Outdated
Show resolved
Hide resolved
...System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/tests/Serialization/DictionaryTests.KeyPolicy.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/tests/Serialization/DictionaryTests.KeyPolicy.cs
Outdated
Show resolved
Hide resolved
@@ -83,7 +84,9 @@ public static partial class JsonSerializer | |||
|
|||
// An object or another enumerator requires a new stack frame. | |||
state.Push(elementClassInfo, value); | |||
if (options?.DictionaryKeyPolicy != null && state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) | |||
var extensionDataAttribute = jsonPropertyInfo.PropertyInfo?.GetCustomAttribute(typeof(JsonExtensionDataAttribute)); |
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 there a specific reason to check the Attribute?
GetCustomAttribute
uses reflection which is very expensive. I was thinking that the sole condition of state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing
was enough. did you find any issues while just depending on the condition?
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, it is heavy operation, but we need this check for passing test SerializationWithJsonExtensionDataAttribute_IgoneDictionaryKeyPolicy, I'm debugging this test (with extensionDataAttribute) for non-value class types, they were affected by DictionaryKeyPolicy, state.Current.ExtensionDataStatus had NotStarted value.
Without checking attribute, I've got next:
"{""KeyInt"":1000,""KeyString"":""text"",""KeyBool"":true,""keyObject"":{},""keyList"":[],""keyDictionary"":{}}"
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.
I see the problem.
The reason why you get NotStarted
is because, as of now, you are checking after one of the values of the JsonExtensionData
dictionary is pushed into the stack in order to be written on the next iteration. The state of said value does not carry the parent dictionary's ExtensionDataStatus
and therefore you see it as NotStarted
.
To check against the proper ExtensionDataStatus
try to move the condition before calling state.Push
.
|
||
private class TestClassWithDictionary | ||
{ | ||
public Dictionary<string, CustomClass> Data { get; set; } |
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.
A test where we serialize a class that has both a dictionary and extension data (both with contents) would be good:
private class TestClassWithDictionaryAndExtensionData
{
public Dictionary<string, CustomClass> Data { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement> ExtensionData { get; set; }
}
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.
Any special reason to use JsonElement?
In order to pass JsonElement values to the ExtensionData I would need to deserialize into a JsonDocument first, thing that I can avoid by using Dictionary<string, object>
instead.
Formatting issues are causing CI failures: https://github.com/dotnet/corefx/pull/41691/checks?check_run_id=300234753 |
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.
Other than some test nits/suggestions, and @jozkee's feedback, looks good to me.
...System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs
Outdated
Show resolved
Hide resolved
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase | ||
}); | ||
|
||
Assert.Equal(JsonCamel, json); |
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 completeness, can we add another Assert to all these tests where we use both camel case for DictionaryKeyPolicy
and PropertyNamingPolicy
, just to make sure there isn't some unexpected interaction causing an issue?
So:
json = JsonSerializer.Serialize(obj, new JsonSerializerOptions()
{
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
const string JsonCamel = @"{""keyDict"":{""name"":""text"",""number"":1000,""isValid"":true,""values"":[1,2,3]}}";
Assert.Equal(JsonCamel, json);
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.
And then consider renaming the original JsonCamel
to something like JsonDictionaryCamel
to differentiate between the two cases.
{ | ||
{ "KeyDict", CreateCustomObject() } | ||
}; | ||
var json = JsonSerializer.Serialize(obj, new JsonSerializerOptions() |
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 all these tests, please add an assert for the default JsonSerializer.Serialize(...)
output, without any options/naming policy.
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.
Please review my suggetions in order to avoid using reflection.
Other than @layomia and @ahsonkhan's feedback, tests looks good to me.
...System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs
Show resolved
Hide resolved
@@ -83,6 +84,13 @@ public static partial class JsonSerializer | |||
|
|||
// An object or another enumerator requires a new stack frame. | |||
state.Push(elementClassInfo, value); | |||
var extensionDataAttribute = jsonPropertyInfo.PropertyInfo?.GetCustomAttribute(typeof(JsonExtensionDataAttribute)); |
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.
var extensionDataAttribute = jsonPropertyInfo.PropertyInfo?.GetCustomAttribute(typeof(JsonExtensionDataAttribute)); |
@@ -83,6 +84,13 @@ public static partial class JsonSerializer | |||
|
|||
// An object or another enumerator requires a new stack frame. | |||
state.Push(elementClassInfo, value); | |||
var extensionDataAttribute = jsonPropertyInfo.PropertyInfo?.GetCustomAttribute(typeof(JsonExtensionDataAttribute)); | |||
if (options.DictionaryKeyPolicy != null && extensionDataAttribute == null |
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.
if (options.DictionaryKeyPolicy != null && extensionDataAttribute == null |
Move this condition before state.Push
and remove extensionDataAttribute
...System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs
Show resolved
Hide resolved
...System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs
Show resolved
Hide resolved
...System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs
Show resolved
Hide resolved
...System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs
Show resolved
Hide resolved
@@ -83,6 +84,13 @@ public static partial class JsonSerializer | |||
|
|||
// An object or another enumerator requires a new stack frame. | |||
state.Push(elementClassInfo, value); | |||
var extensionDataAttribute = jsonPropertyInfo.PropertyInfo?.GetCustomAttribute(typeof(JsonExtensionDataAttribute)); | |||
if (options.DictionaryKeyPolicy != null && extensionDataAttribute == null | |||
&& state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) |
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.
&& state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) |
Also this condition.
@@ -5,6 +5,7 @@ | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.Reflection; |
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.
using System.Reflection; |
@marcnet80 - would you be able to update this PR today before the repo moves? Would it be ok for @jozkee to push to your branch instead and move this forward so we can merge it in now rather than having to wait? It's almost ready. Thanks. |
I would like to push the requested test changes, however that is going to restart the CI, and since the repository consolidation is occuring in an hour, I am afraid that having a pending CI would prevent this PR from making it to master before consolidation happens, should I hold off said changes? |
I am fine with having the test changes be made in a follow up PR. |
@marcnet80 great job and thank you for your contribution. |
…pes #41176
https://github.com/dotnet/corefx/issues/41176#event-2697607085