Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Cache polymorphic properties (#41753)
Browse files Browse the repository at this point in the history
* Cache polymorphic properties

* Move RuntimePropertyCache to JsonClassInfo

* Added test of RuntimePropertyCache using properties with different attributes

* Fixed typo

Co-Authored-By: Ahson Khan <ahkha@microsoft.com>

* Use allocating overload of GetOrAdd on .Net Standard 2.0
  • Loading branch information
svick authored and ahsonkhan committed Oct 21, 2019
1 parent 90706ad commit 4629961
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -130,33 +132,44 @@ internal JsonPropertyInfo CreateRootObject(JsonSerializerOptions options)
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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,6 +28,9 @@ internal sealed partial class JsonClassInfo
// All of the serializable properties on a POCO (except the optional extension property) keyed on property name.
public volatile Dictionary<string, JsonPropertyInfo> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private static void HandleStartArray(JsonSerializerOptions options, ref ReadStac
}
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ private static void HandleValue(JsonTokenType tokenType, JsonSerializerOptions o
}
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private static void GetRuntimePropertyInfo(object value, JsonClassInfo jsonClass
// Nothing to do for typeof(object)
if (runtimeType != typeof(object))
{
jsonPropertyInfo = jsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, runtimeType, options);
jsonPropertyInfo = jsonClassInfo.GetOrAddPolymorphicProperty(jsonPropertyInfo, runtimeType, options);
}
}
}
Expand Down
25 changes: 25 additions & 0 deletions src/System.Text.Json/tests/Serialization/Object.WriteTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,30 @@ private class Indexer

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 => "";
}
}
}

0 comments on commit 4629961

Please sign in to comment.