<data name="DeserializeDuplicateKey" xml:space="preserve">
<value>An item with the same property name '{0}' has already been added.</value>
</data>
- <data name="DeserializeTypeNotSupported" xml:space="preserve">
- <value>Deserialization of type {0} is not supported.</value>
- </data>
<data name="SerializationDataExtensionPropertyInvalid" xml:space="preserve">
<value>The data extension property '{0}.{1}' does not match the required signature of IDictionary<string, JsonElement> or IDictionary<string, object>.</value>
</data>
<Compile Include="System\Text\Json\Serialization\Converters\DefaultArrayConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\DefaultConverters.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\DefaultConvertersOfTValue.cs" />
- <Compile Include="System\Text\Json\Serialization\Converters\DefaultEnumerableConverter.cs" />
- <Compile Include="System\Text\Json\Serialization\Converters\DefaultIEnumerableConstructibleConverter.cs" />
- <Compile Include="System\Text\Json\Serialization\Converters\DefaultImmutableConverter.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\DefaultICollectionConverter.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\DefaultIDictionaryConverter.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\DefaultImmutableEnumerableConverter.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\DefaultImmutableDictionaryConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterBoolean.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterByte.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterByteArray.cs" />
<Compile Include="System\Text\Json\Serialization\JsonCamelCaseNamePolicy.cs" />
<Compile Include="System\Text\Json\Serialization\JsonClassInfo.cs" />
<Compile Include="System\Text\Json\Serialization\JsonClassInfo.AddProperty.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonDictionaryConverter.cs" />
<Compile Include="System\Text\Json\Serialization\JsonEnumerableConverter.cs" />
<Compile Include="System\Text\Json\Serialization\JsonExtensionDataAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\JsonIgnoreAttribute.cs" />
/// </summary>
internal enum ClassType
{
- Unknown = 0, // typeof(object)
- Object = 1, // POCO or rich data type
- Value = 2, // Data type with single value
- Enumerable = 3, // IEnumerable
- Dictionary = 4, // IDictionary
- ImmutableDictionary = 5, // Immutable Dictionary
+ // typeof(object)
+ Unknown = 0,
+ // POCO or rich data type
+ Object = 1,
+ // Data type with single value
+ Value = 2,
+ // IEnumerable
+ Enumerable = 3,
+ // IDictionary
+ Dictionary = 4,
+ // Is deserialized by passing a IDictionary to its constructor
+ // i.e. immutable dictionaries, Hashtable, SortedList,
+ IDictionaryConstructible = 5,
+ // KeyValuePair
+ // TODO: Utilize converter mechanism to handle (de)serialization of KeyValuePair
+ // when it goes through: https://github.com/dotnet/corefx/issues/36639.
+ KeyValuePair = 6,
}
}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// 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.Text.Json.Serialization.Policies;
-
-namespace System.Text.Json.Serialization.Converters
-{
- internal class JsonEnumerableT<T> : ICollection<T>, IEnumerable<T>, IList<T>, IReadOnlyCollection<T>, IReadOnlyList<T>
- {
- List<T> _list;
-
- public JsonEnumerableT(IList sourceList)
- {
- // TODO: Change sourceList from IList to List<T> so we can do a direct assignment here.
- _list = new List<T>();
-
- foreach (object item in sourceList)
- {
- _list.Add((T)item);
- }
- }
-
- public T this[int index] { get => (T)_list[index]; set => _list[index] = value; }
-
- public int Count => _list.Count;
-
- public bool IsReadOnly => false;
-
- public void Add(T item)
- {
- _list.Add(item);
- }
-
- public void Clear()
- {
- _list.Clear();
- }
-
- public bool Contains(T item)
- {
- return _list.Contains(item);
- }
-
- public void CopyTo(T[] array, int arrayIndex)
- {
- _list.CopyTo(array, arrayIndex);
- }
-
- public IEnumerator<T> GetEnumerator()
- {
- return _list.GetEnumerator();
- }
-
- public int IndexOf(T item)
- {
- return _list.IndexOf(item);
- }
-
- public void Insert(int index, T item)
- {
- _list.Insert(index, item);
- }
-
- public bool Remove(T item)
- {
- return _list.Remove(item);
- }
-
- public void RemoveAt(int index)
- {
- _list.RemoveAt(index);
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
-
- internal sealed class DefaultEnumerableConverter : JsonEnumerableConverter
- {
- public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options)
- {
- Type elementType = state.Current.GetElementType();
-
- Type t = typeof(JsonEnumerableT<>).MakeGenericType(elementType);
- return (IEnumerable)Activator.CreateInstance(t, sourceList);
- }
- }
-}
// See the LICENSE file in the project root for more information.
using System.Collections;
-using System.Collections.Concurrent;
using System.Text.Json.Serialization.Policies;
namespace System.Text.Json.Serialization.Converters
{
- internal sealed class DefaultIEnumerableConstructibleConverter : JsonEnumerableConverter
+ internal sealed class DefaultICollectionConverter : JsonEnumerableConverter
{
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 = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options);
- return propertyInfo.CreateIEnumerableConstructibleType(enumerableType, sourceList);
+ return propertyInfo.CreateIEnumerableInstance(enumerableType, sourceList, state.JsonPath, options);
}
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class DefaultIDictionaryConverter : JsonDictionaryConverter
+ {
+ public override IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options)
+ {
+ Type dictionaryType = state.Current.JsonPropertyInfo.RuntimePropertyType;
+ JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo;
+ JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options);
+ return propertyInfo.CreateIDictionaryInstance(dictionaryType, sourceDictionary, state.JsonPath, options);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class DefaultImmutableDictionaryConverter : JsonDictionaryConverter
+ {
+ private const string ImmutableDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableDictionary`2";
+ private const string ImmutableDictionaryGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableDictionary`2";
+ private const string ImmutableSortedDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableSortedDictionary`2";
+
+ public 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 = DefaultImmutableEnumerableConverter.GetDelegateKey(immutableCollectionType, elementType, out Type underlyingType, out string constructingTypeName);
+
+ // Exit if we have registered this immutable dictionary type.
+ if (options.CreateRangeDelegatesContainsKey(delegateKey))
+ {
+ 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
+ options.TryAddCreateRangeDelegate(delegateKey, createRangeDelegate);
+ }
+
+ public static bool IsImmutableDictionary(Type type)
+ {
+ if (!type.IsGenericType)
+ {
+ return false;
+ }
+
+ switch (type.GetGenericTypeDefinition().FullName)
+ {
+ case ImmutableDictionaryGenericTypeName:
+ case ImmutableDictionaryGenericInterfaceTypeName:
+ case ImmutableSortedDictionaryGenericTypeName:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public override IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options)
+ {
+ Type immutableCollectionType = state.Current.JsonPropertyInfo.RuntimePropertyType;
+ Type elementType = state.Current.GetElementType();
+
+ string delegateKey = DefaultImmutableEnumerableConverter.GetDelegateKey(immutableCollectionType, elementType, out _, out _);
+
+ JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo;
+ JsonPropertyInfo propertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options);
+ return propertyInfo.CreateImmutableDictionaryInstance(immutableCollectionType, delegateKey, sourceDictionary, state.JsonPath, options);
+ }
+ }
+}
// See the LICENSE file in the project root for more information.
using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text.Json.Serialization.Policies;
namespace System.Text.Json.Serialization.Converters
{
// This converter returns enumerables in the System.Collections.Immutable namespace.
- internal sealed class DefaultImmutableConverter : JsonEnumerableConverter
+ internal sealed class DefaultImmutableEnumerableConverter : JsonEnumerableConverter
{
- public const string ImmutableNamespace = "System.Collections.Immutable";
-
private const string ImmutableListTypeName = "System.Collections.Immutable.ImmutableList";
private const string ImmutableListGenericTypeName = "System.Collections.Immutable.ImmutableList`1";
private const string ImmutableListGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableList`1";
private const string ImmutableSortedDictionaryTypeName = "System.Collections.Immutable.ImmutableSortedDictionary";
private const string ImmutableSortedDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableSortedDictionary`2";
- internal delegate object ImmutableCreateRangeDelegate<T>(IEnumerable<T> items);
- internal delegate object ImmutableDictCreateRangeDelegate<TKey, TValue>(IEnumerable<KeyValuePair<TKey, TValue>> items);
-
- private static ConcurrentDictionary<string, object> s_createRangeDelegates = new ConcurrentDictionary<string, object>();
-
- private static string GetConstructingTypeName(string immutableCollectionTypeName)
+ public static string GetDelegateKey(
+ Type immutableCollectionType,
+ Type elementType,
+ out Type underlyingType,
+ out string constructingTypeName)
{
- switch (immutableCollectionTypeName)
+ // 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<elementType>` method on, which returns an assignable immutable collection.
+ underlyingType = immutableCollectionType.GetGenericTypeDefinition();
+
+ switch (underlyingType.FullName)
{
case ImmutableListGenericTypeName:
case ImmutableListGenericInterfaceTypeName:
- return ImmutableListTypeName;
+ constructingTypeName = ImmutableListTypeName;
+ break;
case ImmutableStackGenericTypeName:
case ImmutableStackGenericInterfaceTypeName:
- return ImmutableStackTypeName;
+ constructingTypeName = ImmutableStackTypeName;
+ break;
case ImmutableQueueGenericTypeName:
case ImmutableQueueGenericInterfaceTypeName:
- return ImmutableQueueTypeName;
+ constructingTypeName = ImmutableQueueTypeName;
+ break;
case ImmutableSortedSetGenericTypeName:
- return ImmutableSortedSetTypeName;
+ constructingTypeName = ImmutableSortedSetTypeName;
+ break;
case ImmutableHashSetGenericTypeName:
case ImmutableSetGenericInterfaceTypeName:
- return ImmutableHashSetTypeName;
+ constructingTypeName = ImmutableHashSetTypeName;
+ break;
case ImmutableDictionaryGenericTypeName:
case ImmutableDictionaryGenericInterfaceTypeName:
- return ImmutableDictionaryTypeName;
+ constructingTypeName = ImmutableDictionaryTypeName;
+ break;
case ImmutableSortedDictionaryGenericTypeName:
- return ImmutableSortedDictionaryTypeName;
+ constructingTypeName = ImmutableSortedDictionaryTypeName;
+ break;
default:
- // TODO: Refactor exception throw following serialization exception changes.
- throw new NotSupportedException(SR.Format(SR.DeserializeTypeNotSupported, immutableCollectionTypeName));
+ throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(immutableCollectionType, null, null);
}
- }
-
- 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<elementType>` 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)
- {
- return false;
- }
-
- switch (type.GetGenericTypeDefinition().FullName)
- {
- case ImmutableDictionaryGenericTypeName:
- case ImmutableDictionaryGenericInterfaceTypeName:
- case ImmutableSortedDictionaryGenericTypeName:
- return true;
- default:
- return false;
- }
- }
-
- internal static bool TryGetCreateRangeDelegate(string delegateKey, out object createRangeDelegate)
- {
- return s_createRangeDelegates.TryGetValue(delegateKey, out createRangeDelegate) && createRangeDelegate != null;
- }
-
- internal static void RegisterImmutableCollection(Type immutableCollectionType, Type elementType, JsonSerializerOptions options)
+ public 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);
// Exit if we have registered this immutable collection type.
- if (s_createRangeDelegates.ContainsKey(delegateKey))
+ if (options.CreateRangeDelegatesContainsKey(delegateKey))
{
return;
}
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))
- {
- 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);
+ options.TryAddCreateRangeDelegate(delegateKey, createRangeDelegate);
}
public override IEnumerable CreateFromList(ref ReadStack state, IList sourceList, JsonSerializerOptions options)
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 = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options);
- return propertyInfo.CreateImmutableCollectionFromList(immutableCollectionType, delegateKey, sourceList, state.JsonPath);
- }
-
- internal 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 = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options);
- return propertyInfo.CreateImmutableCollectionFromDictionary(immutableCollectionType, delegateKey, sourceDictionary, state.JsonPath);
+ return propertyInfo.CreateImmutableCollectionInstance(immutableCollectionType, delegateKey, sourceList, state.JsonPath, options);
}
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class DefaultIDictionaryConverter : JsonDictionaryConverter
+ {
+ public override IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options)
+ {
+ Type enumerableType = state.Current.JsonPropertyInfo.RuntimePropertyType;
+ return (IDictionary)Activator.CreateInstance(enumerableType, sourceDictionary);
+ }
+ }
+}
// 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.Reflection;
using System.Text.Json.Serialization;
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)
+ if (jsonInfo.ElementClassInfo.ClassType != ClassType.Unknown || propertyType == typeof(IDictionary))
{
Type newPropertyType = jsonInfo.ElementClassInfo.GetPolicyProperty().GetDictionaryConcreteType();
if (propertyType != newPropertyType)
}
}
}
+ else if (jsonInfo.ClassType == ClassType.Enumerable &&
+ !propertyType.IsArray &&
+ (IsDeserializedByAssigningFromList(propertyType) || IsSetInterface(propertyType)))
+ {
+ JsonClassInfo elementClassInfo = jsonInfo.ElementClassInfo;
+ JsonPropertyInfo elementPropertyInfo = options.GetJsonPropertyInfoFromClassInfo(elementClassInfo, options);
+
+ // Get a runtime type for the property. e.g. ISet<T> -> HashSet<T>, ICollection -> List<object>
+ // We use the element's JsonPropertyInfo so we can utilize the generic support.
+ Type newPropertyType = elementPropertyInfo.GetConcreteType(propertyType);
+ if ((propertyType != newPropertyType) && propertyType.IsAssignableFrom(newPropertyType))
+ {
+ jsonInfo = CreateProperty(propertyType, newPropertyType, propertyInfo, classType, options);
+ }
+ }
if (propertyInfo != null)
{
{
case ClassType.Enumerable:
case ClassType.Dictionary:
- case ClassType.ImmutableDictionary:
+ case ClassType.IDictionaryConstructible:
+ case ClassType.KeyValuePair:
case ClassType.Unknown:
collectionElementType = GetElementType(runtimePropertyType, parentClassType, propertyInfo);
break;
switch (ClassType)
{
case ClassType.Object:
- var propertyNames = new HashSet<string>(StringComparer.Ordinal);
-
- foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
- // Ignore indexers
- if (propertyInfo.GetIndexParameters().Length > 0)
- {
- continue;
- }
+ var propertyNames = new HashSet<string>(StringComparer.Ordinal);
- // For now we only support public getters\setters
- if (propertyInfo.GetMethod?.IsPublic == true ||
- propertyInfo.SetMethod?.IsPublic == true)
+ foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
- JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo.PropertyType, propertyInfo, type, options);
-
- Debug.Assert(jsonPropertyInfo.NameUsedToCompareAsString != null);
-
- // If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception.
- if (!propertyNames.Add(jsonPropertyInfo.NameUsedToCompareAsString))
+ // Ignore indexers
+ if (propertyInfo.GetIndexParameters().Length > 0)
{
- ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(this, jsonPropertyInfo);
+ continue;
}
- jsonPropertyInfo.ClearUnusedValuesAfterAdd();
+ // For now we only support public getters\setters
+ if (propertyInfo.GetMethod?.IsPublic == true ||
+ propertyInfo.SetMethod?.IsPublic == true)
+ {
+ JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo.PropertyType, propertyInfo, type, options);
+
+ Debug.Assert(jsonPropertyInfo.NameUsedToCompareAsString != null);
+
+ // If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception.
+ if (!propertyNames.Add(jsonPropertyInfo.NameUsedToCompareAsString))
+ {
+ ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(this, jsonPropertyInfo);
+ }
+
+ jsonPropertyInfo.ClearUnusedValuesAfterAdd();
+ }
}
- }
- DetermineExtensionDataProperty();
+ DetermineExtensionDataProperty();
+ }
break;
case ClassType.Enumerable:
case ClassType.Dictionary:
ElementClassInfo = options.GetOrAddClass(elementType);
}
break;
- case ClassType.ImmutableDictionary:
+ case ClassType.IDictionaryConstructible:
{
// Add a single property that maps to the class type so we can have policies applied.
AddPolicyProperty(type, options);
ElementClassInfo = options.GetOrAddClass(elementType);
}
break;
+ // TODO: Utilize converter mechanism to handle (de)serialization of KeyValuePair
+ // when it goes through: https://github.com/dotnet/corefx/issues/36639.
+ case ClassType.KeyValuePair:
+ {
+ // For deserialization, we treat it as ClassType.IDictionaryConstructible so we can parse it like a dictionary
+ // before using converter-like logic to create a KeyValuePair instance.
+
+ // 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);
+
+ // Make this Dictionary<string, object> to accomodate input of form {"Key": "MyKey", "Value": 1}.
+ CreateObject = options.ClassMaterializerStrategy.CreateConstructor(typeof(Dictionary<string, object>));
+
+ // Create a ClassInfo that maps to the element type which is used for deserialization and policies.
+ ElementClassInfo = options.GetOrAddClass(elementType);
+
+ // For serialization, we treat it like ClassType.Object to utilize the public getters.
+
+ // Add Key property
+ PropertyInfo propertyInfo = type.GetProperty("Key", BindingFlags.Public | BindingFlags.Instance);
+ JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo.PropertyType, propertyInfo, type, options);
+ Debug.Assert(jsonPropertyInfo.NameUsedToCompareAsString != null);
+ jsonPropertyInfo.ClearUnusedValuesAfterAdd();
+
+ // Add Value property.
+ propertyInfo = type.GetProperty("Value", BindingFlags.Public | BindingFlags.Instance);
+ jsonPropertyInfo = AddProperty(propertyInfo.PropertyType, propertyInfo, type, options);
+ Debug.Assert(jsonPropertyInfo.NameUsedToCompareAsString != null);
+ jsonPropertyInfo.ClearUnusedValuesAfterAdd();
+ }
+ break;
case ClassType.Value:
case ClassType.Unknown:
// Add a single property that maps to the class type so we can have policies applied.
return _propertyRefs[0].Info;
}
+ internal JsonPropertyInfo GetPolicyPropertyOfKeyValuePair()
+ {
+ // We have 3 here. One for the KeyValuePair itself, one for Key property, and one for the Value property.
+ Debug.Assert(_propertyRefs.Count == 3);
+ return _propertyRefs[0].Info;
+ }
+
internal JsonPropertyInfo GetProperty(int index)
{
Debug.Assert(index < _propertyRefs.Count);
return key;
}
- // Return the element type of the IEnumerable, or return null if not an IEnumerable.
+ // Return the element type of the IEnumerable or KeyValuePair, or return null if not an IEnumerable or KayValuePair.
public static Type GetElementType(Type propertyType, Type parentType, MemberInfo memberInfo)
{
- if (!typeof(IEnumerable).IsAssignableFrom(propertyType))
+ if (!typeof(IEnumerable).IsAssignableFrom(propertyType) && !IsKeyValuePair(propertyType))
{
return null;
}
Type[] args = propertyType.GetGenericArguments();
ClassType classType = GetClassType(propertyType);
- if ((classType == ClassType.Dictionary || classType == ClassType.ImmutableDictionary) &&
+ if ((classType == ClassType.Dictionary || classType == ClassType.IDictionaryConstructible || classType == ClassType.KeyValuePair) &&
args.Length >= 2 && // It is >= 2 in case there is a IDictionary<TKey, TValue, TSomeExtension>.
args[0].UnderlyingSystemType == typeof(string))
{
}
}
+ if (propertyType.IsAssignableFrom(typeof(IList)) ||
+ propertyType.IsAssignableFrom(typeof(IDictionary)) ||
+ IsDeserializedByConstructingWithIList(propertyType) ||
+ IsDeserializedByConstructingWithIDictionary(propertyType))
+ {
+ return typeof(object);
+ }
+
throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(propertyType, parentType, memberInfo);
}
- internal static ClassType GetClassType(Type type)
+ public static ClassType GetClassType(Type type)
{
Debug.Assert(type != null);
return ClassType.Value;
}
- if (DefaultImmutableConverter.TypeIsImmutableDictionary(type))
+ if (DefaultImmutableDictionaryConverter.IsImmutableDictionary(type) ||
+ IsDeserializedByConstructingWithIDictionary(type))
{
- return ClassType.ImmutableDictionary;
+ return ClassType.IDictionaryConstructible;
}
- if (typeof(IDictionary).IsAssignableFrom(type) ||
+ if (typeof(IDictionary).IsAssignableFrom(type) ||
(type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(IDictionary<,>) ||
type.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>))))
{
return ClassType.Dictionary;
}
+ if (IsKeyValuePair(type))
+ {
+ return ClassType.KeyValuePair;
+ }
+
if (typeof(IEnumerable).IsAssignableFrom(type))
{
return ClassType.Enumerable;
return ClassType.Object;
}
+
+ public const string ImmutableNamespaceName = "System.Collections.Immutable";
+
+ private const string EnumerableGenericInterfaceTypeName = "System.Collections.Generic.IEnumerable`1";
+ private const string EnumerableInterfaceTypeName = "System.Collections.IEnumerable";
+
+ private const string ListGenericInterfaceTypeName = "System.Collections.Generic.IList`1";
+ private const string ListInterfaceTypeName = "System.Collections.IList";
+
+ private const string CollectionGenericInterfaceTypeName = "System.Collections.Generic.ICollection`1";
+ private const string CollectionInterfaceTypeName = "System.Collections.ICollection";
+
+ private const string ReadOnlyListGenericInterfaceTypeName = "System.Collections.Generic.IReadOnlyList`1";
+
+ private const string ReadOnlyCollectionGenericInterfaceTypeName = "System.Collections.Generic.IReadOnlyCollection`1";
+
+ public const string HashtableTypeName = "System.Collections.Hashtable";
+ public const string SortedListTypeName = "System.Collections.SortedList";
+
+ public const string StackTypeName = "System.Collections.Stack";
+ public const string QueueTypeName = "System.Collections.Queue";
+ public const string ArrayListTypeName = "System.Collections.ArrayList";
+
+ public static bool IsDeserializedByAssigningFromList(Type type)
+ {
+ if (type.IsGenericType)
+ {
+ switch (type.GetGenericTypeDefinition().FullName)
+ {
+ case EnumerableGenericInterfaceTypeName:
+ case ListGenericInterfaceTypeName:
+ case CollectionGenericInterfaceTypeName:
+ case ReadOnlyListGenericInterfaceTypeName:
+ case ReadOnlyCollectionGenericInterfaceTypeName:
+ return true;
+ default:
+ return false;
+ }
+ }
+ else
+ {
+ switch (type.FullName)
+ {
+ case EnumerableInterfaceTypeName:
+ case ListInterfaceTypeName:
+ case CollectionInterfaceTypeName:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+
+ public static bool IsSetInterface(Type type)
+ {
+ return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ISet<>);
+ }
+
+ public static bool HasConstructorThatTakesGenericIEnumerable(Type type)
+ {
+ Type elementType = GetElementType(type, parentType: null, memberInfo: null);
+ return type.GetConstructor(new Type[] { typeof(List<>).MakeGenericType(elementType) }) != null;
+ }
+
+ public static bool IsDeserializedByConstructingWithIList(Type type)
+ {
+ switch (type.FullName)
+ {
+ case StackTypeName:
+ case QueueTypeName:
+ case ArrayListTypeName:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static bool IsDeserializedByConstructingWithIDictionary(Type type)
+ {
+ switch (type.FullName)
+ {
+ case HashtableTypeName:
+ case SortedListTypeName:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static bool IsKeyValuePair(Type type)
+ {
+ return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>);
+ }
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.Text.Json.Serialization.Policies
+{
+ internal abstract class JsonDictionaryConverter
+ {
+ public abstract IDictionary CreateFromDictionary(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options);
+ }
+}
// See the LICENSE file in the project root for more information.
using System.Collections;
-using System.Collections.Generic;
namespace System.Text.Json.Serialization.Policies
{
// 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;
{
// Cache the converters so they don't get created for every enumerable property.
private static readonly JsonEnumerableConverter s_jsonArrayConverter = new DefaultArrayConverter();
- private static readonly JsonEnumerableConverter s_jsonEnumerableConverter = new DefaultEnumerableConverter();
- private static readonly JsonEnumerableConverter s_jsonIEnumerableConstuctibleConverter = new DefaultIEnumerableConstructibleConverter();
- private static readonly JsonEnumerableConverter s_jsonImmutableConverter = new DefaultImmutableConverter();
+ private static readonly JsonEnumerableConverter s_jsonICollectionConverter = new DefaultICollectionConverter();
+ private static readonly JsonDictionaryConverter s_jsonIDictionaryConverter = new DefaultIDictionaryConverter();
+ private static readonly JsonEnumerableConverter s_jsonImmutableEnumerableConverter = new DefaultImmutableEnumerableConverter();
+ private static readonly JsonDictionaryConverter s_jsonImmutableDictionaryConverter = new DefaultImmutableDictionaryConverter();
+
private JsonClassInfo _runtimeClassInfo;
{
if (_elementClassInfo == null && _elementType != null)
{
- Debug.Assert(ClassType == ClassType.Enumerable || ClassType == ClassType.Dictionary || ClassType == ClassType.ImmutableDictionary);
+ Debug.Assert(ClassType == ClassType.Enumerable ||
+ ClassType == ClassType.Dictionary ||
+ ClassType == ClassType.IDictionaryConstructible ||
+ ClassType == ClassType.KeyValuePair);
_elementClassInfo = Options.GetOrAddClass(_elementType);
}
}
public bool CanBeNull { get; private set; }
+
public JsonEnumerableConverter EnumerableConverter { get; private set; }
+ public JsonDictionaryConverter DictionaryConverter { get; private set; }
public bool IsNullableType { get; private set; }
private void DetermineSerializationCapabilities()
{
- if (ClassType != ClassType.Enumerable && ClassType != ClassType.Dictionary && ClassType != ClassType.ImmutableDictionary)
+ if (ClassType != ClassType.Enumerable &&
+ ClassType != ClassType.Dictionary &&
+ ClassType != ClassType.IDictionaryConstructible &&
+ ClassType != ClassType.KeyValuePair)
{
// We serialize if there is a getter + not ignoring readonly properties.
ShouldSerialize = HasGetter && (HasSetter || !Options.IgnoreReadOnlyProperties);
{
EnumerableConverter = s_jsonArrayConverter;
}
- else if (ClassType == ClassType.ImmutableDictionary)
+ else if (ClassType == ClassType.IDictionaryConstructible)
{
- DefaultImmutableConverter.RegisterImmutableDictionary(
- RuntimePropertyType, JsonClassInfo.GetElementType(RuntimePropertyType, parentType: null, memberInfo: null), Options);
- EnumerableConverter = s_jsonImmutableConverter;
+ if (RuntimePropertyType.FullName.StartsWith(JsonClassInfo.ImmutableNamespaceName))
+ {
+ DefaultImmutableDictionaryConverter.RegisterImmutableDictionary(
+ RuntimePropertyType, JsonClassInfo.GetElementType(RuntimePropertyType, ParentClassType, PropertyInfo), Options);
+ DictionaryConverter = s_jsonImmutableDictionaryConverter;
+ }
+ else if (JsonClassInfo.IsDeserializedByConstructingWithIDictionary(RuntimePropertyType))
+ {
+ DictionaryConverter = s_jsonIDictionaryConverter;
+ }
}
else if (typeof(IEnumerable).IsAssignableFrom(RuntimePropertyType))
{
- Type elementType = JsonClassInfo.GetElementType(RuntimePropertyType, ParentClassType, PropertyInfo);
-
- // If the property type only has interface(s) exposed by JsonEnumerableT<T> then use JsonEnumerableT as the converter.
- if (RuntimePropertyType.IsAssignableFrom(typeof(JsonEnumerableT<>).MakeGenericType(elementType)))
- {
- EnumerableConverter = s_jsonEnumerableConverter;
- }
- // Else if IList can't be assigned from the property type (we populate and return an IList directly)
- // and the type can be constructed with an IEnumerable<T>, then use the
- // IEnumerableConstructible converter to create the instance.
- else if (!typeof(IList).IsAssignableFrom(RuntimePropertyType) &&
- RuntimePropertyType.GetConstructor(new Type[] { typeof(List<>).MakeGenericType(elementType) }) != null)
+ if (JsonClassInfo.IsDeserializedByConstructingWithIList(RuntimePropertyType) ||
+ (!typeof(IList).IsAssignableFrom(RuntimePropertyType) && JsonClassInfo.HasConstructorThatTakesGenericIEnumerable(RuntimePropertyType)))
{
- EnumerableConverter = s_jsonIEnumerableConstuctibleConverter;
+ EnumerableConverter = s_jsonICollectionConverter;
}
- // Else if it's a System.Collections.Immutable type with one generic argument.
else if (RuntimePropertyType.IsGenericType &&
- RuntimePropertyType.FullName.StartsWith(DefaultImmutableConverter.ImmutableNamespace) &&
+ RuntimePropertyType.FullName.StartsWith(JsonClassInfo.ImmutableNamespaceName) &&
RuntimePropertyType.GetGenericArguments().Length == 1)
{
- DefaultImmutableConverter.RegisterImmutableCollection(RuntimePropertyType, elementType, Options);
- EnumerableConverter = s_jsonImmutableConverter;
+ DefaultImmutableEnumerableConverter.RegisterImmutableCollection(RuntimePropertyType,
+ JsonClassInfo.GetElementType(RuntimePropertyType, ParentClassType, PropertyInfo),
+ Options);
+ EnumerableConverter = s_jsonImmutableEnumerableConverter;
}
}
}
return (TAttribute)propertyInfo?.GetCustomAttribute(typeof(TAttribute), inherit: false);
}
- public abstract IEnumerable CreateImmutableCollectionFromList(Type collectionType, string delegateKey, IList sourceList, string propertyPath);
+ public abstract IEnumerable CreateIEnumerableInstance(Type parentType, IList sourceList, string jsonPath, JsonSerializerOptions options);
- public abstract IDictionary CreateImmutableCollectionFromDictionary(Type collectionType, string delegateKey, IDictionary sourceDictionary, string propertyPath);
+ public abstract IDictionary CreateIDictionaryInstance(Type parentType, IDictionary sourceDictionary, string jsonPath, JsonSerializerOptions options);
- public abstract IEnumerable CreateIEnumerableConstructibleType(Type enumerableType, IList sourceList);
+ public abstract IEnumerable CreateImmutableCollectionInstance(Type collectionType, string delegateKey, IList sourceList, string propertyPath, JsonSerializerOptions options);
+
+ public abstract IDictionary CreateImmutableDictionaryInstance(Type collectionType, string delegateKey, IDictionary sourceDictionary, string propertyPath, JsonSerializerOptions options);
public abstract IList CreateConverterList();
+ public abstract ValueType CreateKeyValuePairInstance(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options);
+
public abstract Type GetDictionaryConcreteType();
+ public abstract Type GetConcreteType(Type type);
+
public abstract void Read(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader);
public abstract void ReadEnumerable(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader);
public abstract void SetValueAsObject(object obj, object value);
return typeof(Dictionary<string, TRuntimeProperty>);
}
+ public override Type GetConcreteType(Type parentType)
+ {
+ if (JsonClassInfo.IsDeserializedByAssigningFromList(parentType))
+ {
+ return typeof(List<TDeclaredProperty>);
+ }
+ else if (JsonClassInfo.IsSetInterface(parentType))
+ {
+ return typeof(HashSet<TDeclaredProperty>);
+ }
+
+ return parentType;
+ }
+
+ public override IEnumerable CreateIEnumerableInstance(Type parentType, IList sourceList, string jsonPath, JsonSerializerOptions options)
+ {
+ if (parentType.IsGenericType)
+ {
+ Type genericTypeDefinition = parentType.GetGenericTypeDefinition();
+ IEnumerable<TDeclaredProperty> items = CreateGenericTDeclaredPropertyIEnumerable(sourceList);
+
+ if (genericTypeDefinition == typeof(Stack<>))
+ {
+ return new Stack<TDeclaredProperty>(items);
+ }
+ else if (genericTypeDefinition == typeof(Queue<>))
+ {
+ return new Queue<TDeclaredProperty>(items);
+ }
+ else if (genericTypeDefinition == typeof(HashSet<>))
+ {
+ return new HashSet<TDeclaredProperty>(items);
+ }
+ else if (genericTypeDefinition == typeof(LinkedList<>))
+ {
+ return new LinkedList<TDeclaredProperty>(items);
+ }
+ else if (genericTypeDefinition == typeof(SortedSet<>))
+ {
+ return new SortedSet<TDeclaredProperty>(items);
+ }
+
+ return (IEnumerable)Activator.CreateInstance(parentType, items);
+ }
+ else
+ {
+ if (parentType == typeof(ArrayList))
+ {
+ return new ArrayList(sourceList);
+ }
+ // Stack and Queue go into this condition, unless we add a ref to System.Collections.NonGeneric.
+ else
+ {
+ return (IEnumerable)Activator.CreateInstance(parentType, sourceList);
+ }
+ }
+ }
+
+ public override IDictionary CreateIDictionaryInstance(Type parentType, IDictionary sourceDictionary, string jsonPath, JsonSerializerOptions options)
+ {
+ if (parentType.FullName == JsonClassInfo.HashtableTypeName)
+ {
+ return new Hashtable(sourceDictionary);
+ }
+ // SortedList goes into this condition, unless we add a ref to System.Collections.NonGeneric.
+ else
+ {
+ return (IDictionary)Activator.CreateInstance(parentType, sourceDictionary);
+ }
+ }
+
// Creates an IEnumerable<TRuntimePropertyType> and populates it with the items in the
// sourceList argument then uses the delegateKey argument to identify the appropriate cached
// CreateRange<TRuntimePropertyType> method to create and return the desired immutable collection type.
- public override IEnumerable CreateImmutableCollectionFromList(Type collectionType, string delegateKey, IList sourceList, string propertyPath)
+ public override IEnumerable CreateImmutableCollectionInstance(Type collectionType, string delegateKey, IList sourceList, string jsonPath, JsonSerializerOptions options)
{
- if (!DefaultImmutableConverter.TryGetCreateRangeDelegate(delegateKey, out object createRangeDelegateObj))
+ if (!options.TryGetCreateRangeDelegate(delegateKey, out object createRangeDelegateObj))
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(collectionType, propertyPath);
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(collectionType, jsonPath);
}
- DefaultImmutableConverter.ImmutableCreateRangeDelegate<TRuntimeProperty> createRangeDelegate = (
- (DefaultImmutableConverter.ImmutableCreateRangeDelegate<TRuntimeProperty>)createRangeDelegateObj);
+ JsonSerializerOptions.ImmutableCreateRangeDelegate<TRuntimeProperty> createRangeDelegate = (
+ (JsonSerializerOptions.ImmutableCreateRangeDelegate<TRuntimeProperty>)createRangeDelegateObj);
- return (IEnumerable)createRangeDelegate.Invoke(CreateGenericIEnumerableFromList(sourceList));
+ return (IEnumerable)createRangeDelegate.Invoke(CreateGenericTRuntimePropertyIEnumerable(sourceList));
}
// Creates an IEnumerable<TRuntimePropertyType> and populates it with the items in the
// sourceList argument then uses the delegateKey argument to identify the appropriate cached
// CreateRange<TRuntimePropertyType> method to create and return the desired immutable collection type.
- public override IDictionary CreateImmutableCollectionFromDictionary(Type collectionType, string delegateKey, IDictionary sourceDictionary, string propertyPath)
+ public override IDictionary CreateImmutableDictionaryInstance(Type collectionType, string delegateKey, IDictionary sourceDictionary, string jsonPath, JsonSerializerOptions options)
{
- if (!DefaultImmutableConverter.TryGetCreateRangeDelegate(delegateKey, out object createRangeDelegateObj))
+ if (!options.TryGetCreateRangeDelegate(delegateKey, out object createRangeDelegateObj))
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(collectionType, propertyPath);
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(collectionType, jsonPath);
}
- DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<string, TRuntimeProperty> createRangeDelegate = (
- (DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<string, TRuntimeProperty>)createRangeDelegateObj);
+ JsonSerializerOptions.ImmutableDictCreateRangeDelegate<string, TRuntimeProperty> createRangeDelegate = (
+ (JsonSerializerOptions.ImmutableDictCreateRangeDelegate<string, TRuntimeProperty>)createRangeDelegateObj);
return (IDictionary)createRangeDelegate.Invoke(CreateGenericIEnumerableFromDictionary(sourceDictionary));
}
- public override IEnumerable CreateIEnumerableConstructibleType(Type enumerableType, IList sourceList)
+ public override ValueType CreateKeyValuePairInstance(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options)
{
- return (IEnumerable)Activator.CreateInstance(enumerableType, CreateGenericIEnumerableFromList(sourceList));
+ Type enumerableType = state.Current.JsonPropertyInfo.RuntimePropertyType;
+
+ // Form {"MyKey": 1}.
+ if (sourceDictionary.Count == 1)
+ {
+ IDictionaryEnumerator enumerator = sourceDictionary.GetEnumerator();
+ enumerator.MoveNext();
+ return new KeyValuePair<string, TRuntimeProperty>((string)enumerator.Key, (TRuntimeProperty)enumerator.Value);
+ }
+ // Form {"Key": "MyKey", "Value": 1}.
+ else if (sourceDictionary.Count == 2 &&
+ sourceDictionary["Key"] is string key &&
+ sourceDictionary["Value"] is TRuntimeProperty value
+ )
+ {
+ return new KeyValuePair<string, TRuntimeProperty>(key, value);
+ }
+
+ throw ThrowHelper.GetJsonException_DeserializeUnableToConvertValue(enumerableType, state.JsonPath);
}
- private IEnumerable<TRuntimeProperty> CreateGenericIEnumerableFromList(IList sourceList)
+ private IEnumerable<TRuntimeProperty> CreateGenericTRuntimePropertyIEnumerable(IList sourceList)
{
foreach (object item in sourceList)
{
}
}
+ private IEnumerable<TDeclaredProperty> CreateGenericTDeclaredPropertyIEnumerable(IList sourceList)
+ {
+ foreach (object item in sourceList)
+ {
+ yield return (TDeclaredProperty)item;
+ }
+ }
+
private IEnumerable<KeyValuePair<string, TRuntimeProperty>> CreateGenericIEnumerableFromDictionary(IDictionary sourceDictionary)
{
foreach (DictionaryEntry item in sourceDictionary)
using System.Collections.Generic;
using System.Diagnostics;
+using System.Text.Json.Serialization.Converters;
+using System.Text.Json.Serialization.Policies;
namespace System.Text.Json
{
{
Debug.Assert(ShouldDeserialize);
- // We need a property in order to a apply a value in a dictionary.
- if (state.Current.KeyName == null && (state.Current.IsProcessingDictionary || state.Current.IsProcessingImmutableDictionary))
+ if (state.Current.KeyName == null && (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair))
{
ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath);
return;
if (ValueConverter == null || !ValueConverter.TryRead(RuntimePropertyType, ref reader, out TRuntimeProperty value))
{
+ if (state.Current.IsProcessingKeyValuePair && state.Current.KeyName == "Key")
+ {
+ // Handle the special case where the input KeyValuePair is of form {"Key": "MyKey", "Value": 1}
+ // (as opposed to form {"MyKey": 1}) and the value type is not string.
+ // If we have one, the ValueConverter failed to read the current token because it should be of type string
+ // (we only support string keys) but we initially tried to read it as type TRuntimeProperty.
+ // We have TRuntimeProperty not string because for deserialization, we parse the KeyValuePair as a
+ // dictionary before creating a KeyValuePair instance in a converter-like manner with the parsed values.
+ // Because it's dictionary-like parsing, we set the element type of the dictionary earlier on to the KeyValuePair's value
+ // type, which led us here.
+ // If there's no ValueConverter, the runtime type of the KeyValuePair's value is probably an object, dictionary or enumerable.
+ JsonValueConverter<string> stringConverter = DefaultConverters<string>.s_converter;
+ if (!stringConverter.TryRead(typeof(string), ref reader, out string strValue))
+ {
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(typeof(string), reader, state.JsonPath);
+ }
+
+ object objValue = strValue;
+
+ JsonSerializer.ApplyValueToEnumerable(ref objValue, ref state, ref reader);
+ return;
+ }
+
ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath);
return;
}
+ if (state.Current.IsProcessingKeyValuePair)
+ {
+ // The value is being applied to a Dictionary<string, object>, so we need to cast to object here.
+ object objValue = value;
+ JsonSerializer.ApplyValueToEnumerable(ref objValue, ref state, ref reader);
+ return;
+ }
+
JsonSerializer.ApplyValueToEnumerable(ref value, ref state, ref reader);
}
// Verify that we don't have a multidimensional array.
Type arrayType = jsonPropertyInfo.RuntimePropertyType;
- if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1))
+ if (!state.Current.IsProcessingKeyValuePair && !typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1))
{
ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType, reader, state.JsonPath);
}
state.Current.ReturnValue = value;
return true;
}
- else if (state.Current.IsEnumerable || state.Current.IsDictionary || state.Current.IsImmutableDictionary)
+ else if (state.Current.IsEnumerable || state.Current.IsDictionary || state.Current.IsIDictionaryConstructible)
{
// Returning a non-converted list.
return true;
ThrowHelper.ThrowJsonException_DeserializeDuplicateKey(key, reader, state.JsonPath);
}
}
- else if (state.Current.IsImmutableDictionary || (state.Current.IsImmutableDictionaryProperty && !setPropertyDirectly))
+ else if (state.Current.IsIDictionaryConstructible ||
+ (state.Current.IsIDictionaryConstructibleProperty && !setPropertyDirectly) ||
+ state.Current.IsKeyValuePair ||
+ (state.Current.IsKeyValuePairProperty && !setPropertyDirectly))
{
Debug.Assert(state.Current.TempDictionaryValues != null);
IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.TempDictionaryValues);
ThrowHelper.ThrowJsonException_DeserializeDuplicateKey(key, reader, state.JsonPath);
}
}
- else if (state.Current.IsProcessingImmutableDictionary)
+ else if (state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
{
Debug.Assert(state.Current.TempDictionaryValues != null);
IDictionary<string, TProperty> dictionary = (IDictionary<string, TProperty>)state.Current.TempDictionaryValues;
using System.Collections;
using System.Diagnostics;
-using System.Text.Json.Serialization.Converters;
+using System.Text.Json.Serialization.Policies;
namespace System.Text.Json
{
JsonClassInfo classInfo = state.Current.JsonClassInfo;
- if (state.Current.IsProcessingImmutableDictionary)
+ if (state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
{
state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject();
}
state.Current.PropertyInitialized = true;
- if (state.Current.IsProcessingImmutableDictionary)
+ if (state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
{
JsonClassInfo dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType);
state.Current.TempDictionaryValues = (IDictionary)dictionaryClassInfo.CreateObject();
// We added the items to the dictionary already.
state.Current.ResetProperty();
}
- else if (state.Current.IsImmutableDictionaryProperty)
+ else if (state.Current.IsIDictionaryConstructibleProperty)
{
Debug.Assert(state.Current.TempDictionaryValues != null);
- state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, CreateImmutableDictionaryFromTempValues(ref state, options));
+ JsonDictionaryConverter converter = state.Current.JsonPropertyInfo.DictionaryConverter;
+ state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, converter.CreateFromDictionary(ref state, state.Current.TempDictionaryValues, options));
+ state.Current.ResetProperty();
+ }
+ else if (state.Current.IsKeyValuePairProperty)
+ {
+ JsonClassInfo elementClassInfo = state.Current.JsonPropertyInfo.ElementClassInfo;
+
+ JsonPropertyInfo propertyInfo;
+ if (elementClassInfo.ClassType == ClassType.KeyValuePair)
+ {
+ propertyInfo = elementClassInfo.GetPolicyPropertyOfKeyValuePair();
+ }
+ else
+ {
+ propertyInfo = elementClassInfo.GetPolicyProperty();
+ }
+
+ Debug.Assert(state.Current.TempDictionaryValues != null);
+ state.Current.JsonPropertyInfo.SetValueAsObject(
+ state.Current.ReturnValue,
+ propertyInfo.CreateKeyValuePairInstance(ref state, state.Current.TempDictionaryValues, options));
state.Current.ResetProperty();
}
else
object value;
if (state.Current.TempDictionaryValues != null)
{
- value = CreateImmutableDictionaryFromTempValues(ref state, options);
+ if (state.Current.IsKeyValuePair)
+ {
+ JsonClassInfo elementClassInfo = state.Current.JsonClassInfo.ElementClassInfo;
+
+ JsonPropertyInfo propertyInfo;
+ if (elementClassInfo.ClassType == ClassType.KeyValuePair)
+ {
+ propertyInfo = elementClassInfo.GetPolicyPropertyOfKeyValuePair();
+ }
+ else
+ {
+ propertyInfo = elementClassInfo.GetPolicyProperty();
+ }
+
+ value = propertyInfo.CreateKeyValuePairInstance(ref state, state.Current.TempDictionaryValues, options);
+ }
+ else
+ {
+ JsonDictionaryConverter converter = state.Current.JsonPropertyInfo.DictionaryConverter;
+ value = converter.CreateFromDictionary(ref state, state.Current.TempDictionaryValues, options);
+ }
}
else
{
}
}
}
-
- private static IDictionary CreateImmutableDictionaryFromTempValues(ref ReadStack state, JsonSerializerOptions options)
- {
- Debug.Assert(state.Current.IsProcessingImmutableDictionary);
-
- DefaultImmutableConverter converter = (DefaultImmutableConverter)state.Current.JsonPropertyInfo.EnumerableConverter;
- Debug.Assert(converter != null);
-
- return converter.CreateFromDictionary(ref state, state.Current.TempDictionaryValues, options);
- }
}
}
ThrowHelper.ThrowJsonException_DeserializeCannotBeNull(reader, state.JsonPath);
}
- if (state.Current.IsEnumerable || state.Current.IsDictionary || state.Current.IsImmutableDictionary)
+ if (state.Current.IsEnumerable || state.Current.IsDictionary || state.Current.IsIDictionaryConstructible)
{
ApplyObjectToEnumerable(null, ref state, ref reader);
return false;
}
- if (state.Current.IsEnumerableProperty || state.Current.IsDictionaryProperty || state.Current.IsImmutableDictionaryProperty)
+ if (state.Current.IsEnumerableProperty || state.Current.IsDictionaryProperty || state.Current.IsIDictionaryConstructibleProperty)
{
bool setPropertyToNull = !state.Current.PropertyInitialized;
ApplyObjectToEnumerable(null, ref state, ref reader, setPropertyDirectly: setPropertyToNull);
{
private static void HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
{
- Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingImmutableDictionary);
+ Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair);
if (state.Current.IsProcessingEnumerable)
{
}
}
- if (state.Current.IsProcessingImmutableDictionary)
+ if (state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
{
state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject();
}
private static void HandleEndObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
{
- Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingImmutableDictionary);
+ Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair);
state.Current.JsonClassInfo.UpdateSortedPropertyCache(ref state.Current);
Debug.Assert(state.Current.ReturnValue != default || state.Current.TempDictionaryValues != default);
Debug.Assert(state.Current.JsonClassInfo != default);
- if (state.Current.IsProcessingDictionary || state.Current.IsProcessingImmutableDictionary)
+ if (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
{
if (ReferenceEquals(state.Current.JsonClassInfo.DataExtensionProperty, state.Current.JsonPropertyInfo))
{
keyName = options.DictionaryKeyPolicy.ConvertName(keyName);
}
- if (state.Current.IsDictionary || state.Current.IsImmutableDictionary)
+ if (state.Current.IsDictionary || state.Current.IsIDictionaryConstructible)
{
state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.GetPolicyProperty();
}
Debug.Assert(
state.Current.IsDictionary ||
(state.Current.IsDictionaryProperty && state.Current.JsonPropertyInfo != null) ||
- state.Current.IsImmutableDictionary ||
- (state.Current.IsImmutableDictionaryProperty && state.Current.JsonPropertyInfo != null));
+ state.Current.IsIDictionaryConstructible ||
+ (state.Current.IsIDictionaryConstructibleProperty && state.Current.JsonPropertyInfo != null) ||
+ state.Current.IsKeyValuePair ||
+ (state.Current.IsKeyValuePairProperty && state.Current.JsonPropertyInfo != null));
state.Current.KeyName = keyName;
}
break;
}
}
- else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingImmutableDictionary)
+ else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
{
HandleStartDictionary(options, ref reader, ref readStack);
}
{
readStack.Pop();
}
- else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingImmutableDictionary)
+ else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
{
HandleEndDictionary(options, ref reader, ref readStack);
}
value = (TProperty)polymorphicEnumerator.Current.Value;
key = polymorphicEnumerator.Current.Key;
}
- else if (current.IsImmutableDictionary || current.IsImmutableDictionaryProperty)
+ else if (current.IsIDictionaryConstructible || current.IsIDictionaryConstructibleProperty)
{
value = (TProperty)((DictionaryEntry)current.Enumerator.Current).Value;
key = (string)((DictionaryEntry)current.Enumerator.Current).Key;
// An object or another enumerator requires a new stack frame.
object nextValue = state.Current.Enumerator.Current;
state.Push(elementClassInfo, nextValue);
+
+ if (elementClassInfo.ClassType == ClassType.KeyValuePair)
+ {
+ state.Current.JsonPropertyInfo = elementClassInfo.GetPolicyPropertyOfKeyValuePair();
+ state.Current.PropertyIndex++;
+ }
}
return false;
{
Debug.Assert(
state.Current.JsonClassInfo.ClassType == ClassType.Object ||
+ state.Current.JsonClassInfo.ClassType == ClassType.KeyValuePair ||
state.Current.JsonClassInfo.ClassType == ClassType.Unknown);
JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(state.Current.PropertyIndex);
}
// A property that returns an immutable dictionary keeps the same stack frame.
- if (jsonPropertyInfo.ClassType == ClassType.ImmutableDictionary)
+ if (jsonPropertyInfo.ClassType == ClassType.IDictionaryConstructible)
{
- state.Current.IsImmutableDictionaryProperty = true;
+ state.Current.IsIDictionaryConstructibleProperty = true;
bool endOfEnumerable = HandleDictionary(jsonPropertyInfo.ElementClassInfo, options, writer, ref state);
if (endOfEnumerable)
// Set the PropertyInfo so we can obtain the property name in order to write it.
state.Current.JsonPropertyInfo = previousPropertyInfo;
+
+ if (state.Current.JsonPropertyInfo.ClassType == ClassType.KeyValuePair)
+ {
+ // Advance to the next property, since the first one is the KeyValuePair type itself,
+ // not its first property (Key or Value).
+ state.Current.PropertyIndex++;
+ }
}
else
{
finishedSerializing = true;
break;
case ClassType.Object:
+ case ClassType.KeyValuePair:
finishedSerializing = WriteObject(options, writer, ref state);
break;
case ClassType.Dictionary:
- case ClassType.ImmutableDictionary:
+ case ClassType.IDictionaryConstructible:
finishedSerializing = HandleDictionary(current.JsonClassInfo.ElementClassInfo, options, writer, ref state);
break;
default:
// 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.Generic;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text.Json.Serialization;
private readonly ConcurrentDictionary<Type, JsonClassInfo> _classes = new ConcurrentDictionary<Type, JsonClassInfo>();
private readonly ConcurrentDictionary<Type, JsonPropertyInfo> _objectJsonProperties = new ConcurrentDictionary<Type, JsonPropertyInfo>();
+ private static ConcurrentDictionary<string, object> s_createRangeDelegates = new ConcurrentDictionary<string, object>();
private ClassMaterializer _classMaterializerStrategy;
private JsonNamingPolicy _dictionayKeyPolicy;
private JsonNamingPolicy _jsonPropertyNamingPolicy;
};
}
+ internal delegate object ImmutableCreateRangeDelegate<T>(IEnumerable<T> items);
+ internal delegate object ImmutableDictCreateRangeDelegate<TKey, TValue>(IEnumerable<KeyValuePair<TKey, TValue>> items);
+
internal JsonPropertyInfo GetJsonPropertyInfoFromClassInfo(JsonClassInfo classInfo, JsonSerializerOptions options)
{
+ if (classInfo.ClassType == ClassType.KeyValuePair)
+ {
+ return classInfo.GetPolicyPropertyOfKeyValuePair();
+ }
+
if (classInfo.ClassType != ClassType.Object)
{
return classInfo.GetPolicyProperty();
return propertyInfo;
}
+ internal bool CreateRangeDelegatesContainsKey(string key)
+ {
+ return s_createRangeDelegates.ContainsKey(key);
+ }
+
+ internal bool TryGetCreateRangeDelegate(string delegateKey, out object createRangeDelegate)
+ {
+ return s_createRangeDelegates.TryGetValue(delegateKey, out createRangeDelegate) && createRangeDelegate != null;
+ }
+
+ internal bool TryAddCreateRangeDelegate(string key, object createRangeDelegate)
+ {
+ return s_createRangeDelegates.TryAdd(key, createRangeDelegate);
+ }
+
+
private void VerifyMutable()
{
// The default options are hidden and thus should be immutable.
// Has an array or dictionary property been initialized.
public bool PropertyInitialized;
- // Support Immutable dictionary types.
+ // Support IDictionary constructible types, i.e. types that we
+ // support by passing and IDictionary to their constructors:
+ // immutable dictionaries, Hashtable, SortedList
public IDictionary TempDictionaryValues;
// For performance, we order the properties by the first deserialize and PropertyIndex helps find the right slot quicker.
// The current JSON data for a property does not match a given POCO, so ignore the property (recursively).
public bool Drain;
- public bool IsImmutableDictionary => JsonClassInfo.ClassType == ClassType.ImmutableDictionary;
+ public bool IsIDictionaryConstructible => JsonClassInfo.ClassType == ClassType.IDictionaryConstructible;
public bool IsDictionary => JsonClassInfo.ClassType == ClassType.Dictionary;
+ public bool IsKeyValuePair => JsonClassInfo.ClassType == ClassType.KeyValuePair;
public bool IsDictionaryProperty => JsonPropertyInfo != null &&
!JsonPropertyInfo.IsPropertyPolicy &&
JsonPropertyInfo.ClassType == ClassType.Dictionary;
- public bool IsImmutableDictionaryProperty => JsonPropertyInfo != null &&
- !JsonPropertyInfo.IsPropertyPolicy && (JsonPropertyInfo.ClassType == ClassType.ImmutableDictionary);
+ public bool IsIDictionaryConstructibleProperty => JsonPropertyInfo != null &&
+ !JsonPropertyInfo.IsPropertyPolicy && (JsonPropertyInfo.ClassType == ClassType.IDictionaryConstructible);
+ public bool IsKeyValuePairProperty => JsonPropertyInfo != null &&
+ !JsonPropertyInfo.IsPropertyPolicy && (JsonPropertyInfo.ClassType == ClassType.KeyValuePair);
public bool IsEnumerable => JsonClassInfo.ClassType == ClassType.Enumerable;
!JsonPropertyInfo.IsPropertyPolicy &&
JsonPropertyInfo.ClassType == ClassType.Enumerable;
- public bool IsProcessingEnumerableOrDictionary => IsProcessingEnumerable || IsProcessingDictionary || IsProcessingImmutableDictionary;
+ public bool IsProcessingEnumerableOrDictionary => IsProcessingEnumerable || IsProcessingDictionary || IsProcessingIDictionaryConstructibleOrKeyValuePair;
+ public bool IsProcessingIDictionaryConstructibleOrKeyValuePair => IsProcessingIDictionaryConstructible || IsProcessingKeyValuePair;
+
public bool IsProcessingDictionary => IsDictionary || IsDictionaryProperty;
- public bool IsProcessingImmutableDictionary => IsImmutableDictionary || IsImmutableDictionaryProperty;
+ public bool IsProcessingIDictionaryConstructible => IsIDictionaryConstructible || IsIDictionaryConstructibleProperty;
+ public bool IsProcessingKeyValuePair => IsKeyValuePair || IsKeyValuePairProperty;
public bool IsProcessingEnumerable => IsEnumerable || IsEnumerableProperty;
public bool IsProcessingValue
return false;
}
+ if (PropertyInitialized)
+ {
+ if ((IsEnumerable || IsDictionary || IsIDictionaryConstructible) &&
+ JsonClassInfo.ElementClassInfo.ClassType == ClassType.Unknown)
+ {
+ return true;
+ }
+ else if ((IsEnumerableProperty || IsDictionaryProperty || IsIDictionaryConstructibleProperty) &&
+ JsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Unknown)
+ {
+ return true;
+ }
+ }
+
// We've got a property info. If we're a Value or polymorphic Value
// (ClassType.Unknown), return true.
ClassType type = JsonPropertyInfo.ClassType;
KeyName != null && (
(IsDictionary && JsonClassInfo.ElementClassInfo.ClassType == ClassType.Unknown) ||
(IsDictionaryProperty && JsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Unknown) ||
- (IsImmutableDictionary && JsonClassInfo.ElementClassInfo.ClassType == ClassType.Unknown) ||
- (IsImmutableDictionaryProperty && JsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Unknown)
+ (IsIDictionaryConstructible && JsonClassInfo.ElementClassInfo.ClassType == ClassType.Unknown) ||
+ (IsIDictionaryConstructibleProperty && JsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Unknown)
);
}
}
if (JsonClassInfo.ClassType == ClassType.Value ||
JsonClassInfo.ClassType == ClassType.Enumerable ||
JsonClassInfo.ClassType == ClassType.Dictionary ||
- JsonClassInfo.ClassType == ClassType.ImmutableDictionary)
+ JsonClassInfo.ClassType == ClassType.IDictionaryConstructible)
{
JsonPropertyInfo = JsonClassInfo.GetPolicyProperty();
}
+ else if (JsonClassInfo.ClassType == ClassType.KeyValuePair)
+ {
+ JsonPropertyInfo = JsonClassInfo.GetPolicyPropertyOfKeyValuePair();
+ }
}
public void Reset()
return null;
}
- Type propType = state.Current.JsonPropertyInfo.RuntimePropertyType;
- if (typeof(IList).IsAssignableFrom(propType))
+ Type propertyType = state.Current.JsonPropertyInfo.RuntimePropertyType;
+ if (typeof(IList).IsAssignableFrom(propertyType))
{
// If IList, add the members as we create them.
JsonClassInfo collectionClassInfo = state.Current.JsonPropertyInfo.RuntimeClassInfo;
}
else
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(propType, reader, state.JsonPath);
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(propertyType, reader, state.JsonPath);
return null;
}
}
public Type GetElementType()
{
- if (IsEnumerableProperty || IsDictionaryProperty || IsImmutableDictionaryProperty)
+ if (IsEnumerableProperty || IsDictionaryProperty || IsIDictionaryConstructibleProperty)
{
return JsonPropertyInfo.ElementClassInfo.Type;
}
- if (IsEnumerable || IsDictionary || IsImmutableDictionary)
+ if (IsEnumerable || IsDictionary || IsIDictionaryConstructible)
{
return JsonClassInfo.ElementClassInfo.Type;
}
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
-using System.Text.Json.Serialization.Converters;
namespace System.Text.Json
{
}
return createRange.CreateDelegate(
- typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null);
+ typeof(JsonSerializerOptions.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null);
}
public override object ImmutableDictionaryCreateRange(Type constructingType, Type elementType)
}
return createRange.CreateDelegate(
- typeof(DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null);
+ typeof(JsonSerializerOptions.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null);
}
}
}
using System.Diagnostics;
using System.Reflection;
-using System.Text.Json.Serialization.Converters;
namespace System.Text.Json
{
}
return createRange.CreateDelegate(
- typeof(DefaultImmutableConverter.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null);
+ typeof(JsonSerializerOptions.ImmutableCreateRangeDelegate<>).MakeGenericType(elementType), null);
}
public override object ImmutableDictionaryCreateRange(Type constructingType, Type elementType)
}
return createRange.CreateDelegate(
- typeof(DefaultImmutableConverter.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null);
+ typeof(JsonSerializerOptions.ImmutableDictCreateRangeDelegate<,>).MakeGenericType(typeof(string), elementType), null);
}
}
}
Current.PopStackOnEnd = true;
Current.JsonPropertyInfo = Current.JsonClassInfo.GetPolicyProperty();
}
- else if (classType == ClassType.ImmutableDictionary)
+ else if (classType == ClassType.IDictionaryConstructible)
{
Current.PopStackOnEnd = true;
Current.JsonPropertyInfo = Current.JsonClassInfo.GetPolicyProperty();
- Current.IsImmutableDictionary = true;
+ Current.IsIDictionaryConstructible = true;
}
else
{
- Debug.Assert(nextClassInfo.ClassType == ClassType.Object || nextClassInfo.ClassType == ClassType.Unknown);
+ Debug.Assert(nextClassInfo.ClassType == ClassType.Object || nextClassInfo.ClassType == ClassType.KeyValuePair || nextClassInfo.ClassType == ClassType.Unknown);
Current.PopStackOnEndObject = true;
}
}
// 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.Diagnostics;
-using System.Text.Json.Serialization.Converters;
namespace System.Text.Json
{
// Support Dictionary keys.
public string KeyName;
- // Whether the current object is an immutable dictionary.
- public bool IsImmutableDictionary;
- public bool IsImmutableDictionaryProperty;
+ // Whether the current object can be constructed with IDictionary.
+ public bool IsIDictionaryConstructible;
+ public bool IsIDictionaryConstructibleProperty;
// The current enumerator for the IEnumerable or IDictionary.
public IEnumerator Enumerator;
{
JsonPropertyInfo = JsonClassInfo.GetPolicyProperty();
}
- else if (JsonClassInfo.ClassType == ClassType.ImmutableDictionary)
+ else if (JsonClassInfo.ClassType == ClassType.IDictionaryConstructible)
{
JsonPropertyInfo = JsonClassInfo.GetPolicyProperty();
- IsImmutableDictionary = true;
+ IsIDictionaryConstructible = true;
+ }
+ else if (JsonClassInfo.ClassType == ClassType.KeyValuePair)
+ {
+ JsonPropertyInfo = JsonClassInfo.GetPolicyPropertyOfKeyValuePair();
+ // Advance to the next property, since the first one is the KeyValuePair type itself,
+ // not its first property (Key or Value).
+ PropertyIndex++;
}
}
Debug.Assert(writeNull == false);
// Write start without a property name.
- if (classType == ClassType.Object || classType == ClassType.Dictionary || classType == ClassType.ImmutableDictionary)
+ if (classType == ClassType.Object || classType == ClassType.Dictionary || classType == ClassType.IDictionaryConstructible)
{
writer.WriteStartObject();
StartObjectWritten = true;
}
else if (classType == ClassType.Object ||
classType == ClassType.Dictionary ||
- classType == ClassType.ImmutableDictionary)
+ classType == ClassType.IDictionaryConstructible)
{
writer.WriteStartObject(propertyName);
StartObjectWritten = true;
JsonClassInfo = null;
JsonPropertyInfo = null;
PropertyIndex = 0;
- IsImmutableDictionary = false;
+ IsIDictionaryConstructible = false;
PopStackOnEndObject = false;
PopStackOnEnd = false;
StartObjectWritten = false;
{
PropertyIndex = 0;
PopStackOnEndObject = false;
- IsImmutableDictionaryProperty = false;
+ IsIDictionaryConstructibleProperty = false;
JsonPropertyInfo = null;
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
+ public static JsonException GetJsonException_DeserializeUnableToConvertValue(Type propertyType, string path)
+ {
+ string message = SR.Format(SR.DeserializeUnableToConvertValue, propertyType) + $" Path: {path}.";
+ return new JsonException(message, path, null, null);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowJsonException_DeserializeCannotBeNull(in Utf8JsonReader reader, string path)
{
ThowJsonException(SR.DeserializeCannotBeNull, in reader, path);
}
[Fact]
+ public static void ReadClassWithObjectIEnumerable()
+ {
+ TestClassWithObjectIEnumerable obj = JsonSerializer.Parse<TestClassWithObjectIEnumerable>(TestClassWithObjectIEnumerable.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
+ public static void ReadClassWithObjectIList()
+ {
+ TestClassWithObjectIList obj = JsonSerializer.Parse<TestClassWithObjectIList>(TestClassWithObjectIList.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
+ public static void ReadClassWithObjectICollection()
+ {
+ TestClassWithObjectICollection obj = JsonSerializer.Parse<TestClassWithObjectICollection>(TestClassWithObjectICollection.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
public static void ReadClassWithObjectIEnumerableT()
{
TestClassWithObjectIEnumerableT obj = JsonSerializer.Parse<TestClassWithObjectIEnumerableT>(TestClassWithObjectIEnumerableT.s_data);
}
[Fact]
+ public static void ReadClassWithGenericIEnumerable()
+ {
+ TestClassWithGenericIEnumerable obj = JsonSerializer.Parse<TestClassWithGenericIEnumerable>(TestClassWithGenericIEnumerable.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
+ public static void ReadClassWithGenericIList()
+ {
+ TestClassWithGenericIList obj = JsonSerializer.Parse<TestClassWithGenericIList>(TestClassWithGenericIList.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
+ public static void ReadClassWithGenericICollection()
+ {
+ TestClassWithGenericICollection obj = JsonSerializer.Parse<TestClassWithGenericICollection>(TestClassWithGenericICollection.s_data);
+ }
+
+ public static void ReadClassWithObjectISetT()
+ {
+ TestClassWithObjectISetT obj = JsonSerializer.Parse<TestClassWithObjectISetT>(TestClassWithObjectISetT.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
public static void ReadClassWithGenericIEnumerableT()
{
TestClassWithGenericIEnumerableT obj = JsonSerializer.Parse<TestClassWithGenericIEnumerableT>(TestClassWithGenericIEnumerableT.s_data);
}
[Fact]
+ public static void ReadClassWithGenericISetT()
+ {
+ TestClassWithGenericISetT obj = JsonSerializer.Parse<TestClassWithGenericISetT>(TestClassWithGenericISetT.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
public static void ReadClassWithObjectIEnumerableConstructibleTypes()
{
TestClassWithObjectIEnumerableConstructibleTypes obj = JsonSerializer.Parse<TestClassWithObjectIEnumerableConstructibleTypes>(TestClassWithObjectIEnumerableConstructibleTypes.s_data);
const string ReorderedJsonString = @"{""Hello2"":""World2"",""Hello"":""World""}";
{
+ IDictionary obj = JsonSerializer.Parse<IDictionary>(JsonString);
+ Assert.Equal("World", ((JsonElement)obj["Hello"]).GetString());
+ Assert.Equal("World2", ((JsonElement)obj["Hello2"]).GetString());
+
+ string json = JsonSerializer.ToString(obj);
+ Assert.Equal(JsonString, json);
+
+ json = JsonSerializer.ToString<object>(obj);
+ Assert.Equal(JsonString, json);
+ }
+
+ {
Dictionary<string, string> obj = JsonSerializer.Parse<Dictionary<string, string>>(JsonString);
Assert.Equal("World", obj["Hello"]);
Assert.Equal("World2", obj["Hello2"]);
}
{
+ SortedDictionary<string, string> obj = JsonSerializer.Parse<SortedDictionary<string, string>>(JsonString);
+ Assert.Equal("World", obj["Hello"]);
+ Assert.Equal("World2", obj["Hello2"]);
+
+ string json = JsonSerializer.ToString(obj);
+ Assert.Equal(JsonString, json);
+
+ json = JsonSerializer.ToString<object>(obj);
+ Assert.Equal(JsonString, json);
+ }
+
+ {
IDictionary<string, string> obj = JsonSerializer.Parse<IDictionary<string, string>>(JsonString);
Assert.Equal("World", obj["Hello"]);
Assert.Equal("World2", obj["Hello2"]);
json = JsonSerializer.ToString<object>(obj);
Assert.True(JsonString == json);
}
+
+ {
+ Hashtable obj = JsonSerializer.Parse<Hashtable>(JsonString);
+ Assert.Equal("World", ((JsonElement)obj["Hello"]).GetString());
+ Assert.Equal("World2", ((JsonElement)obj["Hello2"]).GetString());
+
+ string json = JsonSerializer.ToString(obj);
+ Assert.True(JsonString == json || ReorderedJsonString == json);
+
+ json = JsonSerializer.ToString<object>(obj);
+ Assert.True(JsonString == json || ReorderedJsonString == json);
+ }
+
+ {
+ SortedList obj = JsonSerializer.Parse<SortedList>(JsonString);
+ Assert.Equal("World", ((JsonElement)obj["Hello"]).GetString());
+ Assert.Equal("World2", ((JsonElement)obj["Hello2"]).GetString());
+
+ string json = JsonSerializer.ToString(obj);
+ Assert.Equal(JsonString, json);
+
+ json = JsonSerializer.ToString<object>(obj);
+ Assert.Equal(JsonString, json);
+ }
}
[Fact]
public static void DuplicateKeysFail()
{
+ // Non-generic IDictionary case.
+ Assert.Throws<JsonException>(() => JsonSerializer.Parse<IDictionary>(
+ @"{""Hello"":""World"", ""Hello"":""World""}"));
+
// Strongly-typed IDictionary<,> case.
Assert.Throws<JsonException>(() => JsonSerializer.Parse<Dictionary<string, string>>(
@"{""Hello"":""World"", ""Hello"":""World""}"));
[Fact]
public static void DictionaryOfObject()
{
- Dictionary<string, object> obj = JsonSerializer.Parse<Dictionary<string, object>>(@"{""Key1"":1}");
- Assert.Equal(1, obj.Count);
- JsonElement element = (JsonElement)obj["Key1"];
- Assert.Equal(JsonValueType.Number, element.Type);
- Assert.Equal(1, element.GetInt32());
+ {
+ IDictionary obj = JsonSerializer.Parse<IDictionary>(@"{""Key1"":1}");
+ Assert.Equal(1, obj.Count);
+ JsonElement element = (JsonElement)obj["Key1"];
+ Assert.Equal(JsonValueType.Number, element.Type);
+ Assert.Equal(1, element.GetInt32());
- string json = JsonSerializer.ToString(obj);
- Assert.Equal(@"{""Key1"":1}", json);
+ string json = JsonSerializer.ToString(obj);
+ Assert.Equal(@"{""Key1"":1}", json);
+ }
+
+ {
+ Dictionary<string, object> obj = JsonSerializer.Parse<Dictionary<string, object>>(@"{""Key1"":1}");
+ Assert.Equal(1, obj.Count);
+ JsonElement element = (JsonElement)obj["Key1"];
+ Assert.Equal(JsonValueType.Number, element.Type);
+ Assert.Equal(1, element.GetInt32());
+
+ string json = JsonSerializer.ToString(obj);
+ Assert.Equal(@"{""Key1"":1}", json);
+ }
}
[Theory]
const string JsonString = @"{""Key1"":[1,2],""Key2"":[3,4]}";
{
+ IDictionary obj = JsonSerializer.Parse<IDictionary>(JsonString);
+
+ Assert.Equal(2, obj.Count);
+
+ int expectedNumber = 1;
+
+ JsonElement element = (JsonElement)obj["Key1"];
+ foreach (JsonElement value in element.EnumerateArray())
+ {
+ Assert.Equal(expectedNumber++, value.GetInt32());
+ }
+
+ element = (JsonElement)obj["Key2"];
+ foreach (JsonElement value in element.EnumerateArray())
+ {
+ Assert.Equal(expectedNumber++, value.GetInt32());
+ }
+
+ string json = JsonSerializer.ToString(obj);
+ Assert.Equal(JsonString, json);
+ }
+
+ {
IDictionary<string, List<int>> obj = JsonSerializer.Parse<IDictionary<string, List<int>>>(JsonString);
Assert.Equal(2, obj.Count);
public static void DictionaryOfClasses()
{
{
+ IDictionary obj;
+
+ {
+ string json = @"{""Key1"":" + SimpleTestClass.s_json + @",""Key2"":" + SimpleTestClass.s_json + "}";
+ obj = JsonSerializer.Parse<IDictionary>(json);
+ Assert.Equal(2, obj.Count);
+
+ if (obj["Key1"] is JsonElement element)
+ {
+ SimpleTestClass result = JsonSerializer.Parse<SimpleTestClass>(element.GetRawText());
+ result.Verify();
+ }
+ else
+ {
+ ((SimpleTestClass)obj["Key1"]).Verify();
+ ((SimpleTestClass)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<IDictionary>(json);
+ Assert.Equal(2, obj.Count);
+
+ if (obj["Key1"] is JsonElement element)
+ {
+ SimpleTestClass result = JsonSerializer.Parse<SimpleTestClass>(element.GetRawText());
+ result.Verify();
+ }
+ else
+ {
+ ((SimpleTestClass)obj["Key1"]).Verify();
+ ((SimpleTestClass)obj["Key2"]).Verify();
+ }
+ }
+
+ {
+ string json = JsonSerializer.ToString<object>(obj);
+ obj = JsonSerializer.Parse<IDictionary>(json);
+ Assert.Equal(2, obj.Count);
+
+ if (obj["Key1"] is JsonElement element)
+ {
+ SimpleTestClass result = JsonSerializer.Parse<SimpleTestClass>(element.GetRawText());
+ result.Verify();
+ }
+ else
+ {
+ ((SimpleTestClass)obj["Key1"]).Verify();
+ ((SimpleTestClass)obj["Key2"]).Verify();
+ }
+ }
+ }
+
+ {
Dictionary<string, SimpleTestClass> obj;
{
public static void HashtableFail()
{
{
- string json = @"{""Key"":""Value""}";
-
- // Verify we can deserialize into Dictionary<,>
- JsonSerializer.Parse<Dictionary<string, string>>(json);
-
- // We don't support non-generic IDictionary
- Assert.Throws<NotSupportedException>(() => JsonSerializer.Parse<Hashtable>(json));
- }
-
- {
- Hashtable ht = new Hashtable();
- ht.Add("Key", "Value");
-
- Assert.Throws<NotSupportedException>(() => JsonSerializer.ToString(ht));
- }
-
- {
- string json = @"{""Key"":""Value""}";
-
- // We don't support non-generic IDictionary
- Assert.Throws<NotSupportedException>(() => JsonSerializer.Parse<IDictionary>(json));
- }
-
- {
IDictionary ht = new Hashtable();
ht.Add("Key", "Value");
Assert.Throws<NotSupportedException>(() => JsonSerializer.ToString(ht));
// 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.Generic;
using Xunit;
namespace System.Text.Json.Serialization.Tests
// 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.Collections.Immutable;
using Xunit;
json = JsonSerializer.ToString<object>(list);
Assert.Equal(ExpectedJson, json);
- IEnumerable<object> ienumerable = new List<object> { 1, true, address, null, "foo" };
+ IEnumerable ienumerable = new List<object> { 1, true, address, null, "foo" };
json = JsonSerializer.ToString(ienumerable);
Assert.Equal(ExpectedJson, json);
json = JsonSerializer.ToString<object>(ienumerable);
Assert.Equal(ExpectedJson, json);
- IList<object> ilist = new List<object> { 1, true, address, null, "foo" };
+ IList ilist = new List<object> { 1, true, address, null, "foo" };
json = JsonSerializer.ToString(ilist);
Assert.Equal(ExpectedJson, json);
json = JsonSerializer.ToString<object>(ilist);
Assert.Equal(ExpectedJson, json);
- ICollection<object> icollection = new List<object> { 1, true, address, null, "foo" };
+ ICollection icollection = new List<object> { 1, true, address, null, "foo" };
json = JsonSerializer.ToString(icollection);
Assert.Equal(ExpectedJson, json);
json = JsonSerializer.ToString<object>(icollection);
Assert.Equal(ExpectedJson, json);
- IReadOnlyCollection<object> ireadonlycollection = new List<object> { 1, true, address, null, "foo" };
- json = JsonSerializer.ToString(ireadonlycollection);
+ IEnumerable<object> genericIEnumerable = new List<object> { 1, true, address, null, "foo" };
+ json = JsonSerializer.ToString(genericIEnumerable);
Assert.Equal(ExpectedJson, json);
- json = JsonSerializer.ToString<object>(ireadonlycollection);
+ json = JsonSerializer.ToString<object>(genericIEnumerable);
Assert.Equal(ExpectedJson, json);
- IReadOnlyList<object> ireadonlylist = new List<object> { 1, true, address, null, "foo" };
- json = JsonSerializer.ToString(ireadonlylist);
+ IList<object> genericIList = new List<object> { 1, true, address, null, "foo" };
+ json = JsonSerializer.ToString(genericIList);
Assert.Equal(ExpectedJson, json);
- json = JsonSerializer.ToString<object>(ireadonlylist);
+ json = JsonSerializer.ToString<object>(genericIList);
+ Assert.Equal(ExpectedJson, json);
+
+ ICollection<object> genericICollection = new List<object> { 1, true, address, null, "foo" };
+ json = JsonSerializer.ToString(genericICollection);
+ Assert.Equal(ExpectedJson, json);
+
+ json = JsonSerializer.ToString<object>(genericICollection);
+ Assert.Equal(ExpectedJson, json);
+
+ IReadOnlyCollection<object> genericIReadOnlyCollection = new List<object> { 1, true, address, null, "foo" };
+ json = JsonSerializer.ToString(genericIReadOnlyCollection);
+ Assert.Equal(ExpectedJson, json);
+
+ json = JsonSerializer.ToString<object>(genericIReadOnlyCollection);
+ Assert.Equal(ExpectedJson, json);
+
+ IReadOnlyList<object> genericIReadonlyList = new List<object> { 1, true, address, null, "foo" };
+ json = JsonSerializer.ToString(genericIReadonlyList);
+ Assert.Equal(ExpectedJson, json);
+
+ json = JsonSerializer.ToString<object>(genericIReadonlyList);
+ Assert.Equal(ExpectedJson, json);
+
+ ISet<object> iset = new HashSet<object> { 1, true, address, null, "foo" };
+ json = JsonSerializer.ToString(iset);
+ Assert.Equal(ExpectedJson, json);
+
+ json = JsonSerializer.ToString<object>(iset);
Assert.Equal(ExpectedJson, json);
Stack<object> stack = new Stack<object>(new List<object> { 1, true, address, null, "foo" });
Assert.Contains(@"""Address"":{""City"":""MyCity""}", json);
Assert.Contains(@"""List"":[""Hello"",""World""]", json);
Assert.Contains(@"""Array"":[""Hello"",""Again""]", json);
+ Assert.Contains(@"""IEnumerable"":[""Hello"",""World""]", json);
+ Assert.Contains(@"""IList"":[""Hello"",""World""]", json);
+ Assert.Contains(@"""ICollection"":[""Hello"",""World""]", json);
Assert.Contains(@"""IEnumerableT"":[""Hello"",""World""]", json);
Assert.Contains(@"""IListT"":[""Hello"",""World""]", json);
Assert.Contains(@"""ICollectionT"":[""Hello"",""World""]", json);
Assert.Contains(@"""IReadOnlyCollectionT"":[""Hello"",""World""]", json);
Assert.Contains(@"""IReadOnlyListT"":[""Hello"",""World""]", json);
+ Assert.Contains(@"""ISetT"":[""Hello"",""World""]", json);
Assert.Contains(@"""StackT"":[""World"",""Hello""]", json);
Assert.Contains(@"""QueueT"":[""Hello"",""World""]", json);
Assert.Contains(@"""HashSetT"":[""Hello"",""World""]", json);
public object /*Address*/ Address { get; set; }
public object /*List<string>*/ List { get; set; }
public object /*string[]*/ Array { get; set; }
+ public object /*IEnumerable of strings*/ IEnumerable { get; set; }
+ public object /*IList of strings */ IList { get; set; }
+ public object /*ICollection of strings */ ICollection { get; set; }
public object /*IEnumerable<string>*/ IEnumerableT { get; set; }
public object /*IList<string>*/ IListT { get; set; }
public object /*ICollection<string>*/ ICollectionT { get; set; }
public object /*IReadOnlyCollection<string>*/ IReadOnlyCollectionT { get; set; }
public object /*IReadOnlyList<string>*/ IReadOnlyListT { get; set; }
+ public object /*ISet<string>*/ ISetT { get; set; }
public object /*Stack<string>*/ StackT { get; set; }
public object /*Queue<string>*/ QueueT { get; set; }
public object /*HashSet<string>*/ HashSetT { get; set; }
List = new List<string> { "Hello", "World" };
Array = new string[] { "Hello", "Again" };
+ IEnumerable = new List<string> { "Hello", "World" };
+ IList = new List<string> { "Hello", "World" };
+ ICollection = new List<string> { "Hello", "World" };
IEnumerableT = new List<string> { "Hello", "World" };
IListT = new List<string> { "Hello", "World" };
ICollectionT = new List<string> { "Hello", "World" };
IReadOnlyCollectionT = new List<string> { "Hello", "World" };
IReadOnlyListT = new List<string> { "Hello", "World" };
+ ISetT = new HashSet<string> { "Hello", "World" };
StackT = new Stack<string>(new List<string> { "Hello", "World" });
QueueT = new Queue<string>(new List<string> { "Hello", "World" });
HashSetT = new HashSet<string>(new List<string> { "Hello", "World" });
// 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.Collections.Immutable;
using System.Linq;
public int[][][] MyInt16ThreeDimensionArray { get; set; }
public List<List<List<int>>> MyInt16ThreeDimensionList { get; set; }
public List<string> MyStringList { get; set; }
+ public IEnumerable MyStringIEnumerable { get; set; }
+ public IList MyStringIList { get; set; }
+ public ICollection MyStringICollection { get; set; }
public IEnumerable<string> MyStringIEnumerableT { get; set; }
public IList<string> MyStringIListT { get; set; }
public ICollection<string> MyStringICollectionT { get; set; }
public IReadOnlyCollection<string> MyStringIReadOnlyCollectionT { get; set; }
public IReadOnlyList<string> MyStringIReadOnlyListT { get; set; }
- public Dictionary<string, string> MyStringToStringDict { get; set; }
- public IDictionary<string, string> MyStringToStringIDict { get; set; }
- public IReadOnlyDictionary<string, string> MyStringToStringIReadOnlyDict { get; set; }
+ public ISet<string> MyStringISetT { get; set; }
+ public KeyValuePair<string, string> MyStringToStringKeyValuePair { get; set; }
+ public IDictionary MyStringToStringIDict { get; set; }
+ public Dictionary<string, string> MyStringToStringGenericDict { get; set; }
+ public IDictionary<string, string> MyStringToStringGenericIDict { get; set; }
+ public IReadOnlyDictionary<string, string> MyStringToStringGenericIReadOnlyDict { get; set; }
public ImmutableDictionary<string, string> MyStringToStringImmutableDict { get; set; }
public IImmutableDictionary<string, string> MyStringToStringIImmutableDict { get; set; }
public ImmutableSortedDictionary<string, string> MyStringToStringImmutableSortedDict { get; set; }
@"""MyDateTime"" : ""2019-01-30T12:01:02.0000000Z""," +
@"""MyDateTimeOffset"" : ""2019-01-30T12:01:02.0000000+01:00""," +
@"""MyEnum"" : 2," + // int by default
- @"""MyStringToStringDict"" : {""key"" : ""value""}," +
+ @"""MyStringToStringKeyValuePair"" : {""Key"" : ""myKey"", ""Value"" : ""myValue""}," +
@"""MyStringToStringIDict"" : {""key"" : ""value""}," +
- @"""MyStringToStringIReadOnlyDict"" : {""key"" : ""value""}," +
+ @"""MyStringToStringGenericDict"" : {""key"" : ""value""}," +
+ @"""MyStringToStringGenericIDict"" : {""key"" : ""value""}," +
+ @"""MyStringToStringGenericIReadOnlyDict"" : {""key"" : ""value""}," +
@"""MyStringToStringImmutableDict"" : {""key"" : ""value""}," +
@"""MyStringToStringIImmutableDict"" : {""key"" : ""value""}," +
@"""MyStringToStringImmutableSortedDict"" : {""key"" : ""value""}";
@"""MyInt16ThreeDimensionArray"" : [[[11, 12],[13, 14]],[[21,22],[23,24]]]," +
@"""MyInt16ThreeDimensionList"" : [[[11, 12],[13, 14]],[[21,22],[23,24]]]," +
@"""MyStringList"" : [""Hello""]," +
+ @"""MyStringIEnumerable"" : [""Hello""]," +
+ @"""MyStringIList"" : [""Hello""]," +
+ @"""MyStringICollection"" : [""Hello""]," +
@"""MyStringIEnumerableT"" : [""Hello""]," +
@"""MyStringIListT"" : [""Hello""]," +
@"""MyStringICollectionT"" : [""Hello""]," +
@"""MyStringIReadOnlyCollectionT"" : [""Hello""]," +
@"""MyStringIReadOnlyListT"" : [""Hello""]," +
+ @"""MyStringISetT"" : [""Hello""]," +
@"""MyStringStackT"" : [""Hello"", ""World""]," +
@"""MyStringQueueT"" : [""Hello"", ""World""]," +
@"""MyStringHashSetT"" : [""Hello""]," +
list2.Add(new List<int> { 23, 24 });
MyStringList = new List<string>() { "Hello" };
+
+ MyStringIEnumerable = new string[] { "Hello" };
+ MyStringIList = new string[] { "Hello" };
+ MyStringICollection = new string[] { "Hello" };
+
MyStringIEnumerableT = new string[] { "Hello" };
MyStringIListT = new string[] { "Hello" };
MyStringICollectionT = new string[] { "Hello" };
MyStringIReadOnlyCollectionT = new string[] { "Hello" };
MyStringIReadOnlyListT = new string[] { "Hello" };
+ MyStringISetT = new HashSet<string> { "Hello" };
- MyStringToStringDict = new Dictionary<string, string> { { "key", "value" } };
+ MyStringToStringKeyValuePair = new KeyValuePair<string, string>("myKey", "myValue");
MyStringToStringIDict = new Dictionary<string, string> { { "key", "value" } };
- MyStringToStringIReadOnlyDict = new Dictionary<string, string> { { "key", "value" } };
- MyStringToStringImmutableDict = ImmutableDictionary.CreateRange(MyStringToStringDict);
- MyStringToStringIImmutableDict = ImmutableDictionary.CreateRange(MyStringToStringDict);
- MyStringToStringImmutableSortedDict = ImmutableSortedDictionary.CreateRange(MyStringToStringDict);
+ MyStringToStringGenericDict = new Dictionary<string, string> { { "key", "value" } };
+ MyStringToStringGenericIDict = new Dictionary<string, string> { { "key", "value" } };
+ MyStringToStringGenericIReadOnlyDict = new Dictionary<string, string> { { "key", "value" } };
+
+ MyStringToStringImmutableDict = ImmutableDictionary.CreateRange(MyStringToStringGenericDict);
+ MyStringToStringIImmutableDict = ImmutableDictionary.CreateRange(MyStringToStringGenericDict);
+ MyStringToStringImmutableSortedDict = ImmutableSortedDictionary.CreateRange(MyStringToStringGenericDict);
MyStringStackT = new Stack<string>(new List<string>() { "Hello", "World" } );
MyStringQueueT = new Queue<string>(new List<string>() { "Hello", "World" });
Assert.Equal(24, MyInt16ThreeDimensionList[1][1][1]);
Assert.Equal("Hello", MyStringList[0]);
+
+ IEnumerator enumerator = MyStringIEnumerable.GetEnumerator();
+ enumerator.MoveNext();
+ {
+ // Verifying after deserialization.
+ if (enumerator.Current is JsonElement currentJsonElement)
+ {
+ Assert.Equal("Hello", currentJsonElement.GetString());
+ }
+ // Verifying test data.
+ else
+ {
+ Assert.Equal("Hello", enumerator.Current);
+ }
+ }
+
+ {
+ // Verifying after deserialization.
+ if (MyStringIList[0] is JsonElement currentJsonElement)
+ {
+ Assert.Equal("Hello", currentJsonElement.GetString());
+ }
+ // Verifying test data.
+ else
+ {
+ Assert.Equal("Hello", enumerator.Current);
+ }
+ }
+
+ enumerator = MyStringICollection.GetEnumerator();
+ enumerator.MoveNext();
+ {
+ // Verifying after deserialization.
+ if (enumerator.Current is JsonElement currentJsonElement)
+ {
+ Assert.Equal("Hello", currentJsonElement.GetString());
+ }
+ // Verifying test data.
+ else
+ {
+ Assert.Equal("Hello", enumerator.Current);
+ }
+ }
+
Assert.Equal("Hello", MyStringIEnumerableT.First());
Assert.Equal("Hello", MyStringIListT[0]);
Assert.Equal("Hello", MyStringICollectionT.First());
Assert.Equal("Hello", MyStringIReadOnlyCollectionT.First());
Assert.Equal("Hello", MyStringIReadOnlyListT[0]);
+ Assert.Equal("Hello", MyStringISetT.First());
- Assert.Equal("value", MyStringToStringDict["key"]);
- Assert.Equal("value", MyStringToStringIDict["key"]);
- Assert.Equal("value", MyStringToStringIReadOnlyDict["key"]);
+ enumerator = MyStringToStringIDict.GetEnumerator();
+ enumerator.MoveNext();
+ {
+ // Verifying after deserialization.
+ if (enumerator.Current is JsonElement currentJsonElement)
+ {
+ IEnumerator jsonEnumerator = currentJsonElement.EnumerateObject();
+ jsonEnumerator.MoveNext();
+
+ JsonProperty property = (JsonProperty)jsonEnumerator.Current;
+
+ Assert.Equal("key", property.Name);
+ Assert.Equal("value", property.Value.GetString());
+ }
+ // Verifying test data.
+ else
+ {
+ DictionaryEntry entry = (DictionaryEntry)enumerator.Current;
+ Assert.Equal("key", entry.Key);
+
+ if (entry.Value is JsonElement element)
+ {
+ Assert.Equal("value", element.GetString());
+ }
+ else
+ {
+ Assert.Equal("value", entry.Value);
+ }
+ }
+ }
+
+ Assert.Equal("value", MyStringToStringGenericDict["key"]);
+ Assert.Equal("value", MyStringToStringGenericIDict["key"]);
+ Assert.Equal("value", MyStringToStringGenericIReadOnlyDict["key"]);
Assert.Equal("value", MyStringToStringImmutableDict["key"]);
Assert.Equal("value", MyStringToStringIImmutableDict["key"]);
Assert.Equal("value", MyStringToStringImmutableSortedDict["key"]);
+ Assert.Equal("myKey", MyStringToStringKeyValuePair.Key);
+ Assert.Equal("myValue", MyStringToStringKeyValuePair.Value);
+
Assert.Equal(2, MyStringStackT.Count);
Assert.True(MyStringStackT.Contains("Hello"));
Assert.True(MyStringStackT.Contains("World"));
// 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.Collections.Immutable;
using System.Linq;
public object MyDateTimeArray { get; set; }
public object MyEnumArray { get; set; }
public object MyStringList { get; set; }
+ public object MyStringIEnumerable { get; set; }
+ public object MyStringIList { get; set; }
+ public object MyStringICollection { get; set; }
public object MyStringIEnumerableT { get; set; }
public object MyStringIListT { get; set; }
public object MyStringICollectionT { get; set; }
public object MyStringIReadOnlyCollectionT { get; set; }
public object MyStringIReadOnlyListT { get; set; }
- public object MyStringToStringDict { get; set; }
+ public object MyStringISetT { get; set; }
+ public object MyStringToStringKeyValuePair { get; set; }
public object MyStringToStringIDict { get; set; }
- public object MyStringToStringIReadOnlyDict { get; set; }
+ public object MyStringToStringGenericDict { get; set; }
+ public object MyStringToStringGenericIDict { get; set; }
+ public object MyStringToStringGenericIReadOnlyDict { get; set; }
public object MyStringToStringImmutableDict { get; set; }
public object MyStringToStringIImmutableDict { get; set; }
public object MyStringToStringImmutableSortedDict { get; set; }
@"""MyDateTimeArray"" : [""2019-01-30T12:01:02.0000000Z""]," +
@"""MyEnumArray"" : [2]," + // int by default
@"""MyStringList"" : [""Hello""]," +
+ @"""MyStringIEnumerable"" : [""Hello""]," +
+ @"""MyStringIList"" : [""Hello""]," +
+ @"""MyStringICollection"" : [""Hello""]," +
@"""MyStringIEnumerableT"" : [""Hello""]," +
@"""MyStringIListT"" : [""Hello""]," +
@"""MyStringICollectionT"" : [""Hello""]," +
@"""MyStringIReadOnlyCollectionT"" : [""Hello""]," +
@"""MyStringIReadOnlyListT"" : [""Hello""]," +
- @"""MyStringToStringDict"" : {""key"" : ""value""}," +
+ @"""MyStringISetT"" : [""Hello""]," +
+ @"""MyStringToStringKeyValuePair"" : {""Key"" : ""myKey"", ""Value"" : ""myValue""}," +
@"""MyStringToStringIDict"" : {""key"" : ""value""}," +
- @"""MyStringToStringIReadOnlyDict"" : {""key"" : ""value""}," +
+ @"""MyStringToStringGenericDict"" : {""key"" : ""value""}," +
+ @"""MyStringToStringGenericIDict"" : {""key"" : ""value""}," +
+ @"""MyStringToStringGenericIReadOnlyDict"" : {""key"" : ""value""}," +
@"""MyStringToStringImmutableDict"" : {""key"" : ""value""}," +
@"""MyStringToStringIImmutableDict"" : {""key"" : ""value""}," +
@"""MyStringToStringImmutableSortedDict"" : {""key"" : ""value""}," +
MyEnumArray = new SampleEnum[] { SampleEnum.Two };
MyStringList = new List<string>() { "Hello" };
+
+ MyStringIEnumerable = new string[] { "Hello" };
+ MyStringIList = new string[] { "Hello" };
+ MyStringICollection = new string[] { "Hello" };
+
MyStringIEnumerableT = new string[] { "Hello" };
MyStringIListT = new string[] { "Hello" };
MyStringICollectionT = new string[] { "Hello" };
MyStringIReadOnlyCollectionT = new string[] { "Hello" };
MyStringIReadOnlyListT = new string[] { "Hello" };
+ MyStringIReadOnlyListT = new HashSet<string> { "Hello" };
+
+ MyStringISetT = new HashSet<string> { "Hello" };
- MyStringToStringDict = new Dictionary<string, string> { { "key", "value" } };
+ MyStringToStringKeyValuePair = new KeyValuePair<string, string>("myKey", "myValue");
MyStringToStringIDict = new Dictionary<string, string> { { "key", "value" } };
- MyStringToStringIReadOnlyDict = new Dictionary<string, string> { { "key", "value" } };
- MyStringToStringImmutableDict = ImmutableDictionary.CreateRange((Dictionary<string, string>)MyStringToStringDict);
- MyStringToStringIImmutableDict = ImmutableDictionary.CreateRange((Dictionary<string, string>)MyStringToStringDict);
- MyStringToStringImmutableSortedDict = ImmutableSortedDictionary.CreateRange((Dictionary<string, string>)MyStringToStringDict);
+ MyStringToStringGenericDict = new Dictionary<string, string> { { "key", "value" } };
+ MyStringToStringGenericIDict = new Dictionary<string, string> { { "key", "value" } };
+ MyStringToStringGenericIReadOnlyDict = new Dictionary<string, string> { { "key", "value" } };
+
+ MyStringToStringImmutableDict = ImmutableDictionary.CreateRange((Dictionary<string, string>)MyStringToStringGenericDict);
+ MyStringToStringIImmutableDict = ImmutableDictionary.CreateRange((Dictionary<string, string>)MyStringToStringGenericDict);
+ MyStringToStringImmutableSortedDict = ImmutableSortedDictionary.CreateRange((Dictionary<string, string>)MyStringToStringGenericDict);
MyStringStackT = new Stack<string>(new List<string>() { "Hello", "World" });
MyStringQueueT = new Queue<string>(new List<string>() { "Hello", "World" });
Assert.Equal(SampleEnum.Two, ((SampleEnum[])MyEnumArray)[0]);
Assert.Equal("Hello", ((List<string>)MyStringList)[0]);
+
+ ((IEnumerable)MyStringIEnumerable).GetEnumerator().MoveNext();
+ Assert.Equal("Hello", (string)((IEnumerable)MyStringIEnumerable).GetEnumerator().Current);
+
+ Assert.Equal("Hello", (string)((IList)MyStringIList)[0]);
+
+ ((ICollection)MyStringICollection).GetEnumerator().MoveNext();
+ Assert.Equal("Hello", (string)((ICollection)MyStringICollection).GetEnumerator().Current);
+
Assert.Equal("Hello", ((IEnumerable<string>)MyStringIEnumerableT).First());
Assert.Equal("Hello", ((IList<string>)MyStringIListT)[0]);
Assert.Equal("Hello", ((ICollection<string>)MyStringICollectionT).First());
Assert.Equal("Hello", ((IReadOnlyCollection<string>)MyStringIReadOnlyCollectionT).First());
Assert.Equal("Hello", ((IReadOnlyList<string>)MyStringIReadOnlyListT)[0]);
+ Assert.Equal("Hello", ((ISet<string>)MyStringISetT).First());
+
- Assert.Equal("value", ((Dictionary<string, string>)MyStringToStringDict)["key"]);
- Assert.Equal("value", ((IDictionary<string, string>)MyStringToStringIDict)["key"]);
- Assert.Equal("value", ((IReadOnlyDictionary<string, string>)MyStringToStringIReadOnlyDict)["key"]);
+ IEnumerator enumerator = ((IDictionary)MyStringToStringIDict).GetEnumerator();
+ enumerator.MoveNext();
+
+ DictionaryEntry entry = (DictionaryEntry)(enumerator.Current);
+ Assert.Equal("key", entry.Key);
+ Assert.Equal("value", entry.Value);
+
+ Assert.Equal("value", ((Dictionary<string, string>)MyStringToStringGenericDict)["key"]);
+ Assert.Equal("value", ((IDictionary<string, string>)MyStringToStringGenericIDict)["key"]);
+ Assert.Equal("value", ((IReadOnlyDictionary<string, string>)MyStringToStringGenericIReadOnlyDict)["key"]);
Assert.Equal("value", ((ImmutableDictionary<string, string>)MyStringToStringImmutableDict)["key"]);
Assert.Equal("value", ((IImmutableDictionary<string, string>)MyStringToStringIImmutableDict)["key"]);
Assert.Equal("value", ((ImmutableSortedDictionary<string, string>)MyStringToStringImmutableSortedDict)["key"]);
+ Assert.Equal("myKey", ((KeyValuePair<string, string>)MyStringToStringKeyValuePair).Key);
+ Assert.Equal("myValue", ((KeyValuePair<string, string>)MyStringToStringKeyValuePair).Value);
+
Assert.Equal(2, ((Stack<string>)MyStringStackT).Count);
Assert.True(((Stack<string>)MyStringStackT).Contains("Hello"));
Assert.True(((Stack<string>)MyStringStackT).Contains("World"));
public ICollection<string> MyStringICollectionT { get; set; }
public IReadOnlyCollection<string> MyStringIReadOnlyCollectionT { get; set; }
public IReadOnlyList<string> MyStringIReadOnlyListT { get; set; }
+ public ISet<string> MyStringISetT { get; set; }
public static readonly string s_json = $"{{{s_partialJsonProperties},{s_partialJsonArrays}}}";
public static readonly string s_json_flipped = $"{{{s_partialJsonArrays},{s_partialJsonProperties}}}";
@"""MyStringIListT"" : [""Hello""]," +
@"""MyStringICollectionT"" : [""Hello""]," +
@"""MyStringIReadOnlyCollectionT"" : [""Hello""]," +
- @"""MyStringIReadOnlyListT"" : [""Hello""]";
+ @"""MyStringIReadOnlyListT"" : [""Hello""]," +
+ @"""MyStringISetT"" : [""Hello""]";
public static readonly byte[] s_data = Encoding.UTF8.GetBytes(s_json);
MyStringICollectionT = new string[] { "Hello" };
MyStringIReadOnlyCollectionT = new string[] { "Hello" };
MyStringIReadOnlyListT = new string[] { "Hello" };
+ MyStringISetT = new HashSet<string> { "Hello" };
}
public void Verify()
Assert.Equal("Hello", MyStringICollectionT.First());
Assert.Equal("Hello", MyStringIReadOnlyCollectionT.First());
Assert.Equal("Hello", MyStringIReadOnlyListT[0]);
+ Assert.Equal("Hello", MyStringISetT.First());
}
}
}
// 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.Collections.Immutable;
-using System.Linq;
using Xunit;
namespace System.Text.Json.Serialization.Tests
}
}
+ public class TestClassWithObjectIEnumerable : ITestClass
+ {
+ public IEnumerable MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ SimpleTestClass.s_json + "," +
+ SimpleTestClass.s_json +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ SimpleTestClass obj1 = new SimpleTestClass();
+ obj1.Initialize();
+
+ SimpleTestClass obj2 = new SimpleTestClass();
+ obj2.Initialize();
+
+ MyData = new SimpleTestClass[] { obj1, obj2 };
+ }
+
+ public void Verify()
+ {
+ int count = 0;
+ foreach (object data in MyData)
+ {
+ if (data is JsonElement element)
+ {
+ SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(element.GetRawText());
+ obj.Verify();
+ }
+ else
+ {
+ ((SimpleTestClass)data).Verify();
+ }
+ count++;
+ }
+ Assert.Equal(2, count);
+ }
+ }
+
+ public class TestClassWithObjectIList : ITestClass
+ {
+ public IList MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ SimpleTestClass.s_json + "," +
+ SimpleTestClass.s_json +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ MyData = new List<SimpleTestClass>();
+
+ {
+ SimpleTestClass obj = new SimpleTestClass();
+ obj.Initialize();
+ MyData.Add(obj);
+ }
+
+ {
+ SimpleTestClass obj = new SimpleTestClass();
+ obj.Initialize();
+ MyData.Add(obj);
+ }
+ }
+
+ public void Verify()
+ {
+ int count = 0;
+ foreach (object data in MyData)
+ {
+ if (data is JsonElement element)
+ {
+ SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(element.GetRawText());
+ obj.Verify();
+ }
+ else
+ {
+ ((SimpleTestClass)data).Verify();
+ }
+ count++;
+ }
+ Assert.Equal(2, count);
+ }
+ }
+
+ public class TestClassWithObjectICollection : ITestClass
+ {
+ public ICollection MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ SimpleTestClass.s_json + "," +
+ SimpleTestClass.s_json +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ List<SimpleTestClass> dataTemp = new List<SimpleTestClass>();
+
+ {
+ SimpleTestClass obj = new SimpleTestClass();
+ obj.Initialize();
+ dataTemp.Add(obj);
+ }
+
+ {
+ SimpleTestClass obj = new SimpleTestClass();
+ obj.Initialize();
+ dataTemp.Add(obj);
+ }
+
+ MyData = dataTemp;
+ }
+
+ public void Verify()
+ {
+ int count = 0;
+ foreach (object data in MyData)
+ {
+ if (data is JsonElement element)
+ {
+ SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(element.GetRawText());
+ obj.Verify();
+ }
+ else
+ {
+ ((SimpleTestClass)data).Verify();
+ }
+ count++;
+ }
+ Assert.Equal(2, count);
+ }
+ }
+
public class TestClassWithObjectIReadOnlyCollectionT : ITestClass
{
public IReadOnlyCollection<SimpleTestClass> MyData { get; set; }
}
}
+ public class TestClassWithObjectISetT : ITestClass
+ {
+ public ISet<SimpleTestClass> MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ SimpleTestClass.s_json + "," +
+ SimpleTestClass.s_json +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ SimpleTestClass obj1 = new SimpleTestClass();
+ obj1.Initialize();
+
+ SimpleTestClass obj2 = new SimpleTestClass();
+ obj2.Initialize();
+
+ MyData = new HashSet<SimpleTestClass> { obj1, obj2 };
+ }
+
+ public void Verify()
+ {
+ Assert.Equal(2, MyData.Count);
+
+ foreach (SimpleTestClass obj in MyData)
+ {
+ obj.Verify();
+ }
+ }
+ }
+
public class TestClassWithStringArray : ITestClass
{
public string[] MyData { get; set; }
}
}
+ public class TestClassWithGenericIEnumerable : ITestClass
+ {
+ public IEnumerable MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ @"""Hello""," +
+ @"""World""" +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ MyData = new List<string>
+ {
+ "Hello",
+ "World"
+ };
+
+ int count = 0;
+ foreach (string data in MyData)
+ {
+ count++;
+ }
+ Assert.Equal(2, count);
+ }
+
+ public void Verify()
+ {
+ string[] expected = { "Hello", "World" };
+ int count = 0;
+ foreach (object data in MyData)
+ {
+ if (data is JsonElement element)
+ {
+ Assert.Equal(expected[count], element.GetString());
+ }
+ else
+ {
+ Assert.Equal(expected[count], (string)data);
+ }
+ count++;
+ }
+ Assert.Equal(2, count);
+ }
+ }
+
+ public class TestClassWithGenericIList : ITestClass
+ {
+ public IList MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ @"""Hello""," +
+ @"""World""" +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ MyData = new List<string>
+ {
+ "Hello",
+ "World"
+ };
+ Assert.Equal(2, MyData.Count);
+ }
+
+ public void Verify()
+ {
+ string[] expected = { "Hello", "World" };
+ int count = 0;
+ foreach (object data in MyData)
+ {
+ if (data is JsonElement element)
+ {
+ Assert.Equal(expected[count], element.GetString());
+ }
+ else
+ {
+ Assert.Equal(expected[count], (string)data);
+ }
+ count++;
+ }
+ Assert.Equal(2, count);
+ }
+ }
+
+ public class TestClassWithGenericICollection : ITestClass
+ {
+ public ICollection MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ @"""Hello""," +
+ @"""World""" +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ MyData = new List<string>
+ {
+ "Hello",
+ "World"
+ };
+ Assert.Equal(2, MyData.Count);
+ }
+
+ public void Verify()
+ {
+ string[] expected = { "Hello", "World" };
+ int count = 0;
+ foreach (object data in MyData)
+ {
+ if (data is JsonElement element)
+ {
+ Assert.Equal(expected[count], element.GetString());
+ }
+ else
+ {
+ Assert.Equal(expected[count], (string)data);
+ }
+ count++;
+ }
+ Assert.Equal(2, count);
+ }
+ }
+
public class TestClassWithGenericIEnumerableT : ITestClass
{
public IEnumerable<string> MyData { get; set; }
}
}
+ public class TestClassWithGenericISetT : ITestClass
+ {
+ public ISet<string> MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ @"""Hello""," +
+ @"""World""" +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ MyData = new HashSet<string>
+ {
+ "Hello",
+ "World"
+ };
+ Assert.Equal(2, MyData.Count);
+ }
+
+ public void Verify()
+ {
+ Assert.Equal(2, MyData.Count);
+
+ bool helloSeen = false;
+ bool worldSeen = false;
+
+ foreach (string data in MyData)
+ {
+ if (data == "Hello")
+ {
+ helloSeen = true;
+ }
+ else if (data == "World")
+ {
+ worldSeen = true;
+ }
+ }
+
+ Assert.True(helloSeen && worldSeen);
+ }
+ }
+
public class TestClassWithStringToPrimitiveDictionary : ITestClass
{
public Dictionary<string, int> MyInt32Dict { get; set; }
yield return new object[] { typeof(TestClassWithNestedObjectInner), TestClassWithNestedObjectInner.s_data };
yield return new object[] { typeof(TestClassWithNestedObjectOuter), TestClassWithNestedObjectOuter.s_data };
yield return new object[] { typeof(TestClassWithObjectArray), TestClassWithObjectArray.s_data };
+ yield return new object[] { typeof(TestClassWithObjectIEnumerable), TestClassWithObjectIEnumerable.s_data };
+ yield return new object[] { typeof(TestClassWithObjectIList), TestClassWithObjectIList.s_data };
+ yield return new object[] { typeof(TestClassWithObjectICollection), TestClassWithObjectICollection.s_data };
yield return new object[] { typeof(TestClassWithObjectIEnumerableT), TestClassWithObjectIEnumerableT.s_data };
yield return new object[] { typeof(TestClassWithObjectIListT), TestClassWithObjectIListT.s_data };
yield return new object[] { typeof(TestClassWithObjectICollectionT), TestClassWithObjectICollectionT.s_data };
yield return new object[] { typeof(TestClassWithObjectIReadOnlyCollectionT), TestClassWithObjectIReadOnlyCollectionT.s_data };
yield return new object[] { typeof(TestClassWithObjectIReadOnlyListT), TestClassWithObjectIReadOnlyListT.s_data };
+ yield return new object[] { typeof(TestClassWithObjectISetT), TestClassWithObjectISetT.s_data };
yield return new object[] { typeof(TestClassWithStringArray), TestClassWithStringArray.s_data };
yield return new object[] { typeof(TestClassWithGenericList), TestClassWithGenericList.s_data };
+ yield return new object[] { typeof(TestClassWithGenericIEnumerable), TestClassWithGenericIEnumerable.s_data };
+ yield return new object[] { typeof(TestClassWithGenericIList), TestClassWithGenericIList.s_data };
+ yield return new object[] { typeof(TestClassWithGenericICollection), TestClassWithGenericICollection.s_data };
yield return new object[] { typeof(TestClassWithGenericIEnumerableT), TestClassWithGenericIEnumerableT.s_data };
yield return new object[] { typeof(TestClassWithGenericIListT), TestClassWithGenericIListT.s_data };
yield return new object[] { typeof(TestClassWithGenericICollectionT), TestClassWithGenericICollectionT.s_data };
yield return new object[] { typeof(TestClassWithGenericIReadOnlyCollectionT), TestClassWithGenericIReadOnlyCollectionT.s_data };
yield return new object[] { typeof(TestClassWithGenericIReadOnlyListT), TestClassWithGenericIReadOnlyListT.s_data };
+ yield return new object[] { typeof(TestClassWithGenericISetT), TestClassWithGenericISetT.s_data };
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[] { new TestClassWithNestedObjectInner() };
yield return new object[] { new TestClassWithNestedObjectOuter() };
yield return new object[] { new TestClassWithObjectArray() };
+ yield return new object[] { new TestClassWithObjectIEnumerable() };
+ yield return new object[] { new TestClassWithObjectIList() };
+ yield return new object[] { new TestClassWithObjectICollection() };
yield return new object[] { new TestClassWithObjectIEnumerableT() };
yield return new object[] { new TestClassWithObjectIListT() };
yield return new object[] { new TestClassWithObjectICollectionT() };
yield return new object[] { new TestClassWithObjectIReadOnlyCollectionT() };
yield return new object[] { new TestClassWithObjectIReadOnlyListT() };
+ yield return new object[] { new TestClassWithObjectISetT() };
yield return new object[] { new TestClassWithStringArray() };
yield return new object[] { new TestClassWithGenericList() };
+ yield return new object[] { new TestClassWithGenericIEnumerable() };
+ yield return new object[] { new TestClassWithGenericIList() };
+ yield return new object[] { new TestClassWithGenericICollection() };
yield return new object[] { new TestClassWithGenericIEnumerableT() };
yield return new object[] { new TestClassWithGenericIListT() };
yield return new object[] { new TestClassWithGenericICollectionT() };
yield return new object[] { new TestClassWithGenericIReadOnlyCollectionT() };
yield return new object[] { new TestClassWithGenericIReadOnlyListT() };
+ yield return new object[] { new TestClassWithGenericISetT() };
yield return new object[] { new TestClassWithStringToPrimitiveDictionary() };
yield return new object[] { new TestClassWithObjectIEnumerableConstructibleTypes() };
yield return new object[] { new TestClassWithObjectImmutableTypes() };
}
[Fact]
+ public static void ReadISetTOfISetT_Throws()
+ {
+ ISet<ISet<int>> result = JsonSerializer.Parse<ISet<ISet<int>>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+
+ if (result.First().Contains(1))
+ {
+ Assert.Equal(new HashSet<int> { 1, 2 }, result.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, result.Last());
+ }
+ else
+ {
+ Assert.Equal(new HashSet<int> { 3, 4 }, result.First());
+ Assert.Equal(new HashSet<int> { 1, 2 }, result.Last());
+ }
+ }
+
+ [Fact]
+ public static void ReadISetTOfHashSetT()
+ {
+ ISet<HashSet<int>> result = JsonSerializer.Parse<ISet<HashSet<int>>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+
+ if (result.First().Contains(1))
+ {
+ Assert.Equal(new HashSet<int> { 1, 2 }, result.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, result.Last());
+ }
+ else
+ {
+ Assert.Equal(new HashSet<int> { 3, 4 }, result.First());
+ Assert.Equal(new HashSet<int> { 1, 2 }, result.Last());
+ }
+ }
+
+ [Fact]
+ public static void ReadHashSetTOfISetT()
+ {
+ HashSet<ISet<int>> result = JsonSerializer.Parse<HashSet<ISet<int>>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+
+ if (result.First().Contains(1))
+ {
+ Assert.Equal(new HashSet<int> { 1, 2 }, result.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, result.Last());
+ }
+ else
+ {
+ Assert.Equal(new HashSet<int> { 3, 4 }, result.First());
+ Assert.Equal(new HashSet<int> { 1, 2 }, result.Last());
+ }
+ }
+
+ [Fact]
+ public static void ReadISetTOfArray()
+ {
+ ISet<int[]> result = JsonSerializer.Parse<ISet<int[]>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+
+ if (result.First().Contains(1))
+ {
+ Assert.Equal(new HashSet<int> { 1, 2 }, result.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, result.Last());
+ }
+ else
+ {
+ Assert.Equal(new HashSet<int> { 3, 4 }, result.First());
+ Assert.Equal(new HashSet<int> { 1, 2 }, result.Last());
+ }
+ }
+
+ [Fact]
+ public static void ReadArrayOfISetT()
+ {
+ ISet<int>[] result = JsonSerializer.Parse<ISet<int>[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+
+ Assert.Equal(new HashSet<int> { 1, 2 }, result.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, result.Last());
+ }
+
+ [Fact]
+ public static void ReadPrimitiveISetT()
+ {
+ ISet<int> result = JsonSerializer.Parse<ISet<int>>(Encoding.UTF8.GetBytes(@"[1,2]"));
+
+ Assert.Equal(new HashSet<int> { 1, 2 }, result);
+
+ result = JsonSerializer.Parse<ISet<int>>(Encoding.UTF8.GetBytes(@"[]"));
+ Assert.Equal(0, result.Count());
+ }
+
+ [Fact]
public static void StackTOfStackT()
{
Stack<Stack<int>> result = JsonSerializer.Parse<Stack<Stack<int>>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
result = JsonSerializer.Parse<SortedSet<int>>(Encoding.UTF8.GetBytes(@"[]"));
Assert.Equal(0, result.Count());
}
+
+ [Fact]
+ public static void ReadPrimitiveKeyValuePair()
+ {
+ KeyValuePair<string, int> input = JsonSerializer.Parse<KeyValuePair<string, int>>(@"{""Key"": 123}");
+
+ Assert.Equal(input.Key, "Key");
+ Assert.Equal(input.Value, 123);
+
+ input = JsonSerializer.Parse<KeyValuePair<string, int>>(@"{""Key"": ""Key"", ""Value"": 123}");
+
+ Assert.Equal(input.Key, "Key");
+ Assert.Equal(input.Value, 123);
+
+ // Invalid form: extra property
+ Assert.Throws<JsonException>(() => JsonSerializer.Parse<KeyValuePair<string, int>>(@"{""Key"": ""Key"", ""Value"": 123, ""Value2"": 456}"));
+
+ // Invalid form: does not contain both Key and Value properties
+ Assert.Throws<JsonException>(() => JsonSerializer.Parse<KeyValuePair<string, int>>(@"{""Key"": ""Key"", ""Val"": 123"));
+ }
+
+ [Fact]
+ public static void ReadListOfKeyValuePair()
+ {
+ List<KeyValuePair<string, int>> input = JsonSerializer.Parse<List<KeyValuePair<string, int>>>(@"[{""123"":123},{""456"": 456}]");
+
+ Assert.Equal(2, input.Count);
+ Assert.Equal("123", input[0].Key);
+ Assert.Equal(123, input[0].Value);
+ Assert.Equal("456", input[1].Key);
+ Assert.Equal(456, input[1].Value);
+
+ input = JsonSerializer.Parse<List<KeyValuePair<string, int>>>(@"[{""Key"":""123"",""Value"": 123},{""Key"": ""456"",""Value"": 456}]");
+
+ Assert.Equal(2, input.Count);
+ Assert.Equal("123", input[0].Key);
+ Assert.Equal(123, input[0].Value);
+ Assert.Equal("456", input[1].Key);
+ Assert.Equal(456, input[1].Value);
+ }
+
+ [Fact]
+ public static void ReadKeyValuePairOfList()
+ {
+ KeyValuePair<string, List<int>> input = JsonSerializer.Parse<KeyValuePair<string, List<int>>>(@"{""Key"":[1, 2, 3]}");
+
+ Assert.Equal("Key", input.Key);
+ Assert.Equal(3, input.Value.Count);
+ Assert.Equal(1, input.Value[0]);
+ Assert.Equal(2, input.Value[1]);
+ Assert.Equal(3, input.Value[2]);
+
+ input = JsonSerializer.Parse<KeyValuePair<string, List<int>>>(@"{""Key"": ""Key"", ""Value"": [1, 2, 3]}");
+
+ Assert.Equal("Key", input.Key);
+ Assert.Equal(3, input.Value.Count);
+ Assert.Equal(1, input.Value[0]);
+ Assert.Equal(2, input.Value[1]);
+ Assert.Equal(3, input.Value[2]);
+ }
+
+ [Fact]
+ public static void ReadKeyValuePairOfKeyValuePair()
+ {
+ KeyValuePair<string, KeyValuePair<string, int>> input = JsonSerializer.Parse<KeyValuePair<string, KeyValuePair<string, int>>>(@"{""Key"":{""Key"":1}}");
+
+ Assert.Equal("Key", input.Key);
+ Assert.Equal("Key", input.Value.Key);
+ Assert.Equal(1, input.Value.Value);
+ }
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Linq;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class ValueTests
+ {
+ public static void ReadGenericIEnumerableOfIEnumerable()
+ {
+ IEnumerable<IEnumerable> result = JsonSerializer.Parse<IEnumerable<IEnumerable>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (IEnumerable ie in result)
+ {
+ foreach (JsonElement i in ie)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadIEnumerableOfArray()
+ {
+ IEnumerable result = JsonSerializer.Parse<IEnumerable>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (JsonElement arr in result)
+ {
+ foreach (JsonElement i in arr.EnumerateArray())
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadArrayOfIEnumerable()
+ {
+ IEnumerable[] result = JsonSerializer.Parse<IEnumerable[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (IEnumerable arr in result)
+ {
+ foreach (JsonElement i in arr)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadPrimitiveIEnumerable()
+ {
+ IEnumerable result = JsonSerializer.Parse<IEnumerable>(Encoding.UTF8.GetBytes(@"[1,2]"));
+ int expected = 1;
+
+ foreach (JsonElement i in result)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+
+ result = JsonSerializer.Parse<IEnumerable>(Encoding.UTF8.GetBytes(@"[]"));
+
+ int count = 0;
+ IEnumerator e = result.GetEnumerator();
+ while (e.MoveNext())
+ {
+ count++;
+ }
+ Assert.Equal(0, count);
+ }
+
+ public static void ReadGenericIListOfIList()
+ {
+ IList<IList> result = JsonSerializer.Parse<IList<IList>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (IList list in result)
+ {
+ foreach (JsonElement i in list)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadIListOfArray()
+ {
+ IList result = JsonSerializer.Parse<IList>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (JsonElement arr in result)
+ {
+ foreach (JsonElement i in arr.EnumerateArray())
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadArrayOfIList()
+ {
+ IList[] result = JsonSerializer.Parse<IList[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (IList arr in result)
+ {
+ foreach (JsonElement i in arr)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadPrimitiveIList()
+ {
+ IList result = JsonSerializer.Parse<IList>(Encoding.UTF8.GetBytes(@"[1,2]"));
+ int expected = 1;
+
+ foreach (JsonElement i in result)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+
+ result = JsonSerializer.Parse<IList>(Encoding.UTF8.GetBytes(@"[]"));
+
+ int count = 0;
+ IEnumerator e = result.GetEnumerator();
+ while (e.MoveNext())
+ {
+ count++;
+ }
+ Assert.Equal(0, count);
+ }
+
+ public static void ReadGenericICollectionOfICollection()
+ {
+ ICollection<ICollection> result = JsonSerializer.Parse<ICollection<ICollection>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (ICollection ie in result)
+ {
+ foreach (JsonElement i in ie)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadICollectionOfArray()
+ {
+ ICollection result = JsonSerializer.Parse<ICollection>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (JsonElement arr in result)
+ {
+ foreach (JsonElement i in arr.EnumerateArray())
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadArrayOfICollection()
+ {
+ ICollection[] result = JsonSerializer.Parse<ICollection[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (ICollection arr in result)
+ {
+ foreach (JsonElement i in arr)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadPrimitiveICollection()
+ {
+ ICollection result = JsonSerializer.Parse<ICollection>(Encoding.UTF8.GetBytes(@"[1,2]"));
+ int expected = 1;
+
+ foreach (JsonElement i in result)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+
+ result = JsonSerializer.Parse<ICollection>(Encoding.UTF8.GetBytes(@"[]"));
+
+ int count = 0;
+ IEnumerator e = result.GetEnumerator();
+ while (e.MoveNext())
+ {
+ count++;
+ }
+ Assert.Equal(0, count);
+ }
+
+ public static void ReadGenericStackOfStack()
+ {
+ Stack<Stack> result = JsonSerializer.Parse<Stack<Stack>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 4;
+
+ foreach (Stack stack in result)
+ {
+ foreach (JsonElement i in stack)
+ {
+ Assert.Equal(expected--, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadStackOfArray()
+ {
+ Stack result = JsonSerializer.Parse<Stack>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 3;
+
+ foreach (JsonElement arr in result)
+ {
+ foreach (JsonElement i in arr.EnumerateArray())
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ expected = 1;
+ }
+ }
+
+ [Fact]
+ public static void ReadArrayOfStack()
+ {
+ Stack[] result = JsonSerializer.Parse<Stack[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 2;
+
+ foreach (Stack arr in result)
+ {
+ foreach (JsonElement i in arr)
+ {
+ Assert.Equal(expected--, i.GetInt32());
+ }
+ expected = 4;
+ }
+ }
+
+ [Fact]
+ public static void ReadPrimitiveStack()
+ {
+ Stack result = JsonSerializer.Parse<Stack>(Encoding.UTF8.GetBytes(@"[1,2]"));
+ int expected = 2;
+
+ foreach (JsonElement i in result)
+ {
+ Assert.Equal(expected--, i.GetInt32());
+ }
+
+ result = JsonSerializer.Parse<Stack>(Encoding.UTF8.GetBytes(@"[]"));
+
+ int count = 0;
+ IEnumerator e = result.GetEnumerator();
+ while (e.MoveNext())
+ {
+ count++;
+ }
+ Assert.Equal(0, count);
+ }
+
+ public static void ReadGenericQueueOfQueue()
+ {
+ Queue<Queue> result = JsonSerializer.Parse<Queue<Queue>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (Queue ie in result)
+ {
+ foreach (JsonElement i in ie)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadQueueOfArray()
+ {
+ Queue result = JsonSerializer.Parse<Queue>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (JsonElement arr in result)
+ {
+ foreach (JsonElement i in arr.EnumerateArray())
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadArrayOfQueue()
+ {
+ Queue[] result = JsonSerializer.Parse<Queue[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (Queue arr in result)
+ {
+ foreach (JsonElement i in arr)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadPrimitiveQueue()
+ {
+ Queue result = JsonSerializer.Parse<Queue>(Encoding.UTF8.GetBytes(@"[1,2]"));
+ int expected = 1;
+
+ foreach (JsonElement i in result)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+
+ result = JsonSerializer.Parse<Queue>(Encoding.UTF8.GetBytes(@"[]"));
+
+ int count = 0;
+ IEnumerator e = result.GetEnumerator();
+ while (e.MoveNext())
+ {
+ count++;
+ }
+ Assert.Equal(0, count);
+ }
+
+ [Fact]
+ public static void ReadArrayListOfArray()
+ {
+ ArrayList result = JsonSerializer.Parse<ArrayList>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (JsonElement arr in result)
+ {
+ foreach (JsonElement i in arr.EnumerateArray())
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadArrayOfArrayList()
+ {
+ ArrayList[] result = JsonSerializer.Parse<ArrayList[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ int expected = 1;
+
+ foreach (ArrayList arr in result)
+ {
+ foreach (JsonElement i in arr)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+ }
+ }
+
+ [Fact]
+ public static void ReadPrimitiveArrayList()
+ {
+ ArrayList result = JsonSerializer.Parse<ArrayList>(Encoding.UTF8.GetBytes(@"[1,2]"));
+ int expected = 1;
+
+ foreach (JsonElement i in result)
+ {
+ Assert.Equal(expected++, i.GetInt32());
+ }
+
+ result = JsonSerializer.Parse<ArrayList>(Encoding.UTF8.GetBytes(@"[]"));
+
+ int count = 0;
+ IEnumerator e = result.GetEnumerator();
+ while (e.MoveNext())
+ {
+ count++;
+ }
+ Assert.Equal(0, count);
+ }
+ }
+}
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
+using System.Linq;
using Xunit;
namespace System.Text.Json.Serialization.Tests
}
[Fact]
+ public static void WriteISetTOfISetT()
+ {
+ ISet<ISet<int>> input = new HashSet<ISet<int>>
+ {
+ new HashSet<int>() { 1, 2 },
+ new HashSet<int>() { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+
+ // Because order isn't guaranteed, roundtrip data to ensure write was accurate.
+ input = JsonSerializer.Parse<ISet<ISet<int>>>(json);
+
+ if (input.First().Contains(1))
+ {
+ Assert.Equal(new HashSet<int> { 1, 2 }, input.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, input.Last());
+ }
+ else
+ {
+ Assert.Equal(new HashSet<int> { 3, 4 }, input.First());
+ Assert.Equal(new HashSet<int> { 1, 2 }, input.Last());
+ }
+ }
+
+ [Fact]
+ public static void WriteISetTOfHashSetT()
+ {
+ ISet<HashSet<int>> input = new HashSet<HashSet<int>>
+ {
+ new HashSet<int>() { 1, 2 },
+ new HashSet<int>() { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+
+ // Because order isn't guaranteed, roundtrip data to ensure write was accurate.
+ input = JsonSerializer.Parse<ISet<HashSet<int>>>(json);
+
+ if (input.First().Contains(1))
+ {
+ Assert.Equal(new HashSet<int> { 1, 2 }, input.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, input.Last());
+ }
+ else
+ {
+ Assert.Equal(new HashSet<int> { 3, 4 }, input.First());
+ Assert.Equal(new HashSet<int> { 1, 2 }, input.Last());
+ }
+ }
+
+ [Fact]
+ public static void WriteHashSetTOfISetT()
+ {
+ HashSet<ISet<int>> input = new HashSet<ISet<int>>
+ {
+ new HashSet<int>() { 1, 2 },
+ new HashSet<int>() { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+
+ // Because order isn't guaranteed, roundtrip data to ensure write was accurate.
+ input = JsonSerializer.Parse<HashSet<ISet<int>>>(json);
+
+ if (input.First().Contains(1))
+ {
+ Assert.Equal(new HashSet<int> { 1, 2 }, input.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, input.Last());
+ }
+ else
+ {
+ Assert.Equal(new HashSet<int> { 3, 4 }, input.First());
+ Assert.Equal(new HashSet<int> { 1, 2 }, input.Last());
+ }
+ }
+
+ [Fact]
+ public static void WriteISetTOfArray()
+ {
+ ISet<int[]> input = new HashSet<int[]>
+ {
+ new int[] { 1, 2 },
+ new int[] { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.True(json.Contains("[1,2]"));
+ Assert.True(json.Contains("[3,4]"));
+ }
+
+ [Fact]
+ public static void WriteArrayOfISetT()
+ {
+ ISet<int>[] input = new HashSet<int>[2];
+ input[0] = new HashSet<int>() { 1, 2 };
+ input[1] = new HashSet<int>() { 3, 4 };
+
+ string json = JsonSerializer.ToString(input);
+
+ // Because order isn't guaranteed, roundtrip data to ensure write was accurate.
+ input = JsonSerializer.Parse<ISet<int>[]>(json);
+ Assert.Equal(new HashSet<int> { 1, 2 }, input.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, input.Last());
+ }
+
+ [Fact]
+ public static void WritePrimitiveISetT()
+ {
+ ISet<int> input = new HashSet<int> { 1, 2 };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.True(json == "[1,2]" || json == "[2,1]");
+ }
+
+ [Fact]
public static void WriteStackTOfStackT()
{
Stack<Stack<int>> input = new Stack<Stack<int>>(new List<Stack<int>>
});
string json = JsonSerializer.ToString(input);
- Assert.Equal("[[1,2],[3,4]]", json);
+
+ // Because order isn't guaranteed, roundtrip data to ensure write was accurate.
+ input = JsonSerializer.Parse<HashSet<HashSet<int>>>(json);
+
+ if (input.First().Contains(1))
+ {
+ Assert.Equal(new HashSet<int> { 1, 2 }, input.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, input.Last());
+ }
+ else
+ {
+ Assert.Equal(new HashSet<int> { 3, 4 }, input.First());
+ Assert.Equal(new HashSet<int> { 1, 2 }, input.Last());
+ }
}
[Fact]
});
string json = JsonSerializer.ToString(input);
- Assert.Equal("[[1,2],[3,4]]", json);
+ Assert.True(json.Contains("[1,2]"));
+ Assert.True(json.Contains("[3,4]"));
}
[Fact]
input[1] = new HashSet<int>(new List<int> { 3, 4 });
string json = JsonSerializer.ToString(input);
- Assert.Equal("[[1,2],[3,4]]", json);
+
+ // Because order isn't guaranteed, roundtrip data to ensure write was accurate.
+ input = JsonSerializer.Parse<HashSet<int>[]>(json);
+ Assert.Equal(new HashSet<int> { 1, 2 }, input.First());
+ Assert.Equal(new HashSet<int> { 3, 4 }, input.Last());
}
[Fact]
HashSet<int> input = new HashSet<int>(new List<int> { 1, 2 });
string json = JsonSerializer.ToString(input);
- Assert.Equal("[1,2]", json);
+ Assert.True(json == "[1,2]" || json == "[2,1]");
}
[Fact]
string json = JsonSerializer.ToString(input);
Assert.Equal("[1,2]", json);
}
+
+ [Fact]
+ public static void WritePrimitiveKeyValuePair()
+ {
+ KeyValuePair<string, int> input = new KeyValuePair<string, int>("Key", 123) ;
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal(@"{""Key"":""Key"",""Value"":123}", json);
+ }
+
+ [Fact]
+ public static void WriteListOfKeyValuePair()
+ {
+ List<KeyValuePair<string, int>> input = new List<KeyValuePair<string, int>>
+ {
+ new KeyValuePair<string, int>("123", 123),
+ new KeyValuePair<string, int>("456", 456)
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal(@"[{""Key"":""123"",""Value"":123},{""Key"":""456"",""Value"":456}]", json);
+ }
+
+ [Fact]
+ public static void WriteKeyValuePairOfList()
+ {
+ KeyValuePair<string, List<int>> input = new KeyValuePair<string, List<int>>("Key", new List<int> { 1, 2, 3 });
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal(@"{""Key"":""Key"",""Value"":[1,2,3]}", json);
+ }
+
+ [Fact]
+ public static void WriteKeyValuePairOfKeyValuePair()
+ {
+ KeyValuePair<string, KeyValuePair<string, int>> input = new KeyValuePair<string, KeyValuePair<string, int>>(
+ "Key", new KeyValuePair<string, int>("Key", 1));
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal(@"{""Key"":""Key"",""Value"":{""Key"":""Key"",""Value"":1}}", json);
+ }
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// 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 Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class ValueTests
+ {
+ [Fact]
+ public static void WriteIEnumerableOfIEnumerable()
+ {
+ IEnumerable input = new List<List<int>>
+ {
+ new List<int>() { 1, 2 },
+ new List<int>() { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ public static void WriteGenericIEnumerableOfIEnumerable()
+ {
+ IEnumerable<IEnumerable> input = new List<IEnumerable>
+ {
+ new List<int>() { 1, 2 },
+ new List<int>() { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WriteArrayOfIEnumerable()
+ {
+ IEnumerable[] input = new IEnumerable[2];
+ input[0] = new List<int>() { 1, 2 };
+ input[1] = new List<int>() { 3, 4 };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WritePrimitiveIEnumerable()
+ {
+ IEnumerable input = new List<int> { 1, 2 };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[1,2]", json);
+ }
+
+ [Fact]
+ public static void WriteIListOfIList()
+ {
+ IList input = new List<IList>
+ {
+ new List<int>() { 1, 2 },
+ new List<int>() { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ public static void WriteIListGenericOfIList()
+ {
+ IList<IList> input = new List<IList>
+ {
+ new List<int>() { 1, 2 },
+ new List<int>() { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WriteArrayOfIList()
+ {
+ IList[] input = new IList[2];
+ input[0] = new List<int>() { 1, 2 };
+ input[1] = new List<int>() { 3, 4 };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WritePrimitiveIList()
+ {
+ IList input = new List<int> { 1, 2 };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[1,2]", json);
+ }
+
+ [Fact]
+ public static void WriteICollectionOfICollection()
+ {
+ ICollection input = new List<ICollection>
+ {
+ new List<int>() { 1, 2 },
+ new List<int>() { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ public static void WriteGenericICollectionOfICollection()
+ {
+ ICollection<ICollection> input = new List<ICollection>
+ {
+ new List<int>() { 1, 2 },
+ new List<int>() { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WriteArrayOfICollection()
+ {
+ ICollection[] input = new List<int>[2];
+ input[0] = new List<int>() { 1, 2 };
+ input[1] = new List<int>() { 3, 4 };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WritePrimitiveICollection()
+ {
+ ICollection input = new List<int> { 1, 2 };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[1,2]", json);
+ }
+
+ [Fact]
+ public static void WriteStackOfStack()
+ {
+ Stack input = new Stack();
+ input.Push(new Stack(new List<int>() { 1, 2 }));
+ input.Push(new Stack(new List<int>() { 3, 4 }));
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[4,3],[2,1]]", json);
+ }
+
+ public static void WriteGenericStackOfStack()
+ {
+ Stack<Stack> input = new Stack<Stack>();
+ input.Push(new Stack(new List<int>() { 1, 2 }));
+ input.Push(new Stack(new List<int>() { 3, 4 }));
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[4,3],[2,1]]", json);
+ }
+
+ [Fact]
+ public static void WriteArrayOfStack()
+ {
+ Stack[] input = new Stack[2];
+ input[0] = new Stack(new List<int>() { 1, 2 });
+ input[1] = new Stack(new List<int>() { 3, 4 });
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[2,1],[4,3]]", json);
+ }
+
+ [Fact]
+ public static void WritePrimitiveStack()
+ {
+ Stack input = new Stack( new List<int> { 1, 2 });
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[2,1]", json);
+ }
+
+ [Fact]
+ public static void WriteQueueOfQueue()
+ {
+ Queue input = new Queue();
+ input.Enqueue(new Queue(new List<int>() { 1, 2 }));
+ input.Enqueue(new Queue(new List<int>() { 3, 4 }));
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ public static void WriteGenericQueueOfQueue()
+ {
+ Queue<Queue> input = new Queue<Queue>();
+ input.Enqueue(new Queue(new List<int>() { 1, 2 }));
+ input.Enqueue(new Queue(new List<int>() { 3, 4 }));
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WriteArrayOfQueue()
+ {
+ Queue[] input = new Queue[2];
+ input[0] = new Queue(new List<int>() { 1, 2 });
+ input[1] = new Queue(new List<int>() { 3, 4 });
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WritePrimitiveQueue()
+ {
+ Queue input = new Queue(new List<int> { 1, 2 });
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[1,2]", json);
+ }
+
+ [Fact]
+ public static void WriteArrayListOfArrayList()
+ {
+ ArrayList input = new ArrayList
+ {
+ new ArrayList(new List<int>() { 1, 2 }),
+ new ArrayList(new List<int>() { 3, 4 })
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WriteArrayOfArrayList()
+ {
+ ArrayList[] input = new ArrayList[2];
+ input[0] = new ArrayList(new List<int>() { 1, 2 });
+ input[1] = new ArrayList(new List<int>() { 3, 4 });
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WritePrimitiveArrayList()
+ {
+ ArrayList input = new ArrayList(new List<int> { 1, 2 });
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[1,2]", json);
+ }
+ }
+}
<Compile Include="Serialization\Value.ReadTests.cs" />
<Compile Include="Serialization\Value.ReadTests.GenericCollections.cs" />
<Compile Include="Serialization\Value.ReadTests.ImmutableCollections.cs" />
+ <Compile Include="Serialization\Value.ReadTests.NonGenericCollections.cs" />
<Compile Include="Serialization\Value.WriteTests.cs" />
<Compile Include="Serialization\Value.WriteTests.GenericCollections.cs" />
<Compile Include="Serialization\Value.WriteTests.ImmutableCollections.cs" />
+ <Compile Include="Serialization\Value.WriteTests.NonGenericCollections.cs" />
<Compile Include="TestCaseType.cs" />
<Compile Include="TestClasses.ClassWithComplexObjects.cs" />
<Compile Include="Utf8JsonReaderTests.cs" />