-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New serializer converter model for objects and collections #2259
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.
Will continue reviewing tomorrow.
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
...m.Text.Json/src/System/Text/Json/Serialization/Converters/JsonIEnumerableDefaultConverter.cs
Show resolved
Hide resolved
...s/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleMetadata.cs
Show resolved
Hide resolved
...s/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
...s/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Text.Json/tests/Serialization/ReferenceHandlingTests.Deserialize.cs
Outdated
Show resolved
Hide resolved
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.
Regarding changes related to ReferenceHandling
, most of the things that differs from before are the exception messages. Also I think we should discuss what are the concrete guidelines for building the JSON path in Deserialize errors.
...em.Text.Json/src/System/Text/Json/Serialization/Converters/JsonDictionaryDefaultConverter.cs
Show resolved
Hide resolved
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
...m.Text.Json/src/System/Text/Json/Serialization/Converters/JsonIEnumerableDefaultConverter.cs
Show resolved
Hide resolved
...s/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleMetadata.cs
Show resolved
Hide resolved
src/libraries/System.Text.Json/tests/Serialization/ReferenceHandlingTests.Deserialize.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Text.Json/tests/Serialization/ReferenceHandlingTests.Deserialize.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Text.Json/tests/Serialization/ReferenceHandlingTests.Deserialize.cs
Show resolved
Hide resolved
...em.Text.Json/src/System/Text/Json/Serialization/Converters/JsonDictionaryDefaultConverter.cs
Outdated
Show resolved
Hide resolved
...System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterNullable.cs
Show resolved
Hide resolved
...ystem.Text.Json/src/System/Text/Json/Serialization/Converters/JsonIEnumerableOfTConverter.cs
Show resolved
Hide resolved
....Json/src/System/Text/Json/Serialization/Converters/JsonIEnumerableWithAddMethodConverter.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Text.Json/tests/Serialization/Value.ReadTests.SpecializedCollections.cs
Outdated
Show resolved
Hide resolved
...em.Text.Json/src/System/Text/Json/Serialization/Converters/JsonDictionaryDefaultConverter.cs
Show resolved
Hide resolved
_memberAccessorStrategy = new ReflectionMemberAccessor(); | ||
} | ||
#elif NETFRAMEWORK | ||
#if NETFRAMEWORK || NETCOREAPP |
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.
Currently System.Text.Json does not target NetStandard2.1. If we did add that configuration then we could use RuntimeFeature.IsDynamicCodeSupported
to determine the strategy, but adding a 2.1 configuration is likely not going to help with 5.0 since Xamarin and other will just target netcoreapp5.0 instead.
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.
Let's discuss outside this PR. Maybe we can still use IsDynamicSupported
for the netcoreapp configs. Since we also target netstandard2.0 in this library, we'll always have to ifdef that anyway.
5509f64
to
45432a2
Compare
Looks like most of the benchmarks had an allocation increase of about ~410 bytes (both for serialize and deserialize). Do we know where that's coming from? List Serialize |
src/libraries/System.Text.Json/tests/Serialization/Value.ReadTests.NonGenericCollections.cs
Outdated
Show resolved
Hide resolved
...m.Text.Json/src/System/Text/Json/Serialization/Converters/JsonIEnumerableConverterFactory.cs
Outdated
Show resolved
Hide resolved
...ystem.Text.Json/src/System/Text/Json/Serialization/Converters/JsonIEnumerableOfTConverter.cs
Show resolved
Hide resolved
...ystem.Text.Json/src/System/Text/Json/Serialization/Converters/JsonICollectionOfTConverter.cs
Show resolved
Hide resolved
...System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonObjectFactoryConverter.cs
Outdated
Show resolved
Hide resolved
...em.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterKeyValuePair.cs
Show resolved
Hide resolved
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonValueConverterOfT.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonValueConverterOfT.cs
Show resolved
Hide resolved
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs
Show resolved
Hide resolved
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.ReadAhead.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonIEnumerableConverter.cs
Outdated
Show resolved
Hide resolved
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Show resolved
Hide resolved
...es/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleMetadata.cs
Outdated
Show resolved
Hide resolved
|
||
if (GetMetadataPropertyName(propertyName) != MetadataPropertyName.Values) | ||
{ | ||
ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(converter.TypeToConvert, reader, ref state); |
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.
This one is weird:
Before, if we had more than one $id
property, e.g. {"$id": "1", "$id" :"2", "$values": []}
, we would throw The metadata property '$id' must be the first property in the JSON object.
.
Now, we are throwing Invalid property '$id' found within a JSON object that *must only contain metadata properties* and the nested JSON array to be preserved.
which is weird since $id
is indeed a metadata property.
Fixing this would imply to extend the logic to choose what to throw depending on GetMetadataPropertyName
, similar to ThrowUnexpectedMetadataException on line 253.
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 can't even think of how we can ensure that on a test, both messages contain the literal '$id'
and the Json path is $.$id
on both.
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.
Perhaps if we collapse the exception messages we wouldn't have to condition here?
...em.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterKeyValuePair.cs
Show resolved
Hide resolved
...em.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterKeyValuePair.cs
Show resolved
Hide resolved
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs
Outdated
Show resolved
Hide resolved
...System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonObjectDefaultConverter.cs
Show resolved
Hide resolved
Highlighting the breaking change here. Serializing non-supported dictionaries now deterministically throws (regardless of the runtime values). For example public static void TestSerializeStringAsObjecDictionaryKeys()
{
var notSupportedDictionary = new Dictionary<object, object>
{
{ "foo", "bar" }
};
// Before (3.1): Returned a string with value "{"foo":"bar"}"
// After (5.0): System.NotSupportedException: The type 'System.Collections.Generic.Dictionary`2[System.Object,System.Object]' is not supported.
JsonSerializer.Serialize(notSupportedDictionary);
} |
Is this change in 5.0 Preview2?
I tried to directly reference System.Text.Json 5.0.0-preview.2.20160.6 but get the same exception. |
@steveharter this is labeled breaking change for reason noted by @ahsonkhan . Can you please open an issue if necessary with https://github.com/dotnet/docs/issues/new?template=dotnet-breaking-change.md I don't see an existing one |
@danmosemsft The breaking change that was pointed out by @ahsonkhan no longer occurs. Here's an updated repro: var notSupportedDictionary = new Dictionary<object, object> {{ "foo", "bar" }};
// Before (3.1): Returned a string with value "{"foo":"bar"}"
// After (5.0): Now also returns the same
string json = JsonSerializer.Serialize(notSupportedDictionary);
Debug.Assert(json == "{\"foo\":\"bar\"}"); |
Even better! |
The only potential breaking change that I'm aware of is calling We should doc this in the release notes, so keeping the breaking change tag probably still makes sense. |
FYI this is due to @jozkee's work on supporting non-string dictionary keys: #38056. |
As part of #1562 this PR refactors the converter model for objects and collection.
No public APIs have changed with this PR - changes were made to be internal for now.
Short-term this PR improves perf and maintainability.
Longer-term a subsequent PR will be created for the proposed public API changes which are expected to add capabilities as explained in the above link (mostly supporting code-gen for AOT and adding public APIs for extensibility and new converter capabilities).
Notes:
JsonConverter
class). The previous "main loop" and side infrastructure for objects and collections was removed. This greatly simplifies the many supported collections and increases perf since the previous "main loop" logic can be avoided for each property and\or element; collection elements are no longer boxed.JsonPropertyInfo
any longer (and associated cache) so it is much faster.Skip
functionality instead of the "main loop" eating the various JSON (the implementation is much faster now). The previous code was written before the reader'sSkip
functionality existed.KeyValuePair<TKey, TValue>
converter now re-enters the serializer forTKey
andTValue
without breaking "JsonPath" functionality (if any exceptions are thrown).Performance
The perf benefits are significant for large collections (~1.15x-1.5x on deserialize, ~1.5x-2.4x+ on serialize).
There is currently room for improvement after this PR for additional gains in the areas of:
Examples of large collection (1,024 elements) perf improvement:
DateTime[]
Dictionary<string,string>
List
Below are all current benchmarks with a difference of >=3%. These are not large collections and show that non-collection perf stayed the same or improved for the most part.