From deffe412c9de4f97769923ea98f347ffed88d4b1 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Tue, 14 May 2019 18:10:51 -0700 Subject: [PATCH 01/13] Add support for immutable dictionaries --- .../Json/Serialization/ClassMaterializer.cs | 20 +- .../Converters/DefaultImmutableConverter.cs | 83 ++++- .../JsonClassInfo.AddProperty.cs | 6 +- .../Text/Json/Serialization/JsonClassInfo.cs | 23 +- .../Json/Serialization/JsonPropertyInfo.cs | 10 + .../Serialization/JsonPropertyInfoCommon.cs | 26 +- .../JsonSerializer.Read.HandleArray.cs | 29 +- .../JsonSerializer.Read.HandleObject.cs | 29 +- .../Json/Serialization/JsonSerializer.Read.cs | 2 +- .../JsonSerializer.Write.HandleDictionary.cs | 8 +- .../JsonSerializer.Write.HandleObject.cs | 3 + .../Text/Json/Serialization/ReadStackFrame.cs | 8 + .../ReflectionEmitMaterializer.cs | 20 +- .../Serialization/ReflectionMaterializer.cs | 20 +- .../Text/Json/Serialization/WriteStack.cs | 3 + .../Json/Serialization/WriteStackFrame.cs | 8 + .../tests/Serialization/DictionaryTests.cs | 303 ++++++++++++++---- .../tests/Serialization/Object.ReadTests.cs | 7 + .../TestClasses.SimpleTestClass.cs | 16 +- .../TestClasses.SimpleTestClassWithObject.cs | 16 +- .../tests/Serialization/TestClasses.cs | 138 ++++++++ .../tests/Serialization/TestData.cs | 2 + 22 files changed, 667 insertions(+), 113 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs index 5f786400051e..6841b2d089ce 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs @@ -14,25 +14,29 @@ internal abstract class ClassMaterializer { public abstract JsonClassInfo.ConstructorDelegate CreateConstructor(Type classType); - public abstract object ImmutableCreateRange(Type constructingType, Type elementType); + public abstract object ImmutableCreateRange(Type constructingType, Type elementType, bool constructingTypeIsDict); - protected MethodInfo ImmutableCreateRangeMethod(Type constructingType, Type elementType) + protected MethodInfo ImmutableCreateRangeMethod(Type constructingType, Type elementType, bool constructingTypeIsDict) { MethodInfo[] constructingTypeMethods = constructingType.GetMethods(); - MethodInfo createRange = null; foreach (MethodInfo method in constructingTypeMethods) { if (method.Name == "CreateRange" && method.GetParameters().Length == 1) { - createRange = method; - break; + if (constructingTypeIsDict) + { + return method.MakeGenericMethod(typeof(string), elementType); + } + else + { + return method.MakeGenericMethod(elementType); + } } } - Debug.Assert(createRange != null); - - return createRange.MakeGenericMethod(elementType); + Debug.Fail("Could not create the appropriate CreateRange method."); + return null; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs index 00b77ba795b2..3b4e5b084fd9 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs @@ -34,9 +34,17 @@ internal sealed class DefaultImmutableConverter : JsonEnumerableConverter private const string ImmutableHashSetGenericTypeName = "System.Collections.Immutable.ImmutableHashSet`1"; private const string ImmutableSetGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableSet`1"; + private const string ImmutableDictionaryTypeName = "System.Collections.Immutable.ImmutableDictionary"; + private const string ImmutableDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableDictionary`2"; + private const string ImmutableDictionaryGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableDictionary`2"; + + private const string ImmutableSortedDictionaryTypeName = "System.Collections.Immutable.ImmutableSortedDictionary"; + private const string ImmutableSortedDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableSortedDictionary`2"; + internal delegate object ImmutableCreateRangeDelegate(IEnumerable items); + internal delegate object ImmutableDictCreateRangeDelegate(IEnumerable> items); - public static ConcurrentDictionary CreateRangeDelegates = new ConcurrentDictionary(); + public static ConcurrentDictionary s_createRangeDelegates = new ConcurrentDictionary(); private string GetConstructingTypeName(string immutableCollectionTypeName) { @@ -47,21 +55,44 @@ private string GetConstructingTypeName(string immutableCollectionTypeName) return ImmutableListTypeName; case ImmutableStackGenericTypeName: case ImmutableStackGenericInterfaceTypeName: - return ImmutableStackTypeName; + return ImmutableStackTypeName; case ImmutableQueueGenericTypeName: case ImmutableQueueGenericInterfaceTypeName: - return ImmutableQueueTypeName; + return ImmutableQueueTypeName; case ImmutableSortedSetGenericTypeName: - return ImmutableSortedSetTypeName; + return ImmutableSortedSetTypeName; case ImmutableHashSetGenericTypeName: case ImmutableSetGenericInterfaceTypeName: - return ImmutableHashSetTypeName; + return ImmutableHashSetTypeName; + case ImmutableDictionaryGenericTypeName: + case ImmutableDictionaryGenericInterfaceTypeName: + return ImmutableDictionaryTypeName; + case ImmutableSortedDictionaryGenericTypeName: + return ImmutableSortedDictionaryTypeName; default: // TODO: Refactor exception throw following serialization exception changes. throw new NotSupportedException(SR.Format(SR.DeserializeTypeNotSupported, immutableCollectionTypeName)); } } + internal static bool TypeIsImmutableDictionary(Type type) + { + if (!type.IsGenericType) + { + return false; + } + + switch (type.GetGenericTypeDefinition().FullName) + { + case ImmutableDictionaryGenericTypeName: + case ImmutableDictionaryGenericInterfaceTypeName: + case ImmutableSortedDictionaryGenericTypeName: + return true; + default: + return false; + } + } + private string GetDelegateKey( Type immutableCollectionType, Type elementType, @@ -82,7 +113,7 @@ public void RegisterImmutableCollectionType(Type immutableCollectionType, Type e string delegateKey = GetDelegateKey(immutableCollectionType, elementType, out Type underlyingType, out string constructingTypeName); // Exit if we have registered this immutable collection type. - if (CreateRangeDelegates.ContainsKey(delegateKey)) + if (s_createRangeDelegates.ContainsKey(delegateKey)) { return; } @@ -91,11 +122,10 @@ public void RegisterImmutableCollectionType(Type immutableCollectionType, Type e Type constructingType = underlyingType.Assembly.GetType(constructingTypeName); // Create a delegate which will point to the CreateRange method. - object createRangeDelegate = options.ClassMaterializerStrategy.ImmutableCreateRange(constructingType, elementType); - Debug.Assert(createRangeDelegate != null); + object createRangeDelegate = options.ClassMaterializerStrategy.ImmutableCreateRange(constructingType, elementType, TypeIsImmutableDictionary(immutableCollectionType)); // Cache the delegate - CreateRangeDelegates.TryAdd(delegateKey, createRangeDelegate); + s_createRangeDelegates.TryAdd(delegateKey, createRangeDelegate); } public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options) @@ -104,7 +134,7 @@ public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList Type elementType = state.Current.GetElementType(); string delegateKey = GetDelegateKey(immutableCollectionType, elementType, out _, out _); - Debug.Assert(CreateRangeDelegates.ContainsKey(delegateKey)); + Debug.Assert(s_createRangeDelegates.ContainsKey(delegateKey)); JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; @@ -130,5 +160,38 @@ public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList return propertyInfo.CreateImmutableCollectionFromList(delegateKey, sourceList); } + + public IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options) + { + Type immutableCollectionType = state.Current.JsonPropertyInfo.RuntimePropertyType; + Type elementType = state.Current.GetElementType(); + + string delegateKey = GetDelegateKey(immutableCollectionType, elementType, out _, out _); + Debug.Assert(s_createRangeDelegates.ContainsKey(delegateKey)); + + JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; + + JsonPropertyInfo propertyInfo; + if (elementClassInfo.ClassType == ClassType.Object) + { + Type objectType = elementClassInfo.Type; + + if (DefaultIEnumerableConstructibleConverter.s_objectJsonProperties.ContainsKey(objectType)) + { + propertyInfo = DefaultIEnumerableConstructibleConverter.s_objectJsonProperties[objectType]; + } + else + { + propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, null, typeof(object), options); + DefaultIEnumerableConstructibleConverter.s_objectJsonProperties[objectType] = propertyInfo; + } + } + else + { + propertyInfo = elementClassInfo.GetPolicyProperty(); + } + + return propertyInfo.CreateImmutableCollectionFromDictionary(delegateKey, sourceDictionary); + } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs index 05b9ca1df630..3592e9e314e1 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs @@ -2,8 +2,8 @@ // 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.Diagnostics; using System.Reflection; +using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization { @@ -25,8 +25,8 @@ private JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInf { JsonPropertyInfo jsonInfo = CreateProperty(propertyType, propertyType, propertyInfo, classType, options); - // Convert interfaces to concrete types. - if (propertyType.IsInterface && jsonInfo.ClassType == ClassType.Dictionary) + // Convert non-immutable interfaces to concrete types. + if (propertyType.IsInterface && jsonInfo.ClassType == ClassType.Dictionary && !DefaultImmutableConverter.TypeIsImmutableDictionary(propertyType)) { // If a polymorphic case, we have to wait until run-time values are processed. if (jsonInfo.ElementClassInfo.ClassType != ClassType.Unknown) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs index 769e5e220d0d..75e36a43513b 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; +using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization { @@ -118,11 +119,22 @@ internal JsonClassInfo(Type type, JsonSerializerOptions options) // Add a single property that maps to the class type so we can have policies applied. JsonPropertyInfo policyProperty = AddPolicyProperty(type, options); - // Use the type from the property policy to get any late-bound concrete types (from an interface like IDictionary). - CreateObject = options.ClassMaterializerStrategy.CreateConstructor(policyProperty.RuntimePropertyType); + Type elementType = GetElementType(type); + + if (!DefaultImmutableConverter.TypeIsImmutableDictionary(type)) + { + // Use the type from the property policy to get any late-bound concrete types (from an interface like IDictionary). + CreateObject = options.ClassMaterializerStrategy.CreateConstructor(policyProperty.RuntimePropertyType); + } + else + { + // Since immutable dictionaries do not implement Dictionary<,>, the runtime property type could not be set to + // Dictionary, we have to set CreateObject this way. + CreateObject = options.ClassMaterializerStrategy.CreateConstructor( + typeof(Dictionary<,>).MakeGenericType(typeof(string), elementType)); + } // Create a ClassInfo that maps to the element type which is used for (de)serialization and policies. - Type elementType = GetElementType(type); ElementClassInfo = options.GetOrAddClass(elementType); break; case ClassType.Value: @@ -356,8 +368,9 @@ internal static ClassType GetClassType(Type type) } if (typeof(IDictionary).IsAssignableFrom(type) || - (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(IDictionary<,>) - || type.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>)))) + (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(IDictionary<,>) || + type.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>))) || + DefaultImmutableConverter.TypeIsImmutableDictionary(type)) { return ClassType.Dictionary; } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs index 8c429bc4d722..cc4e214b6aa6 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs @@ -189,6 +189,14 @@ private void DetermineSerializationCapabilities(JsonSerializerOptions options) // We deserialize if there is a setter + no [Ignore] attribute. ShouldDeserialize = HasSetter; + + if (ClassType == ClassType.Dictionary && + ShouldDeserialize && DefaultImmutableConverter.TypeIsImmutableDictionary(RuntimePropertyType)) + { + EnumerableConverter = s_jsonImmutableConverter; + ((DefaultImmutableConverter)EnumerableConverter).RegisterImmutableCollectionType( + RuntimePropertyType, JsonClassInfo.GetElementType(RuntimePropertyType), options); + } } else { @@ -274,6 +282,8 @@ public TAttribute GetAttribute() where TAttribute : Attribute public abstract IEnumerable CreateImmutableCollectionFromList(string delegateKey, IList sourceList); + public abstract IDictionary CreateImmutableCollectionFromDictionary(string delegateKey, IDictionary sourceDictionary); + public abstract IEnumerable CreateIEnumerableConstructibleType(Type enumerableType, IList sourceList); public abstract IList CreateConverterList(); diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs index 708f2d9545c8..bbd4d4496353 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs @@ -102,14 +102,27 @@ public override Type GetDictionaryConcreteType() // CreateRange method to create and return the desired immutable collection type. public override IEnumerable CreateImmutableCollectionFromList(string delegateKey, IList sourceList) { - Debug.Assert(DefaultImmutableConverter.CreateRangeDelegates.ContainsKey(delegateKey)); + Debug.Assert(DefaultImmutableConverter.s_createRangeDelegates.ContainsKey(delegateKey)); DefaultImmutableConverter.ImmutableCreateRangeDelegate createRangeDelegate = ( - (DefaultImmutableConverter.ImmutableCreateRangeDelegate)DefaultImmutableConverter.CreateRangeDelegates[delegateKey]); + (DefaultImmutableConverter.ImmutableCreateRangeDelegate)DefaultImmutableConverter.s_createRangeDelegates[delegateKey]); return (IEnumerable)createRangeDelegate.Invoke(CreateGenericIEnumerableFromList(sourceList)); } + // Creates an IEnumerable and populates it with the items in the, + // sourceList argument then uses the delegateKey argument to identify the appropriate cached + // CreateRange method to create and return the desired immutable collection type. + public override IDictionary CreateImmutableCollectionFromDictionary(string delegateKey, IDictionary sourceDictionary) + { + Debug.Assert(DefaultImmutableConverter.s_createRangeDelegates.ContainsKey(delegateKey)); + + DefaultImmutableConverter.ImmutableDictCreateRangeDelegate createRangeDelegate = ( + (DefaultImmutableConverter.ImmutableDictCreateRangeDelegate)DefaultImmutableConverter.s_createRangeDelegates[delegateKey]); + + return (IDictionary)createRangeDelegate.Invoke(CreateGenericIEnumerableFromDictionary(sourceDictionary)); + } + public override IEnumerable CreateIEnumerableConstructibleType(Type enumerableType, IList sourceList) { return (IEnumerable)Activator.CreateInstance(enumerableType, CreateGenericIEnumerableFromList(sourceList)); @@ -122,5 +135,14 @@ private IEnumerable CreateGenericIEnumerableFromList(IList sou yield return (TRuntimeProperty)item; } } + + private IEnumerable> CreateGenericIEnumerableFromDictionary(IDictionary sourceDictionary) + { + foreach (object item in sourceDictionary) + { + DictionaryEntry itemDictEntry = (DictionaryEntry)item; + yield return new KeyValuePair((string)itemDictEntry.Key, (TRuntimeProperty)itemDictEntry.Value); + } + } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs index b36dbdf366c7..14492e5f1c38 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs @@ -191,8 +191,19 @@ internal static void ApplyObjectToEnumerable( } else if (state.Current.IsDictionary) { - Debug.Assert(state.Current.ReturnValue != null); - IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue); + object dictionaryObject = null; + if (state.Current.ReturnValue != null) + { + dictionaryObject = state.Current.ReturnValue; + } + else if (state.Current.TempDictionaryValues != null) + { + dictionaryObject = state.Current.TempDictionaryValues; + } + Debug.Assert(dictionaryObject != null); + + IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(dictionaryObject); + string key = state.Current.KeyName; Debug.Assert(!string.IsNullOrEmpty(key)); if (!dictionary.Contains(key)) @@ -246,8 +257,18 @@ internal static void ApplyValueToEnumerable( } else if (state.Current.IsDictionary) { - Debug.Assert(state.Current.ReturnValue != null); - IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue); + object dictionaryObject = null; + if (state.Current.ReturnValue != null) + { + dictionaryObject = state.Current.ReturnValue; + } + else if (state.Current.TempDictionaryValues != null) + { + dictionaryObject = state.Current.TempDictionaryValues; + } + Debug.Assert(dictionaryObject != null); + + IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(dictionaryObject); string key = state.Current.KeyName; Debug.Assert(!string.IsNullOrEmpty(key)); diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs index 425c4d78f65d..4a4896c4112a 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs @@ -2,7 +2,9 @@ // 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; using System.Diagnostics; +using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization { @@ -34,7 +36,7 @@ private static void HandleStartObject(JsonSerializerOptions options, ref Utf8Jso ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type, reader, state.PropertyPath); } - if (state.Current.ReturnValue == null) + if (state.Current.ReturnValue == null && state.Current.TempDictionaryValues == null) { // The Dictionary created below will be returned to corresponding Parse() etc method. // Ensure any nested array creates a new frame. @@ -67,7 +69,15 @@ private static void HandleStartObject(JsonSerializerOptions options, ref Utf8Jso } JsonClassInfo classInfo = state.Current.JsonClassInfo; - state.Current.ReturnValue = classInfo.CreateObject(); + + if (state.Current.IsProcessingImmutableDictionary && state.Current.IsImmutableDictionary) + { + state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject(); + } + else + { + state.Current.ReturnValue = classInfo.CreateObject(); + } } private static bool HandleEndObject(JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader) @@ -81,7 +91,20 @@ private static bool HandleEndObject(JsonSerializerOptions options, ref ReadStack state.Current.JsonClassInfo.UpdateSortedPropertyCache(ref state.Current); - object value = state.Current.ReturnValue; + object value; + if (state.Current.TempDictionaryValues != null) + { + Debug.Assert(state.Current.IsProcessingImmutableDictionary); + + DefaultImmutableConverter converter = (DefaultImmutableConverter)state.Current.JsonPropertyInfo.EnumerableConverter; + Debug.Assert(converter != null); + + value = converter.CreateFromDictionary(ref state, (IDictionary)state.Current.TempDictionaryValues, options); + } + else + { + value = state.Current.ReturnValue; + } if (isLastFrame) { diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs index 69cd87d3cf4f..cc0189b12ac5 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs @@ -40,7 +40,7 @@ private static void ReadCore( { if (!state.Current.Drain) { - Debug.Assert(state.Current.ReturnValue != default); + Debug.Assert(state.Current.ReturnValue != default || state.Current.TempDictionaryValues != default); Debug.Assert(state.Current.JsonClassInfo != default); if (state.Current.IsDictionary) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs index b816cae4fb6e..a5a7d86c4027 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs @@ -2,7 +2,6 @@ // 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.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -42,7 +41,7 @@ private static bool HandleDictionary( return true; } - state.Current.Enumerator = enumerable.GetEnumerator(); + state.Current.Enumerator = ((IDictionary)enumerable).GetEnumerator(); state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer); } @@ -111,6 +110,11 @@ internal static void WriteDictionary( value = enumerator.Current.Value; key = enumerator.Current.Key; } + else if (current.IsImmutableDictionary || current.IsPropertyAnImmutableDictionary) + { + value = (TProperty)((DictionaryEntry)current.Enumerator.Current).Value; + key = (string)((DictionaryEntry)current.Enumerator.Current).Key; + } else { // Todo: support non-generic Dictionary here (IDictionaryEnumerator) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs index 1daa62da2db0..f235c3813217 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization { @@ -90,6 +91,8 @@ private static bool HandleObject( // A property that returns a dictionary keeps the same stack frame. if (jsonPropertyInfo.ClassType == ClassType.Dictionary) { + state.Current.IsPropertyAnImmutableDictionary = DefaultImmutableConverter.TypeIsImmutableDictionary(jsonPropertyInfo.RuntimePropertyType); + bool endOfEnumerable = HandleDictionary(jsonPropertyInfo.ElementClassInfo, options, writer, ref state); if (endOfEnumerable) { diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs index 5c3ab721c1a9..e916510e032c 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization { @@ -29,6 +30,9 @@ internal struct ReadStackFrame public IList TempEnumerableValues; public bool EnumerableCreated; + // Support Immutable dictionary types. + public IDictionary TempDictionaryValues; + // For performance, we order the properties by the first deserialize and PropertyIndex helps find the right slot quicker. public int PropertyIndex; public List PropertyRefCache; @@ -36,8 +40,10 @@ internal struct ReadStackFrame // The current JSON data for a property does not match a given POCO, so ignore the property (recursively). public bool Drain; + public bool IsImmutableDictionary => DefaultImmutableConverter.TypeIsImmutableDictionary(JsonClassInfo.Type); public bool IsDictionary => JsonClassInfo.ClassType == ClassType.Dictionary; public bool IsEnumerable => JsonClassInfo.ClassType == ClassType.Enumerable; + public bool IsProcessingImmutableDictionary; public bool IsProcessingEnumerableOrDictionary => IsProcessingEnumerable || IsDictionary; public bool IsProcessingEnumerable => IsEnumerable || IsPropertyEnumerable; public bool IsPropertyEnumerable => JsonPropertyInfo != null ? JsonPropertyInfo.ClassType == ClassType.Enumerable : false; @@ -68,6 +74,7 @@ public void InitializeJsonPropertyInfo() if (JsonClassInfo.ClassType == ClassType.Value || JsonClassInfo.ClassType == ClassType.Enumerable || JsonClassInfo.ClassType == ClassType.Dictionary) { JsonPropertyInfo = JsonClassInfo.GetPolicyProperty(); + IsProcessingImmutableDictionary = DefaultImmutableConverter.TypeIsImmutableDictionary(JsonClassInfo.Type); } } @@ -87,6 +94,7 @@ public void ResetProperty() JsonPropertyInfo = null; PopStackOnEnd = false; TempEnumerableValues = null; + TempDictionaryValues = null; } public void EndObject() diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs index be5dec15cd46..5dfc6c0292df 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs @@ -36,12 +36,26 @@ public override JsonClassInfo.ConstructorDelegate CreateConstructor(Type type) return (JsonClassInfo.ConstructorDelegate)dynamicMethod.CreateDelegate(typeof(JsonClassInfo.ConstructorDelegate)); } - public override object ImmutableCreateRange(Type constructingType, Type elementType) + public override object ImmutableCreateRange(Type constructingType, Type elementType, bool constructingTypeIsDict) { - MethodInfo createRange = ImmutableCreateRangeMethod(constructingType, elementType); + MethodInfo createRange = ImmutableCreateRangeMethod(constructingType, elementType, constructingTypeIsDict); - return createRange.CreateDelegate( + Debug.Assert(createRange != null); + + object createRangeDelegate; + if (constructingTypeIsDict) + { + createRangeDelegate = createRange.CreateDelegate( + typeof(DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null); + } + else + { + createRangeDelegate = createRange.CreateDelegate( typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null); + } + + Debug.Assert(createRangeDelegate != null); + return createRangeDelegate; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs index 5c5c43173af0..1505a359663d 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs @@ -15,12 +15,26 @@ public override JsonClassInfo.ConstructorDelegate CreateConstructor(Type type) return () => Activator.CreateInstance(type); } - public override object ImmutableCreateRange(Type constructingType, Type elementType) + public override object ImmutableCreateRange(Type constructingType, Type elementType, bool constructingTypeIsDict) { - MethodInfo createRange = ImmutableCreateRangeMethod(constructingType, elementType); + MethodInfo createRange = ImmutableCreateRangeMethod(constructingType, elementType, constructingTypeIsDict); - return createRange.CreateDelegate( + Debug.Assert(createRange != null); + + object createRangeDelegate; + if (constructingTypeIsDict) + { + createRangeDelegate = createRange.CreateDelegate( + typeof(DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null); + } + else + { + createRangeDelegate = createRange.CreateDelegate( typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null); + } + + Debug.Assert(createRangeDelegate != null); + return createRangeDelegate; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs index 9f29f9700e59..7680d5084035 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization { @@ -50,6 +51,8 @@ public void Push(JsonClassInfo nextClassInfo, object nextValue) { Current.PopStackOnEnd = true; Current.JsonPropertyInfo = Current.JsonClassInfo.GetPolicyProperty(); + + Current.IsImmutableDictionary = DefaultImmutableConverter.TypeIsImmutableDictionary(nextClassInfo.Type); } else { diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs index 82f823b7b47e..5b6d6d614325 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Collections; using System.Diagnostics; +using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization { @@ -17,6 +18,10 @@ internal struct WriteStackFrame // Support Dictionary keys. public string KeyName; + // Whether the current object is an immutable dictionary. + public bool IsImmutableDictionary; + public bool IsPropertyAnImmutableDictionary; + // The current enumerator for the IEnumerable or IDictionary. public IEnumerator Enumerator; @@ -41,6 +46,7 @@ public void Initialize(Type type, JsonSerializerOptions options) if (JsonClassInfo.ClassType == ClassType.Value || JsonClassInfo.ClassType == ClassType.Enumerable || JsonClassInfo.ClassType == ClassType.Dictionary) { JsonPropertyInfo = JsonClassInfo.GetPolicyProperty(); + IsImmutableDictionary = DefaultImmutableConverter.TypeIsImmutableDictionary(type); } } @@ -114,6 +120,7 @@ public void Reset() JsonClassInfo = null; JsonPropertyInfo = null; PropertyIndex = 0; + IsImmutableDictionary = false; PopStackOnEndObject = false; PopStackOnEnd = false; StartObjectWritten = false; @@ -123,6 +130,7 @@ public void EndObject() { PropertyIndex = 0; PopStackOnEndObject = false; + IsPropertyAnImmutableDictionary = false; JsonPropertyInfo = null; } diff --git a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs index b0386a2c3911..883693b9680a 100644 --- a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs +++ b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs @@ -4,6 +4,8 @@ using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -14,6 +16,7 @@ public static partial class DictionaryTests public static void DictionaryOfString() { const string JsonString = @"{""Hello"":""World"",""Hello2"":""World2""}"; + const string ReorderedJsonString = @"{""Hello2"":""World2"",""Hello"":""World""}"; { Dictionary obj = JsonSerializer.Parse>(JsonString); @@ -50,6 +53,42 @@ public static void DictionaryOfString() json = JsonSerializer.ToString(obj); Assert.Equal(JsonString, json); } + + { + ImmutableDictionary obj = JsonSerializer.Parse>(JsonString); + Assert.Equal("World", obj["Hello"]); + Assert.Equal("World2", obj["Hello2"]); + + string json = JsonSerializer.ToString(obj); + Assert.True(JsonString == json || ReorderedJsonString == json); + + json = JsonSerializer.ToString(obj); + Assert.True(JsonString == json || ReorderedJsonString == json); + } + + { + IImmutableDictionary obj = JsonSerializer.Parse>(JsonString); + Assert.Equal("World", obj["Hello"]); + Assert.Equal("World2", obj["Hello2"]); + + string json = JsonSerializer.ToString(obj); + Assert.True(JsonString == json || ReorderedJsonString == json); + + json = JsonSerializer.ToString(obj); + Assert.True(JsonString == json || ReorderedJsonString == json); + } + + { + ImmutableSortedDictionary obj = JsonSerializer.Parse>(JsonString); + Assert.Equal("World", obj["Hello"]); + Assert.Equal("World2", obj["Hello2"]); + + string json = JsonSerializer.ToString(obj); + Assert.True(JsonString == json); + + json = JsonSerializer.ToString(obj); + Assert.True(JsonString == json); + } } [Fact] @@ -81,19 +120,55 @@ public static void DictionaryOfList() { const string JsonString = @"{""Key1"":[1,2],""Key2"":[3,4]}"; - IDictionary> obj = JsonSerializer.Parse>>(JsonString); + { + IDictionary> obj = JsonSerializer.Parse>>(JsonString); - Assert.Equal(2, obj.Count); - Assert.Equal(2, obj["Key1"].Count); - Assert.Equal(1, obj["Key1"][0]); - Assert.Equal(2, obj["Key1"][1]); - Assert.Equal(2, obj["Key2"].Count); - Assert.Equal(3, obj["Key2"][0]); - Assert.Equal(4, obj["Key2"][1]); + Assert.Equal(2, obj.Count); + Assert.Equal(2, obj["Key1"].Count); + Assert.Equal(1, obj["Key1"][0]); + Assert.Equal(2, obj["Key1"][1]); + Assert.Equal(2, obj["Key2"].Count); + Assert.Equal(3, obj["Key2"][0]); + Assert.Equal(4, obj["Key2"][1]); - string json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + } + + { + ImmutableDictionary> obj = JsonSerializer.Parse>>(JsonString); + + Assert.Equal(2, obj.Count); + Assert.Equal(2, obj["Key1"].Count); + Assert.Equal(1, obj["Key1"][0]); + Assert.Equal(2, obj["Key1"][1]); + Assert.Equal(2, obj["Key2"].Count); + Assert.Equal(3, obj["Key2"][0]); + Assert.Equal(4, obj["Key2"][1]); + + + string json = JsonSerializer.ToString(obj); + const string ReorderedJsonString = @"{""Key2"":[3,4],""Key1"":[1,2]}"; + Assert.True(JsonString == json || ReorderedJsonString == json); + } + + { + IImmutableDictionary> obj = JsonSerializer.Parse>>(JsonString); + + Assert.Equal(2, obj.Count); + Assert.Equal(2, obj["Key1"].Count); + Assert.Equal(1, obj["Key1"][0]); + Assert.Equal(2, obj["Key1"][1]); + Assert.Equal(2, obj["Key2"].Count); + Assert.Equal(3, obj["Key2"][0]); + Assert.Equal(4, obj["Key2"][1]); + + + string json = JsonSerializer.ToString(obj); + const string ReorderedJsonString = @"{""Key2"":[3,4],""Key1"":[1,2]}"; + Assert.True(JsonString == json || ReorderedJsonString == json); + } } [Fact] @@ -118,94 +193,188 @@ public static void DictionaryOfArray() public static void ListOfDictionary() { const string JsonString = @"[{""Key1"":1,""Key2"":2},{""Key1"":3,""Key2"":4}]"; - List> obj = JsonSerializer.Parse>>(JsonString); - Assert.Equal(2, obj.Count); - Assert.Equal(2, obj[0].Count); - Assert.Equal(1, obj[0]["Key1"]); - Assert.Equal(2, obj[0]["Key2"]); - Assert.Equal(2, obj[1].Count); - Assert.Equal(3, obj[1]["Key1"]); - Assert.Equal(4, obj[1]["Key2"]); + { + List> obj = JsonSerializer.Parse>>(JsonString); - string json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); + Assert.Equal(2, obj.Count); + Assert.Equal(2, obj[0].Count); + Assert.Equal(1, obj[0]["Key1"]); + Assert.Equal(2, obj[0]["Key2"]); + Assert.Equal(2, obj[1].Count); + Assert.Equal(3, obj[1]["Key1"]); + Assert.Equal(4, obj[1]["Key2"]); - json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + + json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + } + { + List> obj = JsonSerializer.Parse>>(JsonString); + + Assert.Equal(2, obj.Count); + Assert.Equal(2, obj[0].Count); + Assert.Equal(1, obj[0]["Key1"]); + Assert.Equal(2, obj[0]["Key2"]); + Assert.Equal(2, obj[1].Count); + Assert.Equal(3, obj[1]["Key1"]); + Assert.Equal(4, obj[1]["Key2"]); + + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + + json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + } } [Fact] public static void ArrayOfDictionary() { const string JsonString = @"[{""Key1"":1,""Key2"":2},{""Key1"":3,""Key2"":4}]"; - Dictionary[] obj = JsonSerializer.Parse[]>(JsonString); - Assert.Equal(2, obj.Length); - Assert.Equal(2, obj[0].Count); - Assert.Equal(1, obj[0]["Key1"]); - Assert.Equal(2, obj[0]["Key2"]); - Assert.Equal(2, obj[1].Count); - Assert.Equal(3, obj[1]["Key1"]); - Assert.Equal(4, obj[1]["Key2"]); + { + Dictionary[] obj = JsonSerializer.Parse[]>(JsonString); - string json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); + Assert.Equal(2, obj.Length); + Assert.Equal(2, obj[0].Count); + Assert.Equal(1, obj[0]["Key1"]); + Assert.Equal(2, obj[0]["Key2"]); + Assert.Equal(2, obj[1].Count); + Assert.Equal(3, obj[1]["Key1"]); + Assert.Equal(4, obj[1]["Key2"]); - json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + + json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + } + + { + ImmutableSortedDictionary[] obj = JsonSerializer.Parse[]>(JsonString); + + Assert.Equal(2, obj.Length); + Assert.Equal(2, obj[0].Count); + Assert.Equal(1, obj[0]["Key1"]); + Assert.Equal(2, obj[0]["Key2"]); + Assert.Equal(2, obj[1].Count); + Assert.Equal(3, obj[1]["Key1"]); + Assert.Equal(4, obj[1]["Key2"]); + + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + + json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + } } [Fact] public static void DictionaryOfDictionary() { const string JsonString = @"{""Key1"":{""Key1a"":1,""Key1b"":2},""Key2"":{""Key2a"":3,""Key2b"":4}}"; - Dictionary> obj = JsonSerializer.Parse>>(JsonString); - Assert.Equal(2, obj.Count); - Assert.Equal(2, obj["Key1"].Count); - Assert.Equal(1, obj["Key1"]["Key1a"]); - Assert.Equal(2, obj["Key1"]["Key1b"]); - Assert.Equal(2, obj["Key2"].Count); - Assert.Equal(3, obj["Key2"]["Key2a"]); - Assert.Equal(4, obj["Key2"]["Key2b"]); + { + Dictionary> obj = JsonSerializer.Parse>>(JsonString); - string json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); + Assert.Equal(2, obj.Count); + Assert.Equal(2, obj["Key1"].Count); + Assert.Equal(1, obj["Key1"]["Key1a"]); + Assert.Equal(2, obj["Key1"]["Key1b"]); + Assert.Equal(2, obj["Key2"].Count); + Assert.Equal(3, obj["Key2"]["Key2a"]); + Assert.Equal(4, obj["Key2"]["Key2b"]); - json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); - } + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); - [Fact] - public static void DictionaryOfClasses() - { - Dictionary obj; + json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + } { - string json = @"{""Key1"":" + SimpleTestClass.s_json + @",""Key2"":" + SimpleTestClass.s_json + "}"; - obj = JsonSerializer.Parse>(json); + ImmutableSortedDictionary> obj = JsonSerializer.Parse>>(JsonString); + Assert.Equal(2, obj.Count); - obj["Key1"].Verify(); - obj["Key2"].Verify(); + Assert.Equal(2, obj["Key1"].Count); + Assert.Equal(1, obj["Key1"]["Key1a"]); + Assert.Equal(2, obj["Key1"]["Key1b"]); + Assert.Equal(2, obj["Key2"].Count); + Assert.Equal(3, obj["Key2"]["Key2a"]); + Assert.Equal(4, obj["Key2"]["Key2b"]); + + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + + json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); } + } + [Fact] + public static void DictionaryOfClasses() + { { - // We can't compare against the json string above because property ordering is not deterministic (based on reflection order) - // so just round-trip the json and compare. - string json = JsonSerializer.ToString(obj); - obj = JsonSerializer.Parse>(json); - Assert.Equal(2, obj.Count); - obj["Key1"].Verify(); - obj["Key2"].Verify(); + Dictionary obj; + + { + string json = @"{""Key1"":" + SimpleTestClass.s_json + @",""Key2"":" + SimpleTestClass.s_json + "}"; + obj = JsonSerializer.Parse>(json); + Assert.Equal(2, obj.Count); + obj["Key1"].Verify(); + obj["Key2"].Verify(); + } + + { + // We can't compare against the json string above because property ordering is not deterministic (based on reflection order) + // so just round-trip the json and compare. + string json = JsonSerializer.ToString(obj); + obj = JsonSerializer.Parse>(json); + Assert.Equal(2, obj.Count); + obj["Key1"].Verify(); + obj["Key2"].Verify(); + } + + { + string json = JsonSerializer.ToString(obj); + obj = JsonSerializer.Parse>(json); + Assert.Equal(2, obj.Count); + obj["Key1"].Verify(); + obj["Key2"].Verify(); + } } { - string json = JsonSerializer.ToString(obj); - obj = JsonSerializer.Parse>(json); - Assert.Equal(2, obj.Count); - obj["Key1"].Verify(); - obj["Key2"].Verify(); + ImmutableSortedDictionary obj; + + { + string json = @"{""Key1"":" + SimpleTestClass.s_json + @",""Key2"":" + SimpleTestClass.s_json + "}"; + obj = JsonSerializer.Parse>(json); + Assert.Equal(2, obj.Count); + obj["Key1"].Verify(); + obj["Key2"].Verify(); + } + + { + // We can't compare against the json string above because property ordering is not deterministic (based on reflection order) + // so just round-trip the json and compare. + string json = JsonSerializer.ToString(obj); + obj = JsonSerializer.Parse>(json); + Assert.Equal(2, obj.Count); + obj["Key1"].Verify(); + obj["Key2"].Verify(); + } + + { + string json = JsonSerializer.ToString(obj); + obj = JsonSerializer.Parse>(json); + Assert.Equal(2, obj.Count); + obj["Key1"].Verify(); + obj["Key2"].Verify(); + } } } diff --git a/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs b/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs index bcc76b72396d..872492c27a66 100644 --- a/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs +++ b/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs @@ -103,5 +103,12 @@ public static void ReadClassWithStringToPrimitiveDictionary() TestClassWithStringToPrimitiveDictionary obj = JsonSerializer.Parse(TestClassWithStringToPrimitiveDictionary.s_data); obj.Verify(); } + + [Fact] + public static void ReadClassWithStringToObjectDictionaries() + { + TestClassWithStringToObjectDictionaries obj = JsonSerializer.Parse(TestClassWithStringToObjectDictionaries.s_data); + obj.Verify(); + } } } diff --git a/src/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClass.cs b/src/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClass.cs index f5b090fee2de..0049d06c6398 100644 --- a/src/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClass.cs +++ b/src/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClass.cs @@ -58,6 +58,9 @@ public class SimpleTestClass : ITestClass public Dictionary MyStringToStringDict { get; set; } public IDictionary MyStringToStringIDict { get; set; } public IReadOnlyDictionary MyStringToStringIReadOnlyDict { get; set; } + public ImmutableDictionary MyStringToStringImmutableDict { get; set; } + public IImmutableDictionary MyStringToStringIImmutableDict { get; set; } + public ImmutableSortedDictionary MyStringToStringImmutableSortedDict { get; set; } public Stack MyStringStackT { get; set; } public Queue MyStringQueueT { get; set; } public HashSet MyStringHashSetT { get; set; } @@ -98,7 +101,10 @@ public class SimpleTestClass : ITestClass @"""MyEnum"" : 2," + // int by default @"""MyStringToStringDict"" : {""key"" : ""value""}," + @"""MyStringToStringIDict"" : {""key"" : ""value""}," + - @"""MyStringToStringIReadOnlyDict"" : {""key"" : ""value""}"; + @"""MyStringToStringIReadOnlyDict"" : {""key"" : ""value""}," + + @"""MyStringToStringImmutableDict"" : {""key"" : ""value""}," + + @"""MyStringToStringIImmutableDict"" : {""key"" : ""value""}," + + @"""MyStringToStringImmutableSortedDict"" : {""key"" : ""value""}"; private const string s_partialJsonArrays = @"""MyInt16Array"" : [1]," + @@ -194,6 +200,10 @@ public void Initialize() MyStringToStringIDict = new Dictionary { { "key", "value" } }; MyStringToStringIReadOnlyDict = new Dictionary { { "key", "value" } }; + MyStringToStringImmutableDict = ImmutableDictionary.CreateRange(MyStringToStringDict); + MyStringToStringIImmutableDict = ImmutableDictionary.CreateRange(MyStringToStringDict); + MyStringToStringImmutableSortedDict = ImmutableSortedDictionary.CreateRange(MyStringToStringDict); + MyStringStackT = new Stack(new List() { "Hello", "World" } ); MyStringQueueT = new Queue(new List() { "Hello", "World" }); MyStringHashSetT = new HashSet(new List() { "Hello" }); @@ -264,6 +274,10 @@ public void Verify() Assert.Equal("value", MyStringToStringIDict["key"]); Assert.Equal("value", MyStringToStringIReadOnlyDict["key"]); + Assert.Equal("value", MyStringToStringImmutableDict["key"]); + Assert.Equal("value", MyStringToStringIImmutableDict["key"]); + Assert.Equal("value", MyStringToStringImmutableSortedDict["key"]); + Assert.Equal(2, MyStringStackT.Count); Assert.True(MyStringStackT.Contains("Hello")); Assert.True(MyStringStackT.Contains("World")); diff --git a/src/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClassWithObject.cs b/src/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClassWithObject.cs index 6ae05115e843..1417945e5de5 100644 --- a/src/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClassWithObject.cs +++ b/src/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClassWithObject.cs @@ -37,6 +37,9 @@ public class SimpleTestClassWithObject : SimpleTestClassWithSimpleObject public object MyStringToStringDict { get; set; } public object MyStringToStringIDict { get; set; } public object MyStringToStringIReadOnlyDict { get; set; } + public object MyStringToStringImmutableDict { get; set; } + public object MyStringToStringIImmutableDict { get; set; } + public object MyStringToStringImmutableSortedDict { get; set; } public object MyStringStackT { get; set; } public object MyStringQueueT { get; set; } public object MyStringHashSetT { get; set; } @@ -96,7 +99,10 @@ public class SimpleTestClassWithObject : SimpleTestClassWithSimpleObject @"""MyStringIReadOnlyListT"" : [""Hello""]," + @"""MyStringToStringDict"" : {""key"" : ""value""}," + @"""MyStringToStringIDict"" : {""key"" : ""value""}," + - @"""MyStringToStringIReadOnlyDict"" : {""key"" : ""value""}" + + @"""MyStringToStringIReadOnlyDict"" : {""key"" : ""value""}," + + @"""MyStringToStringImmutableDict"" : {""key"" : ""value""}," + + @"""MyStringToStringIImmutableDict"" : {""key"" : ""value""}," + + @"""MyStringToStringImmutableSortedDict"" : {""key"" : ""value""}," + @"""MyStringStackT"" : [""Hello"", ""World""]," + @"""MyStringQueueT"" : [""Hello"", ""World""]," + @"""MyStringHashSetT"" : [""Hello""]," + @@ -148,6 +154,10 @@ public override void Initialize() MyStringToStringIDict = new Dictionary { { "key", "value" } }; MyStringToStringIReadOnlyDict = new Dictionary { { "key", "value" } }; + MyStringToStringImmutableDict = ImmutableDictionary.CreateRange((Dictionary)MyStringToStringDict); + MyStringToStringIImmutableDict = ImmutableDictionary.CreateRange((Dictionary)MyStringToStringDict); + MyStringToStringImmutableSortedDict = ImmutableSortedDictionary.CreateRange((Dictionary)MyStringToStringDict); + MyStringStackT = new Stack(new List() { "Hello", "World" }); MyStringQueueT = new Queue(new List() { "Hello", "World" }); MyStringHashSetT = new HashSet(new List() { "Hello" }); @@ -198,6 +208,10 @@ public override void Verify() Assert.Equal("value", ((IDictionary)MyStringToStringIDict)["key"]); Assert.Equal("value", ((IReadOnlyDictionary)MyStringToStringIReadOnlyDict)["key"]); + Assert.Equal("value", ((ImmutableDictionary)MyStringToStringImmutableDict)["key"]); + Assert.Equal("value", ((IImmutableDictionary)MyStringToStringIImmutableDict)["key"]); + Assert.Equal("value", ((ImmutableSortedDictionary)MyStringToStringImmutableSortedDict)["key"]); + Assert.Equal(2, ((Stack)MyStringStackT).Count); Assert.True(((Stack)MyStringStackT).Contains("Hello")); Assert.True(((Stack)MyStringStackT).Contains("World")); diff --git a/src/System.Text.Json/tests/Serialization/TestClasses.cs b/src/System.Text.Json/tests/Serialization/TestClasses.cs index 22e7eaceb6f1..afb95c4614bd 100644 --- a/src/System.Text.Json/tests/Serialization/TestClasses.cs +++ b/src/System.Text.Json/tests/Serialization/TestClasses.cs @@ -769,6 +769,144 @@ public void Verify() } } + public class TestClassWithStringToObjectDictionaries : ITestClass + { + public Dictionary MyDict { get; set; } + + // TODO: Add support for classes as dictionary values in class-nested dicitonaries for the following types. + + //public IDictionary MyIDict { get; set; } + //public IReadOnlyDictionary MyIReadOnlyDict { get; set; } + //public ImmutableDictionary MyImmutableDict { get; set; } + //public IImmutableDictionary MyIImmutableDict { get; set; } + //public ImmutableSortedDictionary MyImmutableSortedDict { get; set; } + + public static readonly byte[] s_data = Encoding.UTF8.GetBytes( + @"{" + + @"""MyDict"":{" + + @"""Key1"":" + SimpleTestClass.s_json + "," + + @"""Key2"":" + SimpleTestClass.s_json + + @"}" + + //@"""MyIDict"":{" + + // @"""Key1"":" + SimpleTestClass.s_json + "," + + // @"""Key2"":" + SimpleTestClass.s_json + + //@"}" + + //@"""MyReadOnlyDict"":{" + + // @"""Key1"":" + SimpleTestClass.s_json + "," + + // @"""Key2"":" + SimpleTestClass.s_json + + //@"}" + + //@"""MyImmutableDict"":{" + + // @"""Key1"":" + SimpleTestClass.s_json + "," + + // @"""Key2"":" + SimpleTestClass.s_json + + //@"}," + + //@"""MyIImmutableDict"":{" + + // @"""Key1"":" + SimpleTestClass.s_json + "," + + // @"""Key2"":" + SimpleTestClass.s_json + + //@"}," + + //@"""MyImmutableSortedDict"":{" + + // @"""Key1"":" + SimpleTestClass.s_json + "," + + // @"""Key2"":" + SimpleTestClass.s_json + + //@"}" + + @"}"); + + public void Initialize() + { + MyDict = new Dictionary(); + { + SimpleTestClass obj = new SimpleTestClass(); + obj.Initialize(); + MyDict.TryAdd("Key1", obj); + } + { + SimpleTestClass obj = new SimpleTestClass(); + obj.Initialize(); + MyDict.TryAdd("Key2", obj); + } + + //MyIDict = new Dictionary(); + //{ + // SimpleTestClass obj = new SimpleTestClass(); + // obj.Initialize(); + // MyIDict.TryAdd("Key1", obj); + //} + //{ + // SimpleTestClass obj = new SimpleTestClass(); + // obj.Initialize(); + // MyIDict.TryAdd("Key2", obj); + //} + + //{ + // SimpleTestClass obj1 = new SimpleTestClass(); + // obj1.Initialize(); + + // SimpleTestClass obj2 = new SimpleTestClass(); + // obj2.Initialize(); + + // MyIReadOnlyDict = new Dictionary() { { "Key1", obj1 }, { "Key2", obj2 } }; + //} + + //{ + // SimpleTestClass obj1 = new SimpleTestClass(); + // obj1.Initialize(); + + // SimpleTestClass obj2 = new SimpleTestClass(); + // obj2.Initialize(); + + // Dictionary tempDict = new Dictionary() { { "Key1", obj1 }, { "Key2", obj2 } }; + // MyImmutableDict = ImmutableDictionary.CreateRange(tempDict); + //} + + //{ + // SimpleTestClass obj1 = new SimpleTestClass(); + // obj1.Initialize(); + + // SimpleTestClass obj2 = new SimpleTestClass(); + // obj2.Initialize(); + + // Dictionary tempDict = new Dictionary() { { "Key1", obj1 }, { "Key2", obj2 } }; + // MyIImmutableDict = ImmutableDictionary.CreateRange(tempDict); + //} + + //{ + // SimpleTestClass obj1 = new SimpleTestClass(); + // obj1.Initialize(); + + // SimpleTestClass obj2 = new SimpleTestClass(); + // obj2.Initialize(); + + // Dictionary tempDict = new Dictionary() { { "Key1", obj1 }, { "Key2", obj2 } }; + // MyImmutableSortedDict = ImmutableSortedDictionary.CreateRange(tempDict); + //} + } + + public void Verify() + { + Assert.Equal(2, MyDict.Count); + MyDict["Key1"].Verify(); + MyDict["Key2"].Verify(); + + //Assert.Equal(2, MyIDict.Count); + //MyIDict["Key1"].Verify(); + //MyIDict["Key2"].Verify(); + + //Assert.Equal(2, MyIReadOnlyDict.Count); + //MyIReadOnlyDict["Key1"].Verify(); + //MyIReadOnlyDict["Key2"].Verify(); + + //Assert.Equal(2, MyImmutableDict.Count); + //MyImmutableDict["Key1"].Verify(); + //MyImmutableDict["Key2"].Verify(); + + //Assert.Equal(2, MyIImmutableDict.Count); + //MyIImmutableDict["Key1"].Verify(); + //MyIImmutableDict["Key2"].Verify(); + + //Assert.Equal(2, MyImmutableSortedDict.Count); + //MyImmutableSortedDict["Key1"].Verify(); + //MyImmutableSortedDict["Key2"].Verify(); + } + } + public class TestClassWithObjectIEnumerableConstructibleTypes : ITestClass { public Stack MyStack { get; set; } diff --git a/src/System.Text.Json/tests/Serialization/TestData.cs b/src/System.Text.Json/tests/Serialization/TestData.cs index ebf962f2a232..6d66332b96d7 100644 --- a/src/System.Text.Json/tests/Serialization/TestData.cs +++ b/src/System.Text.Json/tests/Serialization/TestData.cs @@ -37,6 +37,7 @@ public static IEnumerable ReadSuccessCases yield return new object[] { typeof(TestClassWithStringToPrimitiveDictionary), TestClassWithStringToPrimitiveDictionary.s_data }; yield return new object[] { typeof(TestClassWithObjectIEnumerableConstructibleTypes), TestClassWithObjectIEnumerableConstructibleTypes.s_data }; yield return new object[] { typeof(TestClassWithObjectImmutableTypes), TestClassWithObjectImmutableTypes.s_data }; + yield return new object[] { typeof(TestClassWithStringToObjectDictionaries), TestClassWithStringToObjectDictionaries.s_data }; yield return new object[] { typeof(JsonElementTests.JsonElementClass), JsonElementTests.JsonElementClass.s_data }; yield return new object[] { typeof(JsonElementTests.JsonElementArrayClass), JsonElementTests.JsonElementArrayClass.s_data }; yield return new object[] { typeof(ClassWithComplexObjects), ClassWithComplexObjects.s_data }; @@ -71,6 +72,7 @@ public static IEnumerable WriteSuccessCases yield return new object[] { new TestClassWithStringToPrimitiveDictionary() }; yield return new object[] { new TestClassWithObjectIEnumerableConstructibleTypes() }; yield return new object[] { new TestClassWithObjectImmutableTypes() }; + yield return new object[] { new TestClassWithStringToObjectDictionaries() }; yield return new object[] { new JsonElementTests.JsonElementClass() }; yield return new object[] { new JsonElementTests.JsonElementArrayClass() }; yield return new object[] { new ClassWithComplexObjects() }; From 36899979540b28d5bb7f04b576f8daef56efcff0 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Thu, 16 May 2019 11:56:11 -0700 Subject: [PATCH 02/13] Some clean up --- ...efaultIEnumerableConstructibleConverter.cs | 13 ++++-- .../Converters/DefaultImmutableConverter.cs | 44 +------------------ .../tests/Serialization/TestClasses.cs | 8 ++-- 3 files changed, 15 insertions(+), 50 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs index 8aa82ed1b6f7..1970101f3ad8 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs @@ -12,11 +12,8 @@ internal sealed class DefaultIEnumerableConstructibleConverter : JsonEnumerableC { public static ConcurrentDictionary s_objectJsonProperties = new ConcurrentDictionary(); - public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options) + public static JsonPropertyInfo GetElementJsonPropertyInfo(JsonClassInfo elementClassInfo, JsonSerializerOptions options) { - Type enumerableType = state.Current.JsonPropertyInfo.RuntimePropertyType; - JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; - JsonPropertyInfo propertyInfo; if (elementClassInfo.ClassType == ClassType.Object) { @@ -37,6 +34,14 @@ public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList propertyInfo = elementClassInfo.GetPolicyProperty(); } + return propertyInfo; + } + + public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options) + { + Type enumerableType = state.Current.JsonPropertyInfo.RuntimePropertyType; + JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; + JsonPropertyInfo propertyInfo = GetElementJsonPropertyInfo(elementClassInfo, options); return propertyInfo.CreateIEnumerableConstructibleType(enumerableType, sourceList); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs index 3b4e5b084fd9..668e300083dd 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs @@ -137,27 +137,7 @@ public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList Debug.Assert(s_createRangeDelegates.ContainsKey(delegateKey)); JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; - - JsonPropertyInfo propertyInfo; - if (elementClassInfo.ClassType == ClassType.Object) - { - Type objectType = elementClassInfo.Type; - - if (DefaultIEnumerableConstructibleConverter.s_objectJsonProperties.ContainsKey(objectType)) - { - propertyInfo = DefaultIEnumerableConstructibleConverter.s_objectJsonProperties[objectType]; - } - else - { - propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, null, typeof(object), options); - DefaultIEnumerableConstructibleConverter.s_objectJsonProperties[objectType] = propertyInfo; - } - } - else - { - propertyInfo = elementClassInfo.GetPolicyProperty(); - } - + JsonPropertyInfo propertyInfo = DefaultIEnumerableConstructibleConverter.GetElementJsonPropertyInfo(elementClassInfo, options); return propertyInfo.CreateImmutableCollectionFromList(delegateKey, sourceList); } @@ -170,27 +150,7 @@ public IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceD Debug.Assert(s_createRangeDelegates.ContainsKey(delegateKey)); JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; - - JsonPropertyInfo propertyInfo; - if (elementClassInfo.ClassType == ClassType.Object) - { - Type objectType = elementClassInfo.Type; - - if (DefaultIEnumerableConstructibleConverter.s_objectJsonProperties.ContainsKey(objectType)) - { - propertyInfo = DefaultIEnumerableConstructibleConverter.s_objectJsonProperties[objectType]; - } - else - { - propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, null, typeof(object), options); - DefaultIEnumerableConstructibleConverter.s_objectJsonProperties[objectType] = propertyInfo; - } - } - else - { - propertyInfo = elementClassInfo.GetPolicyProperty(); - } - + JsonPropertyInfo propertyInfo = DefaultIEnumerableConstructibleConverter.GetElementJsonPropertyInfo(elementClassInfo, options); return propertyInfo.CreateImmutableCollectionFromDictionary(delegateKey, sourceDictionary); } } diff --git a/src/System.Text.Json/tests/Serialization/TestClasses.cs b/src/System.Text.Json/tests/Serialization/TestClasses.cs index afb95c4614bd..00c5453948a5 100644 --- a/src/System.Text.Json/tests/Serialization/TestClasses.cs +++ b/src/System.Text.Json/tests/Serialization/TestClasses.cs @@ -815,24 +815,24 @@ public void Initialize() { SimpleTestClass obj = new SimpleTestClass(); obj.Initialize(); - MyDict.TryAdd("Key1", obj); + MyDict["Key1"] = obj; } { SimpleTestClass obj = new SimpleTestClass(); obj.Initialize(); - MyDict.TryAdd("Key2", obj); + MyDict["Key2"] = obj; } //MyIDict = new Dictionary(); //{ // SimpleTestClass obj = new SimpleTestClass(); // obj.Initialize(); - // MyIDict.TryAdd("Key1", obj); + // MyIDict["Key1"] = obj; //} //{ // SimpleTestClass obj = new SimpleTestClass(); // obj.Initialize(); - // MyIDict.TryAdd("Key2", obj); + // MyIDict["Key2"] = obj; //} //{ From 39216c8d8250f9af8acd6ce8a8fe1b7ac6754317 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Thu, 16 May 2019 16:29:14 -0700 Subject: [PATCH 03/13] Address review comments --- .../Json/Serialization/ClassMaterializer.cs | 6 +- .../Converters/DefaultImmutableConverter.cs | 4 +- .../Json/Serialization/JsonPropertyInfo.cs | 4 +- .../Serialization/JsonPropertyInfoCommon.cs | 15 ++- .../ReflectionEmitMaterializer.cs | 15 ++- .../Serialization/ReflectionMaterializer.cs | 15 ++- .../tests/Serialization/DictionaryTests.cs | 1 - .../tests/Serialization/TestClasses.cs | 96 ------------------- 8 files changed, 33 insertions(+), 123 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs index 6841b2d089ce..5db5b02ccb20 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs @@ -2,11 +2,8 @@ // 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; -using System.Collections.Generic; using System.Diagnostics; using System.Reflection; -using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization { @@ -35,6 +32,9 @@ protected MethodInfo ImmutableCreateRangeMethod(Type constructingType, Type elem } } + // This shouldn't happen because constructingType should be an immutable type with + // a CreateRange method. `null` being returned here will cause a JsonException to be + // thrown when the desired CreateRange delegate is about to be invoked. Debug.Fail("Could not create the appropriate CreateRange method."); return null; } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs index 668e300083dd..958aee587889 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs @@ -138,7 +138,7 @@ public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; JsonPropertyInfo propertyInfo = DefaultIEnumerableConstructibleConverter.GetElementJsonPropertyInfo(elementClassInfo, options); - return propertyInfo.CreateImmutableCollectionFromList(delegateKey, sourceList); + return propertyInfo.CreateImmutableCollectionFromList(immutableCollectionType, delegateKey, sourceList, state.PropertyPath); } public IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options) @@ -151,7 +151,7 @@ public IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceD JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; JsonPropertyInfo propertyInfo = DefaultIEnumerableConstructibleConverter.GetElementJsonPropertyInfo(elementClassInfo, options); - return propertyInfo.CreateImmutableCollectionFromDictionary(delegateKey, sourceDictionary); + return propertyInfo.CreateImmutableCollectionFromDictionary(immutableCollectionType, delegateKey, sourceDictionary, state.PropertyPath); } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs index cc4e214b6aa6..7d2d7a7ba42f 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs @@ -280,9 +280,9 @@ public TAttribute GetAttribute() where TAttribute : Attribute return (TAttribute)PropertyInfo?.GetCustomAttribute(typeof(TAttribute), inherit: false); } - public abstract IEnumerable CreateImmutableCollectionFromList(string delegateKey, IList sourceList); + public abstract IEnumerable CreateImmutableCollectionFromList(Type collectionType, string delegateKey, IList sourceList, string propertyPath); - public abstract IDictionary CreateImmutableCollectionFromDictionary(string delegateKey, IDictionary sourceDictionary); + public abstract IDictionary CreateImmutableCollectionFromDictionary(Type collectionType, string delegateKey, IDictionary sourceDictionary, string propertyPath); public abstract IEnumerable CreateIEnumerableConstructibleType(Type enumerableType, IList sourceList); diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs index bbd4d4496353..265e332144ce 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs @@ -72,7 +72,6 @@ public override object GetValueAsObject(object obj) return obj; } - Debug.Assert(Get != null); return Get((TClass)obj); } @@ -100,26 +99,36 @@ public override Type GetDictionaryConcreteType() // Creates an IEnumerable and populates it with the items in the, // sourceList argument then uses the delegateKey argument to identify the appropriate cached // CreateRange method to create and return the desired immutable collection type. - public override IEnumerable CreateImmutableCollectionFromList(string delegateKey, IList sourceList) + public override IEnumerable CreateImmutableCollectionFromList(Type collectionType, string delegateKey, IList sourceList, string propertyPath) { Debug.Assert(DefaultImmutableConverter.s_createRangeDelegates.ContainsKey(delegateKey)); DefaultImmutableConverter.ImmutableCreateRangeDelegate createRangeDelegate = ( (DefaultImmutableConverter.ImmutableCreateRangeDelegate)DefaultImmutableConverter.s_createRangeDelegates[delegateKey]); + if (createRangeDelegate == null) + { + ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(collectionType, propertyPath); + } + return (IEnumerable)createRangeDelegate.Invoke(CreateGenericIEnumerableFromList(sourceList)); } // Creates an IEnumerable and populates it with the items in the, // sourceList argument then uses the delegateKey argument to identify the appropriate cached // CreateRange method to create and return the desired immutable collection type. - public override IDictionary CreateImmutableCollectionFromDictionary(string delegateKey, IDictionary sourceDictionary) + public override IDictionary CreateImmutableCollectionFromDictionary(Type collectionType, string delegateKey, IDictionary sourceDictionary, string propertyPath) { Debug.Assert(DefaultImmutableConverter.s_createRangeDelegates.ContainsKey(delegateKey)); DefaultImmutableConverter.ImmutableDictCreateRangeDelegate createRangeDelegate = ( (DefaultImmutableConverter.ImmutableDictCreateRangeDelegate)DefaultImmutableConverter.s_createRangeDelegates[delegateKey]); + if (createRangeDelegate == null) + { + ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(collectionType, propertyPath); + } + return (IDictionary)createRangeDelegate.Invoke(CreateGenericIEnumerableFromDictionary(sourceDictionary)); } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs index 5dfc6c0292df..087b9eda47f8 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs @@ -40,22 +40,21 @@ public override object ImmutableCreateRange(Type constructingType, Type elementT { MethodInfo createRange = ImmutableCreateRangeMethod(constructingType, elementType, constructingTypeIsDict); - Debug.Assert(createRange != null); + if (createRange == null) + { + return null; + } - object createRangeDelegate; if (constructingTypeIsDict) { - createRangeDelegate = createRange.CreateDelegate( + return createRange.CreateDelegate( typeof(DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null); } else { - createRangeDelegate = createRange.CreateDelegate( - typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null); + return createRange.CreateDelegate( + typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null); } - - Debug.Assert(createRangeDelegate != null); - return createRangeDelegate; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs index 1505a359663d..18b93814c0f8 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs @@ -19,22 +19,21 @@ public override object ImmutableCreateRange(Type constructingType, Type elementT { MethodInfo createRange = ImmutableCreateRangeMethod(constructingType, elementType, constructingTypeIsDict); - Debug.Assert(createRange != null); + if (createRange == null) + { + return null; + } - object createRangeDelegate; if (constructingTypeIsDict) { - createRangeDelegate = createRange.CreateDelegate( + return createRange.CreateDelegate( typeof(DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null); } else { - createRangeDelegate = createRange.CreateDelegate( - typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null); + return createRange.CreateDelegate( + typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null); } - - Debug.Assert(createRangeDelegate != null); - return createRangeDelegate; } } } diff --git a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs index 1ad3d131063a..182c12f573c9 100644 --- a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs +++ b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs @@ -177,7 +177,6 @@ public static void DictionaryOfList() Assert.Equal(3, obj["Key2"][0]); Assert.Equal(4, obj["Key2"][1]); - string json = JsonSerializer.ToString(obj); const string ReorderedJsonString = @"{""Key2"":[3,4],""Key1"":[1,2]}"; Assert.True(JsonString == json || ReorderedJsonString == json); diff --git a/src/System.Text.Json/tests/Serialization/TestClasses.cs b/src/System.Text.Json/tests/Serialization/TestClasses.cs index 00c5453948a5..115705397d72 100644 --- a/src/System.Text.Json/tests/Serialization/TestClasses.cs +++ b/src/System.Text.Json/tests/Serialization/TestClasses.cs @@ -774,7 +774,6 @@ public class TestClassWithStringToObjectDictionaries : ITestClass public Dictionary MyDict { get; set; } // TODO: Add support for classes as dictionary values in class-nested dicitonaries for the following types. - //public IDictionary MyIDict { get; set; } //public IReadOnlyDictionary MyIReadOnlyDict { get; set; } //public ImmutableDictionary MyImmutableDict { get; set; } @@ -787,26 +786,6 @@ public class TestClassWithStringToObjectDictionaries : ITestClass @"""Key1"":" + SimpleTestClass.s_json + "," + @"""Key2"":" + SimpleTestClass.s_json + @"}" + - //@"""MyIDict"":{" + - // @"""Key1"":" + SimpleTestClass.s_json + "," + - // @"""Key2"":" + SimpleTestClass.s_json + - //@"}" + - //@"""MyReadOnlyDict"":{" + - // @"""Key1"":" + SimpleTestClass.s_json + "," + - // @"""Key2"":" + SimpleTestClass.s_json + - //@"}" + - //@"""MyImmutableDict"":{" + - // @"""Key1"":" + SimpleTestClass.s_json + "," + - // @"""Key2"":" + SimpleTestClass.s_json + - //@"}," + - //@"""MyIImmutableDict"":{" + - // @"""Key1"":" + SimpleTestClass.s_json + "," + - // @"""Key2"":" + SimpleTestClass.s_json + - //@"}," + - //@"""MyImmutableSortedDict"":{" + - // @"""Key1"":" + SimpleTestClass.s_json + "," + - // @"""Key2"":" + SimpleTestClass.s_json + - //@"}" + @"}"); public void Initialize() @@ -822,61 +801,6 @@ public void Initialize() obj.Initialize(); MyDict["Key2"] = obj; } - - //MyIDict = new Dictionary(); - //{ - // SimpleTestClass obj = new SimpleTestClass(); - // obj.Initialize(); - // MyIDict["Key1"] = obj; - //} - //{ - // SimpleTestClass obj = new SimpleTestClass(); - // obj.Initialize(); - // MyIDict["Key2"] = obj; - //} - - //{ - // SimpleTestClass obj1 = new SimpleTestClass(); - // obj1.Initialize(); - - // SimpleTestClass obj2 = new SimpleTestClass(); - // obj2.Initialize(); - - // MyIReadOnlyDict = new Dictionary() { { "Key1", obj1 }, { "Key2", obj2 } }; - //} - - //{ - // SimpleTestClass obj1 = new SimpleTestClass(); - // obj1.Initialize(); - - // SimpleTestClass obj2 = new SimpleTestClass(); - // obj2.Initialize(); - - // Dictionary tempDict = new Dictionary() { { "Key1", obj1 }, { "Key2", obj2 } }; - // MyImmutableDict = ImmutableDictionary.CreateRange(tempDict); - //} - - //{ - // SimpleTestClass obj1 = new SimpleTestClass(); - // obj1.Initialize(); - - // SimpleTestClass obj2 = new SimpleTestClass(); - // obj2.Initialize(); - - // Dictionary tempDict = new Dictionary() { { "Key1", obj1 }, { "Key2", obj2 } }; - // MyIImmutableDict = ImmutableDictionary.CreateRange(tempDict); - //} - - //{ - // SimpleTestClass obj1 = new SimpleTestClass(); - // obj1.Initialize(); - - // SimpleTestClass obj2 = new SimpleTestClass(); - // obj2.Initialize(); - - // Dictionary tempDict = new Dictionary() { { "Key1", obj1 }, { "Key2", obj2 } }; - // MyImmutableSortedDict = ImmutableSortedDictionary.CreateRange(tempDict); - //} } public void Verify() @@ -884,26 +808,6 @@ public void Verify() Assert.Equal(2, MyDict.Count); MyDict["Key1"].Verify(); MyDict["Key2"].Verify(); - - //Assert.Equal(2, MyIDict.Count); - //MyIDict["Key1"].Verify(); - //MyIDict["Key2"].Verify(); - - //Assert.Equal(2, MyIReadOnlyDict.Count); - //MyIReadOnlyDict["Key1"].Verify(); - //MyIReadOnlyDict["Key2"].Verify(); - - //Assert.Equal(2, MyImmutableDict.Count); - //MyImmutableDict["Key1"].Verify(); - //MyImmutableDict["Key2"].Verify(); - - //Assert.Equal(2, MyIImmutableDict.Count); - //MyIImmutableDict["Key1"].Verify(); - //MyIImmutableDict["Key2"].Verify(); - - //Assert.Equal(2, MyImmutableSortedDict.Count); - //MyImmutableSortedDict["Key1"].Verify(); - //MyImmutableSortedDict["Key2"].Verify(); } } From bf7942e4743559a5bc01da94057b5457bbfdaed9 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Fri, 17 May 2019 16:45:48 -0700 Subject: [PATCH 04/13] Address review comments --- .../Json/Serialization/ClassMaterializer.cs | 38 +++++++++---- ...efaultIEnumerableConstructibleConverter.cs | 25 ++++----- .../Converters/DefaultImmutableConverter.cs | 53 ++++++++++++------- .../Json/Serialization/JsonPropertyInfo.cs | 6 +-- .../Serialization/JsonPropertyInfoCommon.cs | 29 +++++----- .../ReflectionEmitMaterializer.cs | 25 +++++---- .../Serialization/ReflectionMaterializer.cs | 25 +++++---- 7 files changed, 115 insertions(+), 86 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs index 5db5b02ccb20..70fa56c8684b 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ClassMaterializer.cs @@ -11,9 +11,34 @@ internal abstract class ClassMaterializer { public abstract JsonClassInfo.ConstructorDelegate CreateConstructor(Type classType); - public abstract object ImmutableCreateRange(Type constructingType, Type elementType, bool constructingTypeIsDict); + public abstract object ImmutableCollectionCreateRange(Type constructingType, Type elementType); + public abstract object ImmutableDictionaryCreateRange(Type constructingType, Type elementType); - protected MethodInfo ImmutableCreateRangeMethod(Type constructingType, Type elementType, bool constructingTypeIsDict) + protected MethodInfo ImmutableCollectionCreateRangeMethod(Type constructingType, Type elementType) + { + MethodInfo createRangeMethod = FindImmutableCreateRangeMethod(constructingType); + + if (createRangeMethod == null) + { + return null; + } + + return createRangeMethod.MakeGenericMethod(elementType); + } + + protected MethodInfo ImmutableDictionaryCreateRangeMethod(Type constructingType, Type elementType) + { + MethodInfo createRangeMethod = FindImmutableCreateRangeMethod(constructingType); + + if (createRangeMethod == null) + { + return null; + } + + return createRangeMethod.MakeGenericMethod(typeof(string), elementType); + } + + private MethodInfo FindImmutableCreateRangeMethod(Type constructingType) { MethodInfo[] constructingTypeMethods = constructingType.GetMethods(); @@ -21,14 +46,7 @@ protected MethodInfo ImmutableCreateRangeMethod(Type constructingType, Type elem { if (method.Name == "CreateRange" && method.GetParameters().Length == 1) { - if (constructingTypeIsDict) - { - return method.MakeGenericMethod(typeof(string), elementType); - } - else - { - return method.MakeGenericMethod(elementType); - } + return method; } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs index 1970101f3ad8..926398c9c10e 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs @@ -10,28 +10,21 @@ namespace System.Text.Json.Serialization.Converters { internal sealed class DefaultIEnumerableConstructibleConverter : JsonEnumerableConverter { - public static ConcurrentDictionary s_objectJsonProperties = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary s_objectJsonProperties = new ConcurrentDictionary(); public static JsonPropertyInfo GetElementJsonPropertyInfo(JsonClassInfo elementClassInfo, JsonSerializerOptions options) { - JsonPropertyInfo propertyInfo; - if (elementClassInfo.ClassType == ClassType.Object) + if (elementClassInfo.ClassType != ClassType.Object) { - Type objectType = elementClassInfo.Type; - - if (s_objectJsonProperties.ContainsKey(objectType)) - { - propertyInfo = s_objectJsonProperties[objectType]; - } - else - { - propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, null, typeof(object), options); - s_objectJsonProperties[objectType] = propertyInfo; - } + return elementClassInfo.GetPolicyProperty(); } - else + + Type objectType = elementClassInfo.Type; + + if (!s_objectJsonProperties.TryGetValue(objectType, out JsonPropertyInfo propertyInfo)) { - propertyInfo = elementClassInfo.GetPolicyProperty(); + propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, null, typeof(object), options); + s_objectJsonProperties[objectType] = propertyInfo; } return propertyInfo; diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs index 958aee587889..8a32d469804d 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs @@ -44,9 +44,9 @@ internal sealed class DefaultImmutableConverter : JsonEnumerableConverter internal delegate object ImmutableCreateRangeDelegate(IEnumerable items); internal delegate object ImmutableDictCreateRangeDelegate(IEnumerable> items); - public static ConcurrentDictionary s_createRangeDelegates = new ConcurrentDictionary(); + private static ConcurrentDictionary s_createRangeDelegates = new ConcurrentDictionary(); - private string GetConstructingTypeName(string immutableCollectionTypeName) + private static string GetConstructingTypeName(string immutableCollectionTypeName) { switch (immutableCollectionTypeName) { @@ -70,11 +70,25 @@ private string GetConstructingTypeName(string immutableCollectionTypeName) case ImmutableSortedDictionaryGenericTypeName: return ImmutableSortedDictionaryTypeName; default: - // TODO: Refactor exception throw following serialization exception changes. + // TODO: Refactor exception throw following serialization exception changes. throw new NotSupportedException(SR.Format(SR.DeserializeTypeNotSupported, immutableCollectionTypeName)); } } + private static string GetDelegateKey( + Type immutableCollectionType, + Type elementType, + out Type underlyingType, + out string constructingTypeName) + { + // Use the generic type definition of the immutable collection to determine an appropriate constructing type, + // i.e. a type that we can invoke the `CreateRange` method on, which returns an assignable immutable collection. + underlyingType = immutableCollectionType.GetGenericTypeDefinition(); + constructingTypeName = GetConstructingTypeName(underlyingType.FullName); + + return $"{constructingTypeName}:{elementType.FullName}"; + } + internal static bool TypeIsImmutableDictionary(Type type) { if (!type.IsGenericType) @@ -93,21 +107,12 @@ internal static bool TypeIsImmutableDictionary(Type type) } } - private string GetDelegateKey( - Type immutableCollectionType, - Type elementType, - out Type underlyingType, - out string constructingTypeName) + internal static bool TryGetCreateRangeDelegate(string delegateKey, out object createRangeDelegate) { - // Use the generic type definition of the immutable collection to determine an appropriate constructing type, - // i.e. a type that we can invoke the `CreateRange` method on, which returns an assignable immutable collection. - underlyingType = immutableCollectionType.GetGenericTypeDefinition(); - constructingTypeName = GetConstructingTypeName(underlyingType.FullName); - - return $"{constructingTypeName}:{elementType.FullName}"; + return s_createRangeDelegates.TryGetValue(delegateKey, out createRangeDelegate) && createRangeDelegate != null; } - public void RegisterImmutableCollectionType(Type immutableCollectionType, Type elementType, JsonSerializerOptions options) + internal static void RegisterImmutableCollectionType(Type immutableCollectionType, Type elementType, JsonSerializerOptions options) { // Get a unique identifier for a delegate which will point to the appropiate CreateRange method. string delegateKey = GetDelegateKey(immutableCollectionType, elementType, out Type underlyingType, out string constructingTypeName); @@ -122,13 +127,21 @@ public void RegisterImmutableCollectionType(Type immutableCollectionType, Type e Type constructingType = underlyingType.Assembly.GetType(constructingTypeName); // Create a delegate which will point to the CreateRange method. - object createRangeDelegate = options.ClassMaterializerStrategy.ImmutableCreateRange(constructingType, elementType, TypeIsImmutableDictionary(immutableCollectionType)); + object createRangeDelegate; + if (TypeIsImmutableDictionary(immutableCollectionType)) + { + createRangeDelegate = options.ClassMaterializerStrategy.ImmutableDictionaryCreateRange(constructingType, elementType); + } + else + { + createRangeDelegate = options.ClassMaterializerStrategy.ImmutableCollectionCreateRange(constructingType, elementType); + } // Cache the delegate s_createRangeDelegates.TryAdd(delegateKey, createRangeDelegate); } - public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options) + internal IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options) { Type immutableCollectionType = state.Current.JsonPropertyInfo.RuntimePropertyType; Type elementType = state.Current.GetElementType(); @@ -138,10 +151,10 @@ public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; JsonPropertyInfo propertyInfo = DefaultIEnumerableConstructibleConverter.GetElementJsonPropertyInfo(elementClassInfo, options); - return propertyInfo.CreateImmutableCollectionFromList(immutableCollectionType, delegateKey, sourceList, state.PropertyPath); + return propertyInfo.CreateImmutableCollectionFromDictionary(immutableCollectionType, delegateKey, sourceDictionary, state.PropertyPath); } - public IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options) + public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options) { Type immutableCollectionType = state.Current.JsonPropertyInfo.RuntimePropertyType; Type elementType = state.Current.GetElementType(); @@ -151,7 +164,7 @@ public IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceD JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; JsonPropertyInfo propertyInfo = DefaultIEnumerableConstructibleConverter.GetElementJsonPropertyInfo(elementClassInfo, options); - return propertyInfo.CreateImmutableCollectionFromDictionary(immutableCollectionType, delegateKey, sourceDictionary, state.PropertyPath); + return propertyInfo.CreateImmutableCollectionFromList(immutableCollectionType, delegateKey, sourceList, state.PropertyPath); } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs index 7d2d7a7ba42f..c6635c9980ba 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs @@ -193,9 +193,9 @@ private void DetermineSerializationCapabilities(JsonSerializerOptions options) if (ClassType == ClassType.Dictionary && ShouldDeserialize && DefaultImmutableConverter.TypeIsImmutableDictionary(RuntimePropertyType)) { - EnumerableConverter = s_jsonImmutableConverter; - ((DefaultImmutableConverter)EnumerableConverter).RegisterImmutableCollectionType( + DefaultImmutableConverter.RegisterImmutableCollectionType( RuntimePropertyType, JsonClassInfo.GetElementType(RuntimePropertyType), options); + EnumerableConverter = s_jsonImmutableConverter; } } else @@ -246,8 +246,8 @@ private void DetermineSerializationCapabilities(JsonSerializerOptions options) RuntimePropertyType.FullName.StartsWith(DefaultImmutableConverter.ImmutableNamespace) && RuntimePropertyType.GetGenericArguments().Length == 1) { + DefaultImmutableConverter.RegisterImmutableCollectionType(RuntimePropertyType, elementType, options); EnumerableConverter = s_jsonImmutableConverter; - ((DefaultImmutableConverter)EnumerableConverter).RegisterImmutableCollectionType(RuntimePropertyType, elementType, options); } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs index 265e332144ce..94a78af1f906 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs @@ -96,39 +96,35 @@ public override Type GetDictionaryConcreteType() return typeof(Dictionary); } - // Creates an IEnumerable and populates it with the items in the, + // Creates an IEnumerable and populates it with the items in the // sourceList argument then uses the delegateKey argument to identify the appropriate cached // CreateRange method to create and return the desired immutable collection type. public override IEnumerable CreateImmutableCollectionFromList(Type collectionType, string delegateKey, IList sourceList, string propertyPath) { - Debug.Assert(DefaultImmutableConverter.s_createRangeDelegates.ContainsKey(delegateKey)); - - DefaultImmutableConverter.ImmutableCreateRangeDelegate createRangeDelegate = ( - (DefaultImmutableConverter.ImmutableCreateRangeDelegate)DefaultImmutableConverter.s_createRangeDelegates[delegateKey]); - - if (createRangeDelegate == null) + if (!DefaultImmutableConverter.TryGetCreateRangeDelegate(delegateKey, out object createRangeDelegateObj)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(collectionType, propertyPath); } + DefaultImmutableConverter.ImmutableCreateRangeDelegate createRangeDelegate = ( + (DefaultImmutableConverter.ImmutableCreateRangeDelegate)createRangeDelegateObj); + return (IEnumerable)createRangeDelegate.Invoke(CreateGenericIEnumerableFromList(sourceList)); } - // Creates an IEnumerable and populates it with the items in the, + // Creates an IEnumerable and populates it with the items in the // sourceList argument then uses the delegateKey argument to identify the appropriate cached // CreateRange method to create and return the desired immutable collection type. public override IDictionary CreateImmutableCollectionFromDictionary(Type collectionType, string delegateKey, IDictionary sourceDictionary, string propertyPath) { - Debug.Assert(DefaultImmutableConverter.s_createRangeDelegates.ContainsKey(delegateKey)); - - DefaultImmutableConverter.ImmutableDictCreateRangeDelegate createRangeDelegate = ( - (DefaultImmutableConverter.ImmutableDictCreateRangeDelegate)DefaultImmutableConverter.s_createRangeDelegates[delegateKey]); - - if (createRangeDelegate == null) + if (!DefaultImmutableConverter.TryGetCreateRangeDelegate(delegateKey, out object createRangeDelegateObj)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(collectionType, propertyPath); } + DefaultImmutableConverter.ImmutableDictCreateRangeDelegate createRangeDelegate = ( + (DefaultImmutableConverter.ImmutableDictCreateRangeDelegate)createRangeDelegateObj); + return (IDictionary)createRangeDelegate.Invoke(CreateGenericIEnumerableFromDictionary(sourceDictionary)); } @@ -147,10 +143,9 @@ private IEnumerable CreateGenericIEnumerableFromList(IList sou private IEnumerable> CreateGenericIEnumerableFromDictionary(IDictionary sourceDictionary) { - foreach (object item in sourceDictionary) + foreach (DictionaryEntry item in sourceDictionary) { - DictionaryEntry itemDictEntry = (DictionaryEntry)item; - yield return new KeyValuePair((string)itemDictEntry.Key, (TRuntimeProperty)itemDictEntry.Value); + yield return new KeyValuePair((string)item.Key, (TRuntimeProperty)item.Value); } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs index 087b9eda47f8..945fd860aae8 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs @@ -36,25 +36,30 @@ public override JsonClassInfo.ConstructorDelegate CreateConstructor(Type type) return (JsonClassInfo.ConstructorDelegate)dynamicMethod.CreateDelegate(typeof(JsonClassInfo.ConstructorDelegate)); } - public override object ImmutableCreateRange(Type constructingType, Type elementType, bool constructingTypeIsDict) + public override object ImmutableCollectionCreateRange(Type constructingType, Type elementType) { - MethodInfo createRange = ImmutableCreateRangeMethod(constructingType, elementType, constructingTypeIsDict); + MethodInfo createRange = ImmutableCollectionCreateRangeMethod(constructingType, elementType); if (createRange == null) { return null; } - if (constructingTypeIsDict) - { - return createRange.CreateDelegate( - typeof(DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null); - } - else + return createRange.CreateDelegate( + typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null); + } + + public override object ImmutableDictionaryCreateRange(Type constructingType, Type elementType) + { + MethodInfo createRange = ImmutableDictionaryCreateRangeMethod(constructingType, elementType); + + if (createRange == null) { - return createRange.CreateDelegate( - typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null); + return null; } + + return createRange.CreateDelegate( + typeof(DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null); } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs index 18b93814c0f8..c386b6070d98 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMaterializer.cs @@ -15,25 +15,30 @@ public override JsonClassInfo.ConstructorDelegate CreateConstructor(Type type) return () => Activator.CreateInstance(type); } - public override object ImmutableCreateRange(Type constructingType, Type elementType, bool constructingTypeIsDict) + public override object ImmutableCollectionCreateRange(Type constructingType, Type elementType) { - MethodInfo createRange = ImmutableCreateRangeMethod(constructingType, elementType, constructingTypeIsDict); + MethodInfo createRange = ImmutableCollectionCreateRangeMethod(constructingType, elementType); if (createRange == null) { return null; } - if (constructingTypeIsDict) - { - return createRange.CreateDelegate( - typeof(DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null); - } - else + return createRange.CreateDelegate( + typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null); + } + + public override object ImmutableDictionaryCreateRange(Type constructingType, Type elementType) + { + MethodInfo createRange = ImmutableDictionaryCreateRangeMethod(constructingType, elementType); + + if (createRange == null) { - return createRange.CreateDelegate( - typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null); + return null; } + + return createRange.CreateDelegate( + typeof(DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null); } } } From 114f82ff14808c50152e2b2e26448706c08fb754 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Wed, 22 May 2019 14:36:14 -0400 Subject: [PATCH 05/13] Remove commented out code --- .../tests/Serialization/Object.ReadTests.cs | 7 ---- .../tests/Serialization/TestClasses.cs | 41 ------------------- .../tests/Serialization/TestData.cs | 2 - 3 files changed, 50 deletions(-) diff --git a/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs b/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs index c445c7757bde..ed4720c91574 100644 --- a/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs +++ b/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs @@ -174,12 +174,5 @@ public static void ReadClassWithStringToPrimitiveDictionary() TestClassWithStringToPrimitiveDictionary obj = JsonSerializer.Parse(TestClassWithStringToPrimitiveDictionary.s_data); obj.Verify(); } - - [Fact] - public static void ReadClassWithStringToObjectDictionaries() - { - TestClassWithStringToObjectDictionaries obj = JsonSerializer.Parse(TestClassWithStringToObjectDictionaries.s_data); - obj.Verify(); - } } } diff --git a/src/System.Text.Json/tests/Serialization/TestClasses.cs b/src/System.Text.Json/tests/Serialization/TestClasses.cs index 0f265275f766..7f88ab2f26be 100644 --- a/src/System.Text.Json/tests/Serialization/TestClasses.cs +++ b/src/System.Text.Json/tests/Serialization/TestClasses.cs @@ -759,47 +759,6 @@ public void Verify() } } - public class TestClassWithStringToObjectDictionaries : ITestClass - { - // TODO: Add support for classes as dictionary values in class-nested dicitonaries for the following types. - //public Dictionary MyDict { get; set; } - //public IDictionary MyIDict { get; set; } - //public IReadOnlyDictionary MyIReadOnlyDict { get; set; } - //public ImmutableDictionary MyImmutableDict { get; set; } - //public IImmutableDictionary MyIImmutableDict { get; set; } - //public ImmutableSortedDictionary MyImmutableSortedDict { get; set; } - - public static readonly byte[] s_data = Encoding.UTF8.GetBytes( - @"{" + - //@"""MyDict"":{" + - // @"""Key1"":" + SimpleTestClass.s_json + "," + - // @"""Key2"":" + SimpleTestClass.s_json + - //@"}" + - @"}"); - - public void Initialize() - { - //MyDict = new Dictionary(); - //{ - // SimpleTestClass obj = new SimpleTestClass(); - // obj.Initialize(); - // MyDict["Key1"] = obj; - //} - //{ - // SimpleTestClass obj = new SimpleTestClass(); - // obj.Initialize(); - // MyDict["Key2"] = obj; - //} - } - - public void Verify() - { - //Assert.Equal(2, MyDict.Count); - //MyDict["Key1"].Verify(); - //MyDict["Key2"].Verify(); - } - } - public class TestClassWithObjectIEnumerableConstructibleTypes : ITestClass { public Stack MyStack { get; set; } diff --git a/src/System.Text.Json/tests/Serialization/TestData.cs b/src/System.Text.Json/tests/Serialization/TestData.cs index 4859ff377658..8be65ec9cc75 100644 --- a/src/System.Text.Json/tests/Serialization/TestData.cs +++ b/src/System.Text.Json/tests/Serialization/TestData.cs @@ -38,7 +38,6 @@ public static IEnumerable ReadSuccessCases yield return new object[] { typeof(TestClassWithStringToPrimitiveDictionary), TestClassWithStringToPrimitiveDictionary.s_data }; yield return new object[] { typeof(TestClassWithObjectIEnumerableConstructibleTypes), TestClassWithObjectIEnumerableConstructibleTypes.s_data }; yield return new object[] { typeof(TestClassWithObjectImmutableTypes), TestClassWithObjectImmutableTypes.s_data }; - yield return new object[] { typeof(TestClassWithStringToObjectDictionaries), TestClassWithStringToObjectDictionaries.s_data }; yield return new object[] { typeof(JsonElementTests.JsonElementClass), JsonElementTests.JsonElementClass.s_data }; yield return new object[] { typeof(JsonElementTests.JsonElementArrayClass), JsonElementTests.JsonElementArrayClass.s_data }; yield return new object[] { typeof(ClassWithComplexObjects), ClassWithComplexObjects.s_data }; @@ -74,7 +73,6 @@ public static IEnumerable WriteSuccessCases yield return new object[] { new TestClassWithStringToPrimitiveDictionary() }; yield return new object[] { new TestClassWithObjectIEnumerableConstructibleTypes() }; yield return new object[] { new TestClassWithObjectImmutableTypes() }; - yield return new object[] { new TestClassWithStringToObjectDictionaries() }; yield return new object[] { new JsonElementTests.JsonElementClass() }; yield return new object[] { new JsonElementTests.JsonElementArrayClass() }; yield return new object[] { new ClassWithComplexObjects() }; From 11522a56d50029eac4b565424cf7e7b14394a84d Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Thu, 23 May 2019 14:09:19 -0400 Subject: [PATCH 06/13] Move object property infos to JsonSerializerOptions --- ...efaultIEnumerableConstructibleConverter.cs | 22 +------------------ .../Converters/DefaultImmutableConverter.cs | 4 ++-- .../JsonSerializer.Write.HandleDictionary.cs | 2 +- .../JsonSerializer.Write.HandleObject.cs | 2 +- .../Serialization/JsonSerializerOptions.cs | 19 ++++++++++++++++ .../Json/Serialization/WriteStackFrame.cs | 4 ++-- .../tests/Serialization/DictionaryTests.cs | 5 ++--- 7 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs index 926398c9c10e..31066a1eb8e3 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultIEnumerableConstructibleConverter.cs @@ -10,31 +10,11 @@ namespace System.Text.Json.Serialization.Converters { internal sealed class DefaultIEnumerableConstructibleConverter : JsonEnumerableConverter { - private static readonly ConcurrentDictionary s_objectJsonProperties = new ConcurrentDictionary(); - - public static JsonPropertyInfo GetElementJsonPropertyInfo(JsonClassInfo elementClassInfo, JsonSerializerOptions options) - { - if (elementClassInfo.ClassType != ClassType.Object) - { - return elementClassInfo.GetPolicyProperty(); - } - - Type objectType = elementClassInfo.Type; - - if (!s_objectJsonProperties.TryGetValue(objectType, out JsonPropertyInfo propertyInfo)) - { - propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, null, typeof(object), options); - s_objectJsonProperties[objectType] = propertyInfo; - } - - return propertyInfo; - } - public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options) { Type enumerableType = state.Current.JsonPropertyInfo.RuntimePropertyType; JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; - JsonPropertyInfo propertyInfo = GetElementJsonPropertyInfo(elementClassInfo, options); + JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options); return propertyInfo.CreateIEnumerableConstructibleType(enumerableType, sourceList); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs index 8a32d469804d..4fd5d5e9264f 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs @@ -150,7 +150,7 @@ internal IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourc Debug.Assert(s_createRangeDelegates.ContainsKey(delegateKey)); JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; - JsonPropertyInfo propertyInfo = DefaultIEnumerableConstructibleConverter.GetElementJsonPropertyInfo(elementClassInfo, options); + JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options); return propertyInfo.CreateImmutableCollectionFromDictionary(immutableCollectionType, delegateKey, sourceDictionary, state.PropertyPath); } @@ -163,7 +163,7 @@ public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList Debug.Assert(s_createRangeDelegates.ContainsKey(delegateKey)); JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; - JsonPropertyInfo propertyInfo = DefaultIEnumerableConstructibleConverter.GetElementJsonPropertyInfo(elementClassInfo, options); + JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options); return propertyInfo.CreateImmutableCollectionFromList(immutableCollectionType, delegateKey, sourceList, state.PropertyPath); } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs index 5b056a0d896f..6d4a258c398b 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs @@ -112,7 +112,7 @@ internal static void WriteDictionary( value = (TProperty)polymorphicEnumerator.Current.Value; key = polymorphicEnumerator.Current.Key; } - else if (current.IsImmutableDictionary || current.IsPropertyAnImmutableDictionary) + else if (current.IsImmutableDictionary || current.IsImmutableDictionaryProperty) { value = (TProperty)((DictionaryEntry)current.Enumerator.Current).Value; key = (string)((DictionaryEntry)current.Enumerator.Current).Key; diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs index eb6393aa81f4..6da35d29c45c 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs @@ -95,7 +95,7 @@ private static bool HandleObject( // A property that returns a dictionary keeps the same stack frame. if (jsonPropertyInfo.ClassType == ClassType.Dictionary) { - state.Current.IsPropertyAnImmutableDictionary = DefaultImmutableConverter.TypeIsImmutableDictionary(jsonPropertyInfo.RuntimePropertyType); + state.Current.IsImmutableDictionaryProperty = DefaultImmutableConverter.TypeIsImmutableDictionary(jsonPropertyInfo.RuntimePropertyType); bool endOfEnumerable = HandleDictionary(jsonPropertyInfo.ElementClassInfo, options, writer, ref state); if (endOfEnumerable) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs index e78e2d39ca7e..13dc596abd66 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs @@ -17,6 +17,7 @@ public sealed class JsonSerializerOptions internal static readonly JsonSerializerOptions s_defaultOptions = new JsonSerializerOptions(); private readonly ConcurrentDictionary _classes = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _objectJsonProperties = new ConcurrentDictionary(); private ClassMaterializer _classMaterializerStrategy; private JsonNamingPolicy _dictionayKeyPolicy; private JsonNamingPolicy _jsonPropertyNamingPolicy; @@ -301,6 +302,24 @@ internal JsonWriterOptions GetWriterOptions() }; } + internal JsonPropertyInfo GetJsonPropertyInfoFromClassInfo(JsonClassInfo classInfo, JsonSerializerOptions options) + { + if (classInfo.ClassType != ClassType.Object) + { + return classInfo.GetPolicyProperty(); + } + + Type objectType = classInfo.Type; + + if (!_objectJsonProperties.TryGetValue(objectType, out JsonPropertyInfo propertyInfo)) + { + propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, null, typeof(object), options); + _objectJsonProperties[objectType] = propertyInfo; + } + + return propertyInfo; + } + private void VerifyMutable() { // The default options are hidden and thus should be immutable. diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs index 060b606c34ad..96d16b9c16f5 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs @@ -20,7 +20,7 @@ internal struct WriteStackFrame // Whether the current object is an immutable dictionary. public bool IsImmutableDictionary; - public bool IsPropertyAnImmutableDictionary; + public bool IsImmutableDictionaryProperty; // The current enumerator for the IEnumerable or IDictionary. public IEnumerator Enumerator; @@ -130,7 +130,7 @@ public void EndObject() { PropertyIndex = 0; PopStackOnEndObject = false; - IsPropertyAnImmutableDictionary = false; + IsImmutableDictionaryProperty = false; JsonPropertyInfo = null; } diff --git a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs index 7094fc6c5cef..6c5ac51bb5d0 100644 --- a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs +++ b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs @@ -142,8 +142,8 @@ class Poco [Fact] public static void FirstGenericArgNotStringFail() { - Assert.Throws(() => JsonSerializer.Parse>(@"{""Key1"":1}")); - Assert.Throws(() => JsonSerializer.Parse>(@"{""Key1"":1}")); + Assert.Throws(() => JsonSerializer.Parse>(@"{1:1}")); + Assert.Throws(() => JsonSerializer.Parse>(@"{1:1}")); } [Fact] @@ -162,7 +162,6 @@ public static void DictionaryOfList() Assert.Equal(3, obj["Key2"][0]); Assert.Equal(4, obj["Key2"][1]); - string json = JsonSerializer.ToString(obj); Assert.Equal(JsonString, json); } From 4cf3cfac4cf3cc2783d12f4cacacfca586661084 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Sat, 25 May 2019 16:45:49 -0400 Subject: [PATCH 07/13] Re-add immutable test --- .../tests/Serialization/DictionaryTests.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs index c9f1df8ddf28..4bd3612c2d8d 100644 --- a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs +++ b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -319,6 +318,25 @@ public static void DictionaryOfDictionary() string json = JsonSerializer.ToString(obj); Assert.Equal(JsonString, json); + + json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + } + + { + ImmutableSortedDictionary> obj = JsonSerializer.Parse>>(JsonString); + + Assert.Equal(2, obj.Count); + Assert.Equal(2, obj["Key1"].Count); + Assert.Equal(1, obj["Key1"]["Key1a"]); + Assert.Equal(2, obj["Key1"]["Key1b"]); + Assert.Equal(2, obj["Key2"].Count); + Assert.Equal(3, obj["Key2"]["Key2a"]); + Assert.Equal(4, obj["Key2"]["Key2b"]); + + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + json = JsonSerializer.ToString(obj); Assert.Equal(JsonString, json); } From c2994d1a0ce7a6f2f7f0c14dfc8cae8c4134b92a Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Tue, 28 May 2019 16:49:30 -0400 Subject: [PATCH 08/13] Cache IsImmutableDict bool --- .../Text/Json/Serialization/ClassType.cs | 1 + .../Converters/DefaultImmutableConverter.cs | 37 +++++--- .../JsonClassInfo.AddProperty.cs | 5 +- .../Text/Json/Serialization/JsonClassInfo.cs | 40 +++++---- .../Json/Serialization/JsonPropertyInfo.cs | 20 ++--- .../JsonSerializer.Read.HandleArray.cs | 43 +++++---- .../JsonSerializer.Read.HandleDictionary.cs | 2 +- .../JsonSerializer.Read.HandleObject.cs | 4 +- .../JsonSerializer.Read.HandlePropertyName.cs | 9 +- .../Json/Serialization/JsonSerializer.Read.cs | 4 +- .../JsonSerializer.Write.HandleObject.cs | 14 ++- .../Serialization/JsonSerializer.Write.cs | 1 + .../Text/Json/Serialization/ReadStackFrame.cs | 14 +-- .../Text/Json/Serialization/WriteStack.cs | 7 +- .../Json/Serialization/WriteStackFrame.cs | 12 ++- .../tests/Serialization/DictionaryTests.cs | 90 +++++++++---------- 16 files changed, 184 insertions(+), 119 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ClassType.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ClassType.cs index f5fe6ce2c730..9ce97683ede6 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ClassType.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ClassType.cs @@ -14,5 +14,6 @@ internal enum ClassType Value = 2, // Data type with single value Enumerable = 3, // IEnumerable Dictionary = 4, // IDictionary + ImmutableDictionary = 5, // Immutable Dictionary } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs index 4fd5d5e9264f..d1290855e582 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs @@ -112,7 +112,7 @@ internal static bool TryGetCreateRangeDelegate(string delegateKey, out object cr return s_createRangeDelegates.TryGetValue(delegateKey, out createRangeDelegate) && createRangeDelegate != null; } - internal static void RegisterImmutableCollectionType(Type immutableCollectionType, Type elementType, JsonSerializerOptions options) + internal static void RegisterImmutableCollection(Type immutableCollectionType, Type elementType, JsonSerializerOptions options) { // Get a unique identifier for a delegate which will point to the appropiate CreateRange method. string delegateKey = GetDelegateKey(immutableCollectionType, elementType, out Type underlyingType, out string constructingTypeName); @@ -128,20 +128,35 @@ internal static void RegisterImmutableCollectionType(Type immutableCollectionTyp // Create a delegate which will point to the CreateRange method. object createRangeDelegate; - if (TypeIsImmutableDictionary(immutableCollectionType)) - { - createRangeDelegate = options.ClassMaterializerStrategy.ImmutableDictionaryCreateRange(constructingType, elementType); - } - else + createRangeDelegate = options.ClassMaterializerStrategy.ImmutableCollectionCreateRange(constructingType, elementType); + + // Cache the delegate + s_createRangeDelegates.TryAdd(delegateKey, createRangeDelegate); + } + + internal static void RegisterImmutableDictionary(Type immutableCollectionType, Type elementType, JsonSerializerOptions options) + { + // Get a unique identifier for a delegate which will point to the appropiate CreateRange method. + string delegateKey = GetDelegateKey(immutableCollectionType, elementType, out Type underlyingType, out string constructingTypeName); + + // Exit if we have registered this immutable collection type. + if (s_createRangeDelegates.ContainsKey(delegateKey)) { - createRangeDelegate = options.ClassMaterializerStrategy.ImmutableCollectionCreateRange(constructingType, elementType); + return; } + // Get the constructing type. + Type constructingType = underlyingType.Assembly.GetType(constructingTypeName); + + // Create a delegate which will point to the CreateRange method. + object createRangeDelegate; + createRangeDelegate = options.ClassMaterializerStrategy.ImmutableDictionaryCreateRange(constructingType, elementType); + // Cache the delegate s_createRangeDelegates.TryAdd(delegateKey, createRangeDelegate); } - internal IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options) + public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options) { Type immutableCollectionType = state.Current.JsonPropertyInfo.RuntimePropertyType; Type elementType = state.Current.GetElementType(); @@ -151,10 +166,10 @@ internal IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourc JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options); - return propertyInfo.CreateImmutableCollectionFromDictionary(immutableCollectionType, delegateKey, sourceDictionary, state.PropertyPath); + return propertyInfo.CreateImmutableCollectionFromList(immutableCollectionType, delegateKey, sourceList, state.PropertyPath); } - public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options) + internal IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options) { Type immutableCollectionType = state.Current.JsonPropertyInfo.RuntimePropertyType; Type elementType = state.Current.GetElementType(); @@ -164,7 +179,7 @@ public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options); - return propertyInfo.CreateImmutableCollectionFromList(immutableCollectionType, delegateKey, sourceList, state.PropertyPath); + return propertyInfo.CreateImmutableCollectionFromDictionary(immutableCollectionType, delegateKey, sourceDictionary, state.PropertyPath); } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs index 3f2ddbc68163..82316f5bcad4 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs @@ -25,8 +25,8 @@ private JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInf { JsonPropertyInfo jsonInfo = CreateProperty(propertyType, propertyType, propertyInfo, classType, options); - // Convert non-immutable interfaces to concrete types. - if (propertyType.IsInterface && jsonInfo.ClassType == ClassType.Dictionary && !DefaultImmutableConverter.TypeIsImmutableDictionary(propertyType)) + // Convert non-immutable dictionary interfaces to concrete types. + if (propertyType.IsInterface && jsonInfo.ClassType == ClassType.Dictionary) { // If a polymorphic case, we have to wait until run-time values are processed. if (jsonInfo.ElementClassInfo.ClassType != ClassType.Unknown) @@ -65,6 +65,7 @@ internal static JsonPropertyInfo CreateProperty(Type declaredPropertyType, Type { case ClassType.Enumerable: case ClassType.Dictionary: + case ClassType.ImmutableDictionary: case ClassType.Unknown: collectionElementType = GetElementType(runtimePropertyType, parentClassType, propertyInfo); break; diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs index 4e48a0ece652..3962c81a3c84 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs @@ -117,29 +117,33 @@ internal JsonClassInfo(Type type, JsonSerializerOptions options) DetermineExtensionDataProperty(); break; - case ClassType.Enumerable: case ClassType.Dictionary: - // Add a single property that maps to the class type so we can have policies applied. - JsonPropertyInfo policyProperty = AddPolicyProperty(type, options); - - Type elementType = GetElementType(type, parentType: null, memberInfo: null); - - if (!DefaultImmutableConverter.TypeIsImmutableDictionary(type)) { + // Add a single property that maps to the class type so we can have policies applied. + JsonPropertyInfo policyProperty = AddPolicyProperty(type, options); + // Use the type from the property policy to get any late-bound concrete types (from an interface like IDictionary). CreateObject = options.ClassMaterializerStrategy.CreateConstructor(policyProperty.RuntimePropertyType); + + // Create a ClassInfo that maps to the element type which is used for (de)serialization and policies. + Type elementType = GetElementType(type, parentType: null, memberInfo: null); + ElementClassInfo = options.GetOrAddClass(elementType); } - else + break; + case ClassType.ImmutableDictionary: { - // Since immutable dictionaries do not implement Dictionary<,>, the runtime property type could not be set to - // Dictionary, we have to set CreateObject this way. + // Add a single property that maps to the class type so we can have policies applied. + AddPolicyProperty(type, options); + + Type elementType = GetElementType(type, parentType: null, memberInfo: null); + CreateObject = options.ClassMaterializerStrategy.CreateConstructor( typeof(Dictionary<,>).MakeGenericType(typeof(string), elementType)); - } - // Create a ClassInfo that maps to the element type which is used for (de)serialization and policies. - ElementClassInfo = options.GetOrAddClass(elementType); + // Create a ClassInfo that maps to the element type which is used for (de)serialization and policies. + ElementClassInfo = options.GetOrAddClass(elementType); + } break; case ClassType.Value: case ClassType.Unknown: @@ -384,7 +388,7 @@ public static Type GetElementType(Type propertyType, Type parentType, MemberInfo Type[] args = propertyType.GetGenericArguments(); ClassType classType = GetClassType(propertyType); - if (classType == ClassType.Dictionary && + if ((classType == ClassType.Dictionary || classType == ClassType.ImmutableDictionary) && args.Length >= 2 && // It is >= 2 in case there is a IDictionary. args[0].UnderlyingSystemType == typeof(string)) { @@ -414,10 +418,14 @@ internal static ClassType GetClassType(Type type) return ClassType.Value; } + if (DefaultImmutableConverter.TypeIsImmutableDictionary(type)) + { + return ClassType.ImmutableDictionary; + } + if (typeof(IDictionary).IsAssignableFrom(type) || (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(IDictionary<,>) || - type.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>))) || - DefaultImmutableConverter.TypeIsImmutableDictionary(type)) + type.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>)))) { return ClassType.Dictionary; } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs index 004c4ccb1655..5ca376d19e70 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs @@ -73,7 +73,7 @@ public JsonClassInfo ElementClassInfo { if (_elementClassInfo == null && _elementType != null) { - Debug.Assert(ClassType == ClassType.Enumerable || ClassType == ClassType.Dictionary); + Debug.Assert(ClassType == ClassType.Enumerable || ClassType == ClassType.Dictionary || ClassType == ClassType.ImmutableDictionary); _elementClassInfo = Options.GetOrAddClass(_elementType); } @@ -205,7 +205,7 @@ private void DeterminePropertyName() private void DetermineSerializationCapabilities() { - if (ClassType != ClassType.Enumerable && ClassType != ClassType.Dictionary) + if (ClassType != ClassType.Enumerable && ClassType != ClassType.Dictionary && ClassType != ClassType.ImmutableDictionary) { // We serialize if there is a getter + not ignoring readonly properties. ShouldSerialize = HasGetter && (HasSetter || !Options.IgnoreReadOnlyProperties); @@ -226,14 +226,6 @@ private void DetermineSerializationCapabilities() { ShouldDeserialize = true; } - - if (ClassType == ClassType.Dictionary && - ShouldDeserialize && DefaultImmutableConverter.TypeIsImmutableDictionary(RuntimePropertyType)) - { - DefaultImmutableConverter.RegisterImmutableCollectionType( - RuntimePropertyType, JsonClassInfo.GetElementType(RuntimePropertyType, parentType: null, memberInfo: null), Options); - EnumerableConverter = s_jsonImmutableConverter; - } } //else if (HasSetter) //{ @@ -248,6 +240,12 @@ private void DetermineSerializationCapabilities() { EnumerableConverter = s_jsonArrayConverter; } + else if (ClassType == ClassType.ImmutableDictionary) + { + DefaultImmutableConverter.RegisterImmutableDictionary( + RuntimePropertyType, JsonClassInfo.GetElementType(RuntimePropertyType, parentType: null, memberInfo: null), Options); + EnumerableConverter = s_jsonImmutableConverter; + } else if (typeof(IEnumerable).IsAssignableFrom(RuntimePropertyType)) { Type elementType = JsonClassInfo.GetElementType(RuntimePropertyType, ParentClassType, PropertyInfo); @@ -270,7 +268,7 @@ private void DetermineSerializationCapabilities() RuntimePropertyType.FullName.StartsWith(DefaultImmutableConverter.ImmutableNamespace) && RuntimePropertyType.GetGenericArguments().Length == 1) { - DefaultImmutableConverter.RegisterImmutableCollectionType(RuntimePropertyType, elementType, Options); + DefaultImmutableConverter.RegisterImmutableCollection(RuntimePropertyType, elementType, Options); EnumerableConverter = s_jsonImmutableConverter; } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs index bebfab2d4be4..fd6271d299ff 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs @@ -188,18 +188,24 @@ internal static void ApplyObjectToEnumerable( } else if (state.Current.IsDictionary || (state.Current.IsDictionaryProperty && !setPropertyDirectly)) { - object dictionaryObject = null; - if (state.Current.ReturnValue != null) + Debug.Assert(state.Current.ReturnValue != null); + IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue); + + string key = state.Current.KeyName; + Debug.Assert(!string.IsNullOrEmpty(key)); + if (!dictionary.Contains(key)) { - dictionaryObject = state.Current.ReturnValue; + dictionary.Add(key, value); } - else if (state.Current.TempDictionaryValues != null) + else { - dictionaryObject = state.Current.TempDictionaryValues; + ThrowHelper.ThrowJsonException_DeserializeDuplicateKey(key, reader, state.PropertyPath); } - Debug.Assert(dictionaryObject != null); - - IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(dictionaryObject); + } + else if (state.Current.IsImmutableDictionary || (state.Current.IsImmutableDictionaryProperty && !setPropertyDirectly)) + { + Debug.Assert(state.Current.TempDictionaryValues != null); + IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.TempDictionaryValues); string key = state.Current.KeyName; Debug.Assert(!string.IsNullOrEmpty(key)); @@ -261,18 +267,25 @@ internal static void ApplyValueToEnumerable( } else if (state.Current.IsProcessingDictionary) { - IDictionary dictionary = null; - if (state.Current.TempDictionaryValues != null) + Debug.Assert(state.Current.ReturnValue != null); + IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue); + + string key = state.Current.KeyName; + Debug.Assert(!string.IsNullOrEmpty(key)); + if (!dictionary.ContainsKey(key)) // The IDictionary.TryAdd extension method is not available in netstandard. { - dictionary = (IDictionary)state.Current.TempDictionaryValues; + dictionary.Add(key, value); } - else if (state.Current.ReturnValue != null) + else { - dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue); + ThrowHelper.ThrowJsonException_DeserializeDuplicateKey(key, reader, state.PropertyPath); } + } + else if (state.Current.IsProcessingImmutableDictionary) + { + Debug.Assert(state.Current.TempDictionaryValues != null); + IDictionary dictionary = (IDictionary)state.Current.TempDictionaryValues; - Debug.Assert(dictionary != null); - string key = state.Current.KeyName; Debug.Assert(!string.IsNullOrEmpty(key)); if (!dictionary.ContainsKey(key)) // The IDictionary.TryAdd extension method is not available in netstandard. diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs index a2c4d808f837..3c466304e224 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs @@ -25,7 +25,7 @@ private static void HandleStartDictionary(JsonSerializerOptions options, ref Utf // A nested object or dictionary so push new frame. if (state.Current.PropertyInitialized) { - Debug.Assert(state.Current.IsDictionary); + Debug.Assert(state.Current.IsDictionary || state.Current.IsImmutableDictionary); state.Push(); state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo; diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs index 8b2b7eda1126..df5e15c04827 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs @@ -11,7 +11,7 @@ public static partial class JsonSerializer { private static void HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { - Debug.Assert(!state.Current.IsProcessingDictionary); + Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingImmutableDictionary); if (state.Current.IsProcessingEnumerable) { @@ -42,7 +42,7 @@ private static void HandleStartObject(JsonSerializerOptions options, ref Utf8Jso private static void HandleEndObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { - Debug.Assert(!state.Current.IsProcessingDictionary); + Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingImmutableDictionary); state.Current.JsonClassInfo.UpdateSortedPropertyCache(ref state.Current); diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs index e50307e41b1d..6df9ab02eca3 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs @@ -23,7 +23,7 @@ private static void HandlePropertyName( Debug.Assert(state.Current.ReturnValue != default || state.Current.TempDictionaryValues != default); Debug.Assert(state.Current.JsonClassInfo != default); - if (state.Current.IsProcessingDictionary) + if (state.Current.IsProcessingDictionary || state.Current.IsProcessingImmutableDictionary) { if (ReferenceEquals(state.Current.JsonClassInfo.DataExtensionProperty, state.Current.JsonPropertyInfo)) { @@ -52,8 +52,11 @@ private static void HandlePropertyName( state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.GetPolicyProperty(); } - Debug.Assert(state.Current.IsDictionary || - (state.Current.IsDictionaryProperty && state.Current.JsonPropertyInfo != null)); + Debug.Assert( + state.Current.IsDictionary || + (state.Current.IsDictionaryProperty && state.Current.JsonPropertyInfo != null) || + state.Current.IsImmutableDictionary || + (state.Current.IsImmutableDictionaryProperty && state.Current.JsonPropertyInfo != null)); state.Current.KeyName = keyName; } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs index f95f24b0f653..e4d5409ed981 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs @@ -52,7 +52,7 @@ private static void ReadCore( continue; } } - else if (state.Current.IsProcessingDictionary) + else if (state.Current.IsProcessingDictionary || state.Current.IsProcessingImmutableDictionary) { HandleStartDictionary(options, ref reader, ref state); } @@ -67,7 +67,7 @@ private static void ReadCore( { state.Pop(); } - else if (state.Current.IsProcessingDictionary) + else if (state.Current.IsProcessingDictionary || state.Current.IsProcessingImmutableDictionary) { HandleEndDictionary(options, ref reader, ref state); } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs index 6da35d29c45c..f29dddded09e 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs @@ -95,7 +95,19 @@ private static bool HandleObject( // A property that returns a dictionary keeps the same stack frame. if (jsonPropertyInfo.ClassType == ClassType.Dictionary) { - state.Current.IsImmutableDictionaryProperty = DefaultImmutableConverter.TypeIsImmutableDictionary(jsonPropertyInfo.RuntimePropertyType); + bool endOfEnumerable = HandleDictionary(jsonPropertyInfo.ElementClassInfo, options, writer, ref state); + if (endOfEnumerable) + { + state.Current.NextProperty(); + } + + return endOfEnumerable; + } + + // A property that returns an immutable dictionary keeps the same stack frame. + if (jsonPropertyInfo.ClassType == ClassType.ImmutableDictionary) + { + state.Current.IsImmutableDictionaryProperty = true; bool endOfEnumerable = HandleDictionary(jsonPropertyInfo.ElementClassInfo, options, writer, ref state); if (endOfEnumerable) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.cs index c9a9e4a762bb..40c1ce53bdba 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.cs @@ -38,6 +38,7 @@ private static bool Write( finishedSerializing = WriteObject(options, writer, ref state); break; case ClassType.Dictionary: + case ClassType.ImmutableDictionary: finishedSerializing = HandleDictionary(current.JsonClassInfo.ElementClassInfo, options, writer, ref state); break; default: diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs index c58b75f322e5..c86f75ba3d91 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs @@ -39,15 +39,14 @@ internal struct ReadStackFrame // The current JSON data for a property does not match a given POCO, so ignore the property (recursively). public bool Drain; - public bool IsImmutableDictionary => DefaultImmutableConverter.TypeIsImmutableDictionary(JsonClassInfo.Type); + public bool IsImmutableDictionary => JsonClassInfo.ClassType == ClassType.ImmutableDictionary; public bool IsDictionary => JsonClassInfo.ClassType == ClassType.Dictionary; public bool IsDictionaryProperty => JsonPropertyInfo != null && !JsonPropertyInfo.IsPropertyPolicy && JsonPropertyInfo.ClassType == ClassType.Dictionary; public bool IsImmutableDictionaryProperty => JsonPropertyInfo != null && - !JsonPropertyInfo.IsPropertyPolicy && - DefaultImmutableConverter.TypeIsImmutableDictionary(JsonPropertyInfo.RuntimePropertyType); + !JsonPropertyInfo.IsPropertyPolicy && (JsonPropertyInfo.RuntimeClassInfo.ClassType == ClassType.ImmutableDictionary); public bool IsEnumerable => JsonClassInfo.ClassType == ClassType.Enumerable; @@ -90,7 +89,10 @@ public void Initialize(Type type, JsonSerializerOptions options) public void InitializeJsonPropertyInfo() { - if (JsonClassInfo.ClassType == ClassType.Value || JsonClassInfo.ClassType == ClassType.Enumerable || JsonClassInfo.ClassType == ClassType.Dictionary) + if (JsonClassInfo.ClassType == ClassType.Value || + JsonClassInfo.ClassType == ClassType.Enumerable || + JsonClassInfo.ClassType == ClassType.Dictionary || + JsonClassInfo.ClassType == ClassType.ImmutableDictionary) { JsonPropertyInfo = JsonClassInfo.GetPolicyProperty(); } @@ -160,12 +162,12 @@ public static object CreateEnumerableValue(ref Utf8JsonReader reader, ref ReadSt public Type GetElementType() { - if (IsEnumerableProperty || IsDictionaryProperty) + if (IsEnumerableProperty || IsDictionaryProperty || IsImmutableDictionaryProperty) { return JsonPropertyInfo.ElementClassInfo.Type; } - if (IsEnumerable || IsDictionary) + if (IsEnumerable || IsDictionary || IsImmutableDictionary) { return JsonClassInfo.ElementClassInfo.Type; } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs index 7680d5084035..4eedc270486f 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs @@ -48,11 +48,16 @@ public void Push(JsonClassInfo nextClassInfo, object nextValue) ClassType classType = nextClassInfo.ClassType; if (classType == ClassType.Enumerable || nextClassInfo.ClassType == ClassType.Dictionary) + { + Current.PopStackOnEnd = true; + Current.JsonPropertyInfo = Current.JsonClassInfo.GetPolicyProperty(); + } + else if (classType == ClassType.ImmutableDictionary) { Current.PopStackOnEnd = true; Current.JsonPropertyInfo = Current.JsonClassInfo.GetPolicyProperty(); - Current.IsImmutableDictionary = DefaultImmutableConverter.TypeIsImmutableDictionary(nextClassInfo.Type); + Current.IsImmutableDictionary = true; } else { diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs index 96d16b9c16f5..737a40650cbe 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs @@ -46,7 +46,11 @@ public void Initialize(Type type, JsonSerializerOptions options) if (JsonClassInfo.ClassType == ClassType.Value || JsonClassInfo.ClassType == ClassType.Enumerable || JsonClassInfo.ClassType == ClassType.Dictionary) { JsonPropertyInfo = JsonClassInfo.GetPolicyProperty(); - IsImmutableDictionary = DefaultImmutableConverter.TypeIsImmutableDictionary(type); + } + else if (JsonClassInfo.ClassType == ClassType.ImmutableDictionary) + { + JsonPropertyInfo = JsonClassInfo.GetPolicyProperty(); + IsImmutableDictionary = true; } } @@ -81,7 +85,7 @@ public void WriteObjectOrArrayStart(ClassType classType, Utf8JsonWriter writer, Debug.Assert(writeNull == false); // Write start without a property name. - if (classType == ClassType.Object || classType == ClassType.Dictionary) + if (classType == ClassType.Object || classType == ClassType.Dictionary || classType == ClassType.ImmutableDictionary) { writer.WriteStartObject(); StartObjectWritten = true; @@ -100,7 +104,9 @@ private void WriteObjectOrArrayStart(ClassType classType, ReadOnlySpan pro { writer.WriteNull(propertyName); } - else if (classType == ClassType.Object || classType == ClassType.Dictionary) + else if (classType == ClassType.Object || + classType == ClassType.Dictionary || + classType == ClassType.ImmutableDictionary) { writer.WriteStartObject(propertyName); StartObjectWritten = true; diff --git a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs index 4bd3612c2d8d..0719a80f0c24 100644 --- a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs +++ b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs @@ -17,41 +17,41 @@ public static void DictionaryOfString() const string JsonString = @"{""Hello"":""World"",""Hello2"":""World2""}"; const string ReorderedJsonString = @"{""Hello2"":""World2"",""Hello"":""World""}"; - { - Dictionary obj = JsonSerializer.Parse>(JsonString); - Assert.Equal("World", obj["Hello"]); - Assert.Equal("World2", obj["Hello2"]); + //{ + // Dictionary obj = JsonSerializer.Parse>(JsonString); + // Assert.Equal("World", obj["Hello"]); + // Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); + // string json = JsonSerializer.ToString(obj); + // Assert.Equal(JsonString, json); - json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); - } + // json = JsonSerializer.ToString(obj); + // Assert.Equal(JsonString, json); + //} - { - IDictionary obj = JsonSerializer.Parse>(JsonString); - Assert.Equal("World", obj["Hello"]); - Assert.Equal("World2", obj["Hello2"]); + //{ + // IDictionary obj = JsonSerializer.Parse>(JsonString); + // Assert.Equal("World", obj["Hello"]); + // Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); + // string json = JsonSerializer.ToString(obj); + // Assert.Equal(JsonString, json); - json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); - } + // json = JsonSerializer.ToString(obj); + // Assert.Equal(JsonString, json); + //} - { - IReadOnlyDictionary obj = JsonSerializer.Parse>(JsonString); - Assert.Equal("World", obj["Hello"]); - Assert.Equal("World2", obj["Hello2"]); + //{ + // IReadOnlyDictionary obj = JsonSerializer.Parse>(JsonString); + // Assert.Equal("World", obj["Hello"]); + // Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); + // string json = JsonSerializer.ToString(obj); + // Assert.Equal(JsonString, json); - json = JsonSerializer.ToString(obj); - Assert.Equal(JsonString, json); - } + // json = JsonSerializer.ToString(obj); + // Assert.Equal(JsonString, json); + //} { ImmutableDictionary obj = JsonSerializer.Parse>(JsonString); @@ -65,29 +65,29 @@ public static void DictionaryOfString() Assert.True(JsonString == json || ReorderedJsonString == json); } - { - IImmutableDictionary obj = JsonSerializer.Parse>(JsonString); - Assert.Equal("World", obj["Hello"]); - Assert.Equal("World2", obj["Hello2"]); + //{ + // IImmutableDictionary obj = JsonSerializer.Parse>(JsonString); + // Assert.Equal("World", obj["Hello"]); + // Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.ToString(obj); - Assert.True(JsonString == json || ReorderedJsonString == json); + // string json = JsonSerializer.ToString(obj); + // Assert.True(JsonString == json || ReorderedJsonString == json); - json = JsonSerializer.ToString(obj); - Assert.True(JsonString == json || ReorderedJsonString == json); - } + // json = JsonSerializer.ToString(obj); + // Assert.True(JsonString == json || ReorderedJsonString == json); + //} - { - ImmutableSortedDictionary obj = JsonSerializer.Parse>(JsonString); - Assert.Equal("World", obj["Hello"]); - Assert.Equal("World2", obj["Hello2"]); + //{ + // ImmutableSortedDictionary obj = JsonSerializer.Parse>(JsonString); + // Assert.Equal("World", obj["Hello"]); + // Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.ToString(obj); - Assert.True(JsonString == json); + // string json = JsonSerializer.ToString(obj); + // Assert.True(JsonString == json); - json = JsonSerializer.ToString(obj); - Assert.True(JsonString == json); - } + // json = JsonSerializer.ToString(obj); + // Assert.True(JsonString == json); + //} } [Fact] From a090ec0346fa2018ac329d06ed4e4e4558d5e302 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Wed, 29 May 2019 15:57:38 -0400 Subject: [PATCH 09/13] More immutable support --- .../Json/Serialization/JsonSerializer.Read.HandleArray.cs | 2 +- .../Serialization/JsonSerializer.Read.HandleDictionary.cs | 2 +- .../Json/Serialization/JsonSerializer.Read.HandleNull.cs | 4 ++-- .../JsonSerializer.Read.HandlePropertyName.cs | 2 +- .../src/System/Text/Json/Serialization/ReadStackFrame.cs | 8 +++++--- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs index fd6271d299ff..623140cb572f 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs @@ -128,7 +128,7 @@ private static bool HandleEndArray( state.Current.ReturnValue = value; return true; } - else if (state.Current.IsEnumerable || state.Current.IsDictionary) + else if (state.Current.IsEnumerable || state.Current.IsDictionary || state.Current.IsImmutableDictionary) { // Returning a non-converted list. return true; diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs index 3c466304e224..845349242acc 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs @@ -84,7 +84,7 @@ private static void HandleStartDictionary(JsonSerializerOptions options, ref Utf private static void HandleEndDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { - if (state.Current.IsDictionaryProperty) + if (state.Current.IsImmutableDictionaryProperty) { if (state.Current.TempDictionaryValues != null) { diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs index b5b9e5f9c503..5ec4d59ffa4f 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs @@ -29,13 +29,13 @@ private static bool HandleNull(ref Utf8JsonReader reader, ref ReadStack state, J ThrowHelper.ThrowJsonException_DeserializeCannotBeNull(reader, state.PropertyPath); } - if (state.Current.IsEnumerable || state.Current.IsDictionary) + if (state.Current.IsEnumerable || state.Current.IsDictionary || state.Current.IsImmutableDictionary) { ApplyObjectToEnumerable(null, ref state, ref reader); return false; } - if (state.Current.IsEnumerableProperty || state.Current.IsDictionaryProperty) + if (state.Current.IsEnumerableProperty || state.Current.IsDictionaryProperty || state.Current.IsImmutableDictionaryProperty) { bool setPropertyToNull = !state.Current.PropertyInitialized; ApplyObjectToEnumerable(null, ref state, ref reader, setPropertyDirectly: setPropertyToNull); diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs index 6df9ab02eca3..59e844b075bc 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs @@ -47,7 +47,7 @@ private static void HandlePropertyName( keyName = options.DictionaryKeyPolicy.ConvertName(keyName); } - if (state.Current.IsDictionary) + if (state.Current.IsDictionary || state.Current.IsImmutableDictionary) { state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.GetPolicyProperty(); } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs index c86f75ba3d91..b9c0ea804a13 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs @@ -46,7 +46,7 @@ internal struct ReadStackFrame !JsonPropertyInfo.IsPropertyPolicy && JsonPropertyInfo.ClassType == ClassType.Dictionary; public bool IsImmutableDictionaryProperty => JsonPropertyInfo != null && - !JsonPropertyInfo.IsPropertyPolicy && (JsonPropertyInfo.RuntimeClassInfo.ClassType == ClassType.ImmutableDictionary); + !JsonPropertyInfo.IsPropertyPolicy && (JsonPropertyInfo.ClassType == ClassType.ImmutableDictionary); public bool IsEnumerable => JsonClassInfo.ClassType == ClassType.Enumerable; @@ -56,7 +56,7 @@ internal struct ReadStackFrame JsonPropertyInfo.ClassType == ClassType.Enumerable; public bool IsProcessingImmutableDictionary => IsImmutableDictionary || IsImmutableDictionaryProperty; - public bool IsProcessingEnumerableOrDictionary => IsProcessingEnumerable || IsDictionary; + public bool IsProcessingEnumerableOrDictionary => IsProcessingEnumerable || IsDictionary || IsImmutableDictionary; public bool IsProcessingDictionary => IsDictionary || IsDictionaryProperty; public bool IsProcessingEnumerable => IsEnumerable || IsEnumerableProperty; @@ -76,7 +76,9 @@ public bool IsProcessingValue return type == ClassType.Value || type == ClassType.Unknown || KeyName != null && ( (IsDictionary && JsonClassInfo.ElementClassInfo.ClassType == ClassType.Unknown) || - (IsDictionaryProperty && JsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Unknown) + (IsDictionaryProperty && JsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Unknown) || + (IsImmutableDictionary && JsonClassInfo.ElementClassInfo.ClassType == ClassType.Unknown) || + (IsImmutableDictionaryProperty && JsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Unknown) ); } } From eccda717422f7ccb60cdec3c9cf5aa1e21658f63 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Thu, 30 May 2019 14:42:34 -0400 Subject: [PATCH 10/13] Modify processing enumerable or dictionary check --- .../src/System/Text/Json/Serialization/ReadStackFrame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs index b9c0ea804a13..0ed3c4a9fb6e 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs @@ -55,9 +55,9 @@ internal struct ReadStackFrame !JsonPropertyInfo.IsPropertyPolicy && JsonPropertyInfo.ClassType == ClassType.Enumerable; - public bool IsProcessingImmutableDictionary => IsImmutableDictionary || IsImmutableDictionaryProperty; - public bool IsProcessingEnumerableOrDictionary => IsProcessingEnumerable || IsDictionary || IsImmutableDictionary; + public bool IsProcessingEnumerableOrDictionary => IsProcessingEnumerable || IsProcessingDictionary || IsProcessingImmutableDictionary; public bool IsProcessingDictionary => IsDictionary || IsDictionaryProperty; + public bool IsProcessingImmutableDictionary => IsImmutableDictionary || IsImmutableDictionaryProperty; public bool IsProcessingEnumerable => IsEnumerable || IsEnumerableProperty; public bool IsProcessingValue From 97a556225f8a07b5bb8f36e81c366c2323234ada Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Thu, 30 May 2019 15:18:31 -0400 Subject: [PATCH 11/13] Correct handle end dictionary checks --- .../JsonSerializer.Read.HandleDictionary.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs index 845349242acc..00bd1206b546 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs @@ -84,13 +84,15 @@ private static void HandleStartDictionary(JsonSerializerOptions options, ref Utf private static void HandleEndDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { - if (state.Current.IsImmutableDictionaryProperty) + if (state.Current.IsDictionaryProperty) { - if (state.Current.TempDictionaryValues != null) - { - state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, CreateImmutableDictionaryFromTempValues(ref state, options)); - } - + // We added the items to the dictionary already. + state.Current.ResetProperty(); + } + else if (state.Current.IsImmutableDictionaryProperty) + { + Debug.Assert(state.Current.TempDictionaryValues != null); + state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, CreateImmutableDictionaryFromTempValues(ref state, options)); state.Current.ResetProperty(); } else From a6c9062441985938cfbd7b554a54c5d04c9ce700 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Thu, 30 May 2019 16:34:51 -0400 Subject: [PATCH 12/13] Re-add dict tests --- .../tests/Serialization/DictionaryTests.cs | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs index 0719a80f0c24..4bd3612c2d8d 100644 --- a/src/System.Text.Json/tests/Serialization/DictionaryTests.cs +++ b/src/System.Text.Json/tests/Serialization/DictionaryTests.cs @@ -17,41 +17,41 @@ public static void DictionaryOfString() const string JsonString = @"{""Hello"":""World"",""Hello2"":""World2""}"; const string ReorderedJsonString = @"{""Hello2"":""World2"",""Hello"":""World""}"; - //{ - // Dictionary obj = JsonSerializer.Parse>(JsonString); - // Assert.Equal("World", obj["Hello"]); - // Assert.Equal("World2", obj["Hello2"]); + { + Dictionary obj = JsonSerializer.Parse>(JsonString); + Assert.Equal("World", obj["Hello"]); + Assert.Equal("World2", obj["Hello2"]); - // string json = JsonSerializer.ToString(obj); - // Assert.Equal(JsonString, json); + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); - // json = JsonSerializer.ToString(obj); - // Assert.Equal(JsonString, json); - //} + json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + } - //{ - // IDictionary obj = JsonSerializer.Parse>(JsonString); - // Assert.Equal("World", obj["Hello"]); - // Assert.Equal("World2", obj["Hello2"]); + { + IDictionary obj = JsonSerializer.Parse>(JsonString); + Assert.Equal("World", obj["Hello"]); + Assert.Equal("World2", obj["Hello2"]); - // string json = JsonSerializer.ToString(obj); - // Assert.Equal(JsonString, json); + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); - // json = JsonSerializer.ToString(obj); - // Assert.Equal(JsonString, json); - //} + json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + } - //{ - // IReadOnlyDictionary obj = JsonSerializer.Parse>(JsonString); - // Assert.Equal("World", obj["Hello"]); - // Assert.Equal("World2", obj["Hello2"]); + { + IReadOnlyDictionary obj = JsonSerializer.Parse>(JsonString); + Assert.Equal("World", obj["Hello"]); + Assert.Equal("World2", obj["Hello2"]); - // string json = JsonSerializer.ToString(obj); - // Assert.Equal(JsonString, json); + string json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); - // json = JsonSerializer.ToString(obj); - // Assert.Equal(JsonString, json); - //} + json = JsonSerializer.ToString(obj); + Assert.Equal(JsonString, json); + } { ImmutableDictionary obj = JsonSerializer.Parse>(JsonString); @@ -65,29 +65,29 @@ public static void DictionaryOfString() Assert.True(JsonString == json || ReorderedJsonString == json); } - //{ - // IImmutableDictionary obj = JsonSerializer.Parse>(JsonString); - // Assert.Equal("World", obj["Hello"]); - // Assert.Equal("World2", obj["Hello2"]); + { + IImmutableDictionary obj = JsonSerializer.Parse>(JsonString); + Assert.Equal("World", obj["Hello"]); + Assert.Equal("World2", obj["Hello2"]); - // string json = JsonSerializer.ToString(obj); - // Assert.True(JsonString == json || ReorderedJsonString == json); + string json = JsonSerializer.ToString(obj); + Assert.True(JsonString == json || ReorderedJsonString == json); - // json = JsonSerializer.ToString(obj); - // Assert.True(JsonString == json || ReorderedJsonString == json); - //} + json = JsonSerializer.ToString(obj); + Assert.True(JsonString == json || ReorderedJsonString == json); + } - //{ - // ImmutableSortedDictionary obj = JsonSerializer.Parse>(JsonString); - // Assert.Equal("World", obj["Hello"]); - // Assert.Equal("World2", obj["Hello2"]); + { + ImmutableSortedDictionary obj = JsonSerializer.Parse>(JsonString); + Assert.Equal("World", obj["Hello"]); + Assert.Equal("World2", obj["Hello2"]); - // string json = JsonSerializer.ToString(obj); - // Assert.True(JsonString == json); + string json = JsonSerializer.ToString(obj); + Assert.True(JsonString == json); - // json = JsonSerializer.ToString(obj); - // Assert.True(JsonString == json); - //} + json = JsonSerializer.ToString(obj); + Assert.True(JsonString == json); + } } [Fact] From 4ad4770a61128fa7062446edd5109e71f0e1f382 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Fri, 31 May 2019 09:24:04 -0400 Subject: [PATCH 13/13] React to changes from master --- .../Serialization/Converters/DefaultImmutableConverter.cs | 4 ++-- .../Json/Serialization/JsonSerializer.Read.HandleArray.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs index d1290855e582..d4a06b6bc5e0 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableConverter.cs @@ -166,7 +166,7 @@ public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options); - return propertyInfo.CreateImmutableCollectionFromList(immutableCollectionType, delegateKey, sourceList, state.PropertyPath); + return propertyInfo.CreateImmutableCollectionFromList(immutableCollectionType, delegateKey, sourceList, state.JsonPath); } internal IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options) @@ -179,7 +179,7 @@ internal IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourc JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo; JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options); - return propertyInfo.CreateImmutableCollectionFromDictionary(immutableCollectionType, delegateKey, sourceDictionary, state.PropertyPath); + return propertyInfo.CreateImmutableCollectionFromDictionary(immutableCollectionType, delegateKey, sourceDictionary, state.JsonPath); } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs index 3e9152f3402f..cd5668e47c08 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs @@ -215,7 +215,7 @@ internal static void ApplyObjectToEnumerable( } else { - ThrowHelper.ThrowJsonException_DeserializeDuplicateKey(key, reader, state.PropertyPath); + ThrowHelper.ThrowJsonException_DeserializeDuplicateKey(key, reader, state.JsonPath); } } else @@ -294,7 +294,7 @@ internal static void ApplyValueToEnumerable( } else { - ThrowHelper.ThrowJsonException_DeserializeDuplicateKey(key, reader, state.PropertyPath); + ThrowHelper.ThrowJsonException_DeserializeDuplicateKey(key, reader, state.JsonPath); } } else