Skip to content

Commit 6f43b10

Browse files
committed
Cache polymorphic properties (dotnet#41753)
* Cache polymorphic properties * Move RuntimePropertyCache to JsonClassInfo * Added test of RuntimePropertyCache using properties with different attributes * Fixed typo Co-Authored-By: Ahson Khan <[email protected]> * Use allocating overload of GetOrAdd on .Net Standard 2.0
1 parent 8e0ad81 commit 6f43b10

File tree

6 files changed

+70
-28
lines changed

6 files changed

+70
-28
lines changed

src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Collections.Concurrent;
56
using System.Diagnostics;
67
using System.Reflection;
78
using System.Text.Json.Serialization;
9+
using System.Threading;
810

911
namespace System.Text.Json
1012
{
@@ -130,33 +132,44 @@ internal JsonPropertyInfo CreateRootObject(JsonSerializerOptions options)
130132
options);
131133
}
132134

133-
internal JsonPropertyInfo CreatePolymorphicProperty(JsonPropertyInfo property, Type runtimePropertyType, JsonSerializerOptions options)
135+
internal JsonPropertyInfo GetOrAddPolymorphicProperty(JsonPropertyInfo property, Type runtimePropertyType, JsonSerializerOptions options)
134136
{
135-
ClassType classType = GetClassType(
136-
runtimePropertyType,
137-
Type,
138-
property.PropertyInfo,
139-
out _,
140-
out Type elementType,
141-
out Type nullableType,
142-
out _,
143-
out JsonConverter converter,
144-
checkForAddMethod: false,
145-
options);
146-
147-
JsonPropertyInfo runtimeProperty = CreateProperty(
148-
property.DeclaredPropertyType,
149-
runtimePropertyType,
150-
property.PropertyInfo,
151-
parentClassType: Type,
152-
collectionElementType: elementType,
153-
nullableType,
154-
converter,
155-
classType,
156-
options: options);
157-
property.CopyRuntimeSettingsTo(runtimeProperty);
137+
static JsonPropertyInfo CreateRuntimeProperty((JsonPropertyInfo property, Type runtimePropertyType) key, (JsonSerializerOptions options, Type classType) arg)
138+
{
139+
ClassType classType = GetClassType(
140+
key.runtimePropertyType,
141+
arg.classType,
142+
key.property.PropertyInfo,
143+
out _,
144+
out Type elementType,
145+
out Type nullableType,
146+
out _,
147+
out JsonConverter converter,
148+
checkForAddMethod: false,
149+
arg.options);
150+
151+
JsonPropertyInfo runtimeProperty = CreateProperty(
152+
key.property.DeclaredPropertyType,
153+
key.runtimePropertyType,
154+
key.property.PropertyInfo,
155+
parentClassType: arg.classType,
156+
collectionElementType: elementType,
157+
nullableType,
158+
converter,
159+
classType,
160+
options: arg.options);
161+
key.property.CopyRuntimeSettingsTo(runtimeProperty);
162+
163+
return runtimeProperty;
164+
}
158165

159-
return runtimeProperty;
166+
ConcurrentDictionary<(JsonPropertyInfo, Type), JsonPropertyInfo> cache =
167+
LazyInitializer.EnsureInitialized(ref RuntimePropertyCache, () => new ConcurrentDictionary<(JsonPropertyInfo, Type), JsonPropertyInfo>());
168+
#if BUILDING_INBOX_LIBRARY
169+
return cache.GetOrAdd((property, runtimePropertyType), (key, arg) => CreateRuntimeProperty(key, arg), (options, Type));
170+
#else
171+
return cache.GetOrAdd((property, runtimePropertyType), key => CreateRuntimeProperty(key, (options, Type)));
172+
#endif
160173
}
161174
}
162175
}

src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections;
6+
using System.Collections.Concurrent;
67
using System.Collections.Generic;
78
using System.Diagnostics;
89
using System.Reflection;
@@ -26,6 +27,9 @@ internal sealed partial class JsonClassInfo
2627
// All of the serializable properties on a POCO (except the optional extension property) keyed on property name.
2728
public volatile Dictionary<string, JsonPropertyInfo> PropertyCache;
2829

30+
// Serializable runtime/polymorphic properties, keyed on property and runtime type.
31+
public ConcurrentDictionary<(JsonPropertyInfo, Type), JsonPropertyInfo> RuntimePropertyCache;
32+
2933
// All of the serializable properties on a POCO including the optional extension property.
3034
// Used for performance during serialization instead of 'PropertyCache' above.
3135
public volatile JsonPropertyInfo[] PropertyCacheArray;

src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ private static void HandleStartArray(JsonSerializerOptions options, ref ReadStac
2929
}
3030
else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown)
3131
{
32-
jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options);
32+
jsonPropertyInfo = state.Current.JsonClassInfo.GetOrAddPolymorphicProperty(jsonPropertyInfo, typeof(object), options);
3333
}
3434

3535
// Verify that we have a valid enumerable.

src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleValue.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ private static void HandleValue(JsonTokenType tokenType, JsonSerializerOptions o
2424
}
2525
else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown)
2626
{
27-
jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options);
27+
jsonPropertyInfo = state.Current.JsonClassInfo.GetOrAddPolymorphicProperty(jsonPropertyInfo, typeof(object), options);
2828
}
2929

3030
jsonPropertyInfo.Read(tokenType, ref state, ref reader);

src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ private static void GetRuntimePropertyInfo(object value, JsonClassInfo jsonClass
3131
// Nothing to do for typeof(object)
3232
if (runtimeType != typeof(object))
3333
{
34-
jsonPropertyInfo = jsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, runtimeType, options);
34+
jsonPropertyInfo = jsonClassInfo.GetOrAddPolymorphicProperty(jsonPropertyInfo, runtimeType, options);
3535
}
3636
}
3737
}

src/System.Text.Json/tests/Serialization/Object.WriteTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,30 @@ public int this[int index]
7575

7676
public string NonIndexerProp { get; set; }
7777
}
78+
79+
[Fact]
80+
public static void WritePolymorhicSimple()
81+
{
82+
string json = JsonSerializer.Serialize(new { Prop = (object)new[] { 0 } });
83+
Assert.Equal(@"{""Prop"":[0]}", json);
84+
}
85+
86+
[Fact]
87+
public static void WritePolymorphicDifferentAttributes()
88+
{
89+
string json = JsonSerializer.Serialize(new Polymorphic());
90+
Assert.Equal(@"{""P1"":"""",""p_3"":""""}", json);
91+
}
92+
93+
private class Polymorphic
94+
{
95+
public object P1 => "";
96+
97+
[JsonIgnore]
98+
public object P2 => "";
99+
100+
[JsonPropertyName("p_3")]
101+
public object P3 => "";
102+
}
78103
}
79104
}

0 commit comments

Comments
 (0)