From ca9dae0c160cb9b620c7b9afa06d34cc566c6c69 Mon Sep 17 00:00:00 2001 From: Petr Onderka Date: Mon, 21 Oct 2019 22:37:17 +0200 Subject: [PATCH] Cache polymorphic properties (dotnet/corefx#41753) * Cache polymorphic properties * Move RuntimePropertyCache to JsonClassInfo * Added test of RuntimePropertyCache using properties with different attributes * Fixed typo Co-Authored-By: Ahson Khan * Use allocating overload of GetOrAdd on .Net Standard 2.0 Commit migrated from https://github.com/dotnet/corefx/commit/46299619e290936c495264162ede68354ced5b18 --- .../Serialization/JsonClassInfo.AddProperty.cs | 63 +++++++++++++--------- .../Text/Json/Serialization/JsonClassInfo.cs | 4 ++ .../JsonSerializer.Read.HandleArray.cs | 2 +- .../JsonSerializer.Read.HandleValue.cs | 2 +- .../Serialization/JsonSerializer.Write.Helpers.cs | 2 +- .../tests/Serialization/Object.WriteTests.cs | 25 +++++++++ 6 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs index 4ba60eb..4bcb3cb 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Concurrent; using System.Diagnostics; using System.Reflection; using System.Text.Json.Serialization; +using System.Threading; namespace System.Text.Json { @@ -130,33 +132,44 @@ namespace System.Text.Json options); } - internal JsonPropertyInfo CreatePolymorphicProperty(JsonPropertyInfo property, Type runtimePropertyType, JsonSerializerOptions options) + internal JsonPropertyInfo GetOrAddPolymorphicProperty(JsonPropertyInfo property, Type runtimePropertyType, JsonSerializerOptions options) { - ClassType classType = GetClassType( - runtimePropertyType, - Type, - property.PropertyInfo, - out _, - out Type elementType, - out Type nullableType, - out _, - out JsonConverter converter, - checkForAddMethod: false, - options); - - JsonPropertyInfo runtimeProperty = CreateProperty( - property.DeclaredPropertyType, - runtimePropertyType, - property.PropertyInfo, - parentClassType: Type, - collectionElementType: elementType, - nullableType, - converter, - classType, - options: options); - property.CopyRuntimeSettingsTo(runtimeProperty); + static JsonPropertyInfo CreateRuntimeProperty((JsonPropertyInfo property, Type runtimePropertyType) key, (JsonSerializerOptions options, Type classType) arg) + { + ClassType classType = GetClassType( + key.runtimePropertyType, + arg.classType, + key.property.PropertyInfo, + out _, + out Type elementType, + out Type nullableType, + out _, + out JsonConverter converter, + checkForAddMethod: false, + arg.options); + + JsonPropertyInfo runtimeProperty = CreateProperty( + key.property.DeclaredPropertyType, + key.runtimePropertyType, + key.property.PropertyInfo, + parentClassType: arg.classType, + collectionElementType: elementType, + nullableType, + converter, + classType, + options: arg.options); + key.property.CopyRuntimeSettingsTo(runtimeProperty); + + return runtimeProperty; + } - return runtimeProperty; + ConcurrentDictionary<(JsonPropertyInfo, Type), JsonPropertyInfo> cache = + LazyInitializer.EnsureInitialized(ref RuntimePropertyCache, () => new ConcurrentDictionary<(JsonPropertyInfo, Type), JsonPropertyInfo>()); +#if BUILDING_INBOX_LIBRARY + return cache.GetOrAdd((property, runtimePropertyType), (key, arg) => CreateRuntimeProperty(key, arg), (options, Type)); +#else + return cache.GetOrAdd((property, runtimePropertyType), key => CreateRuntimeProperty(key, (options, Type))); +#endif } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs index b10ed84..d34f8a1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; @@ -27,6 +28,9 @@ namespace System.Text.Json // All of the serializable properties on a POCO (except the optional extension property) keyed on property name. public volatile Dictionary PropertyCache; + // Serializable runtime/polymorphic properties, keyed on property and runtime type. + public ConcurrentDictionary<(JsonPropertyInfo, Type), JsonPropertyInfo> RuntimePropertyCache; + // All of the serializable properties on a POCO including the optional extension property. // Used for performance during serialization instead of 'PropertyCache' above. public volatile JsonPropertyInfo[] PropertyCacheArray; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs index 073a73f..b03b93a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs @@ -29,7 +29,7 @@ namespace System.Text.Json } else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown) { - jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options); + jsonPropertyInfo = state.Current.JsonClassInfo.GetOrAddPolymorphicProperty(jsonPropertyInfo, typeof(object), options); } // Verify that we have a valid enumerable. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleValue.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleValue.cs index fb15f4e..45620aa 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleValue.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleValue.cs @@ -24,7 +24,7 @@ namespace System.Text.Json } else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown) { - jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options); + jsonPropertyInfo = state.Current.JsonClassInfo.GetOrAddPolymorphicProperty(jsonPropertyInfo, typeof(object), options); } jsonPropertyInfo.Read(tokenType, ref state, ref reader); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs index 88acff6..b990c37 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs @@ -31,7 +31,7 @@ namespace System.Text.Json // Nothing to do for typeof(object) if (runtimeType != typeof(object)) { - jsonPropertyInfo = jsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, runtimeType, options); + jsonPropertyInfo = jsonClassInfo.GetOrAddPolymorphicProperty(jsonPropertyInfo, runtimeType, options); } } } diff --git a/src/libraries/System.Text.Json/tests/Serialization/Object.WriteTests.cs b/src/libraries/System.Text.Json/tests/Serialization/Object.WriteTests.cs index 52a960e..5089cc2 100644 --- a/src/libraries/System.Text.Json/tests/Serialization/Object.WriteTests.cs +++ b/src/libraries/System.Text.Json/tests/Serialization/Object.WriteTests.cs @@ -75,5 +75,30 @@ namespace System.Text.Json.Serialization.Tests public string NonIndexerProp { get; set; } } + + [Fact] + public static void WritePolymorhicSimple() + { + string json = JsonSerializer.Serialize(new { Prop = (object)new[] { 0 } }); + Assert.Equal(@"{""Prop"":[0]}", json); + } + + [Fact] + public static void WritePolymorphicDifferentAttributes() + { + string json = JsonSerializer.Serialize(new Polymorphic()); + Assert.Equal(@"{""P1"":"""",""p_3"":""""}", json); + } + + private class Polymorphic + { + public object P1 => ""; + + [JsonIgnore] + public object P2 => ""; + + [JsonPropertyName("p_3")] + public object P3 => ""; + } } } -- 2.7.4