Add back KeyValuePair functionality (dotnet/corefx#38700)
authorSteve Harter <steveharter@users.noreply.github.com>
Wed, 19 Jun 2019 23:57:42 +0000 (16:57 -0700)
committerGitHub <noreply@github.com>
Wed, 19 Jun 2019 23:57:42 +0000 (16:57 -0700)
Commit migrated from https://github.com/dotnet/corefx/commit/98a5211f1e2b14ab42e52c28f9ca54d41d8e8751

28 files changed:
src/libraries/System.Text.Json/ref/System.Text.Json.cs
src/libraries/System.Text.Json/src/System.Text.Json.csproj
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ClassType.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonKeyValuePairConverter.cs [new file with mode: 0644]
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterKeyValuePair.cs [new file with mode: 0644]
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoNotNullable.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoNotNullableContravariant.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleArray.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleDictionary.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleObject.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleEnumerable.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleObject.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Utf8JsonWriter.cs [new file with mode: 0644]
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs
src/libraries/System.Text.Json/tests/Serialization/Value.ReadTests.GenericCollections.cs
src/libraries/System.Text.Json/tests/Serialization/Value.WriteTests.GenericCollections.cs

index fe5ad5b..e1f0bc5 100644 (file)
@@ -180,6 +180,8 @@ namespace System.Text.Json
         public static byte[] ToUtf8Bytes<TValue>(TValue value, System.Text.Json.JsonSerializerOptions options = null) { throw null; }
         public static System.Threading.Tasks.Task WriteAsync(System.IO.Stream utf8Json, object value, System.Type type, System.Text.Json.JsonSerializerOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
         public static System.Threading.Tasks.Task WriteAsync<TValue>(System.IO.Stream utf8Json, TValue value, System.Text.Json.JsonSerializerOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+        public static void WriteValue(System.Text.Json.Utf8JsonWriter writer, object value, System.Type type, System.Text.Json.JsonSerializerOptions options = null) { }
+        public static void WriteValue<TValue>(System.Text.Json.Utf8JsonWriter writer, TValue value, System.Text.Json.JsonSerializerOptions options = null) { }
     }
     public sealed partial class JsonSerializerOptions
     {
index cc43052..9298c9f 100644 (file)
@@ -55,6 +55,8 @@
     <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\JsonEnumConverter.cs" />
+    <Compile Include="System\Text\Json\Serialization\Converters\JsonKeyValuePairConverter.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\Converters\JsonValueConverterDateTimeOffset.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterDecimal.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterDouble.cs" />
-    <Compile Include="System\Text\Json\Serialization\Converters\JsonEnumConverter.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterEnum.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterGuid.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterInt16.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterInt32.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterInt64.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterJsonElement.cs" />
+    <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterKeyValuePair.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterObject.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterSByte.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterSingle.cs" />
     <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Helpers.cs" />
     <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Stream.cs" />
     <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.String.cs" />
+    <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Utf8JsonWriter.cs" />
     <Compile Include="System\Text\Json\Serialization\JsonSerializerOptions.cs" />
     <Compile Include="System\Text\Json\Serialization\JsonSerializerOptions.Converters.cs" />
     <Compile Include="System\Text\Json\Serialization\MemberAccessor.cs" />
index 18e656f..b7cbf37 100644 (file)
@@ -22,9 +22,5 @@ namespace System.Text.Json
         // 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,
     }
 }
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonKeyValuePairConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonKeyValuePairConverter.cs
new file mode 100644 (file)
index 0000000..833bb0a
--- /dev/null
@@ -0,0 +1,36 @@
+// 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.Generic;
+using System.Reflection;
+
+namespace System.Text.Json.Serialization.Converters
+{
+    internal sealed class JsonKeyValuePairConverter : JsonConverterFactory
+    {
+        public override bool CanConvert(Type typeToConvert)
+        {
+            if (!typeToConvert.IsGenericType)
+                return false;
+
+            Type generic = typeToConvert.GetGenericTypeDefinition();
+            return (generic == typeof(KeyValuePair<,>));
+        }
+
+        protected override JsonConverter CreateConverter(Type type)
+        {
+            Type keyType = type.GetGenericArguments()[0];
+            Type valueType = type.GetGenericArguments()[1];
+
+            JsonConverter converter = (JsonConverter)Activator.CreateInstance(
+                typeof(JsonKeyValuePairConverter<,>).MakeGenericType(new Type[] { keyType, valueType }),
+                BindingFlags.Instance | BindingFlags.Public,
+                binder: null,
+                args: null,
+                culture: null);
+
+            return converter;
+        }
+    }
+}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterKeyValuePair.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterKeyValuePair.cs
new file mode 100644 (file)
index 0000000..7b4a6df
--- /dev/null
@@ -0,0 +1,141 @@
+// 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.Generic;
+
+namespace System.Text.Json.Serialization.Converters
+{
+    internal sealed class JsonKeyValuePairConverter<TKey, TValue> : JsonConverter<KeyValuePair<TKey, TValue>>
+    {
+        private const string KeyName = "Key";
+        private const string ValueName = "Value";
+
+        private static readonly JsonEncodedText _keyName = JsonEncodedText.Encode(KeyName);
+        private static readonly JsonEncodedText _valueName = JsonEncodedText.Encode(ValueName);
+
+        public override KeyValuePair<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        {
+            if (reader.TokenType != JsonTokenType.StartObject)
+            {
+                ThrowHelper.ThrowJsonException();
+            }
+
+            TKey k = default;
+            bool keySet = false;
+
+            TValue v = default;
+            bool valueSet = false;
+
+            // Get the first property.
+            reader.Read();
+            if (reader.TokenType != JsonTokenType.PropertyName)
+            {
+                ThrowHelper.ThrowJsonException();
+            }
+
+            string propertyName = reader.GetString();
+            if (propertyName == KeyName)
+            {
+                k = ReadProperty<TKey>(ref reader, options);
+                keySet = true;
+            }
+            else if (propertyName == ValueName)
+            {
+                v = ReadProperty<TValue>(ref reader, options);
+                valueSet = true;
+            }
+            else
+            {
+                ThrowHelper.ThrowJsonException();
+            }
+
+            // Get the second property.
+            reader.Read();
+            if (reader.TokenType != JsonTokenType.PropertyName)
+            {
+                ThrowHelper.ThrowJsonException();
+            }
+
+            propertyName = reader.GetString();
+            if (propertyName == ValueName)
+            {
+                v = ReadProperty<TValue>(ref reader, options);
+                valueSet = true;
+            }
+            else if (propertyName == KeyName)
+            {
+                k = ReadProperty<TKey>(ref reader, options);
+                keySet = true;
+            }
+            else
+            {
+                ThrowHelper.ThrowJsonException();
+            }
+
+            if (!keySet || !valueSet)
+            {
+                ThrowHelper.ThrowJsonException();
+            }
+
+            reader.Read();
+
+            if (reader.TokenType != JsonTokenType.EndObject)
+            {
+                ThrowHelper.ThrowJsonException();
+            }
+
+            return new KeyValuePair<TKey, TValue>(k, v);
+        }
+
+        private T ReadProperty<T>(ref Utf8JsonReader reader, JsonSerializerOptions options)
+        {
+            T k;
+            Type typeToConvert = typeof(T);
+
+            JsonConverter<T> keyConverter = options.GetConverter(typeToConvert) as JsonConverter<T>;
+            if (keyConverter == null)
+            {
+                k = JsonSerializer.ReadValue<T>(ref reader, options);
+            }
+            else
+            {
+                reader.Read();
+                k = keyConverter.Read(ref reader, typeToConvert, options);
+            }
+
+            return k;
+        }
+
+        private void WriteProperty<T>(Utf8JsonWriter writer, T value, JsonEncodedText name, JsonSerializerOptions options)
+        {
+            JsonConverter<T> keyConverter = options.GetConverter(typeof(T)) as JsonConverter<T>;
+            if (keyConverter == null)
+            {
+                // todo: set property name on writer once that functionality exists
+                // JsonSerializer.WriteValue<T>(writer, value, options);
+                throw new NotImplementedException();
+            }
+            else
+            {
+                keyConverter.Write(writer, value, name, options);
+            }
+        }
+
+        public override void Write(Utf8JsonWriter writer, KeyValuePair<TKey, TValue> value, JsonSerializerOptions options)
+        {
+            writer.WriteStartObject();
+            WriteProperty(writer, value.Key, _keyName, options);
+            WriteProperty(writer, value.Value, _valueName, options);
+            writer.WriteEndObject();
+        }
+
+        public override void Write(Utf8JsonWriter writer, KeyValuePair<TKey, TValue> value, JsonEncodedText propertyName, JsonSerializerOptions options)
+        {
+            writer.WriteStartObject(propertyName);
+            WriteProperty(writer, value.Key, _keyName, options);
+            WriteProperty(writer, value.Value, _valueName, options);
+            writer.WriteEndObject();
+        }
+    }
+}
index e93474b..e7a112d 100644 (file)
@@ -81,7 +81,6 @@ namespace System.Text.Json
                 case ClassType.Enumerable:
                 case ClassType.Dictionary:
                 case ClassType.IDictionaryConstructible:
-                case ClassType.KeyValuePair:
                 case ClassType.Unknown:
                     collectionElementType = GetElementType(runtimePropertyType, parentClassType, propertyInfo, options);
                     break;
index 2aa3d30..7488a8f 100644 (file)
@@ -154,39 +154,6 @@ namespace System.Text.Json
                         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, options);
-
-                        // 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.
@@ -324,13 +291,6 @@ namespace System.Text.Json
             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);
@@ -416,10 +376,10 @@ namespace System.Text.Json
             return key;
         }
 
-        // Return the element type of the IEnumerable or KeyValuePair, or return null if not an IEnumerable or KeyValuePair.
+        // Return the element type of the IEnumerable or return null if not an IEnumerable.
         public static Type GetElementType(Type propertyType, Type parentType, MemberInfo memberInfo, JsonSerializerOptions options)
         {
-            if (!typeof(IEnumerable).IsAssignableFrom(propertyType) && !IsKeyValuePair(propertyType))
+            if (!typeof(IEnumerable).IsAssignableFrom(propertyType))
             {
                 return null;
             }
@@ -437,7 +397,7 @@ namespace System.Text.Json
                 Type[] args = propertyType.GetGenericArguments();
                 ClassType classType = GetClassType(propertyType, options);
 
-                if ((classType == ClassType.Dictionary || classType == ClassType.IDictionaryConstructible || classType == ClassType.KeyValuePair) &&
+                if ((classType == ClassType.Dictionary || classType == ClassType.IDictionaryConstructible) &&
                     args.Length >= 2 && // It is >= 2 in case there is a IDictionary<TKey, TValue, TSomeExtension>.
                     args[0].UnderlyingSystemType == typeof(string))
                 {
@@ -493,11 +453,6 @@ namespace System.Text.Json
                 return ClassType.Dictionary;
             }
 
-            if (IsKeyValuePair(type))
-            {
-                return ClassType.KeyValuePair;
-            }
-
             if (typeof(IEnumerable).IsAssignableFrom(type))
             {
                 return ClassType.Enumerable;
@@ -593,10 +548,5 @@ namespace System.Text.Json
                     return false;
             }
         }
-
-        public static bool IsKeyValuePair(Type type)
-        {
-            return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>);
-        }
     }
 }
index 1553977..5b10e22 100644 (file)
@@ -72,8 +72,6 @@ namespace System.Text.Json
 
         public abstract IDictionary CreateImmutableDictionaryInstance(Type collectionType, string delegateKey, IDictionary sourceDictionary, string propertyPath, JsonSerializerOptions options);
 
-        public abstract ValueType CreateKeyValuePairInstance(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options);
-
         public Type DeclaredPropertyType { get; private set; }
 
         private void DeterminePropertyName()
@@ -134,8 +132,7 @@ namespace System.Text.Json
         {
             if (ClassType != ClassType.Enumerable &&
                 ClassType != ClassType.Dictionary &&
-                ClassType != ClassType.IDictionaryConstructible &&
-                ClassType != ClassType.KeyValuePair)
+                ClassType != ClassType.IDictionaryConstructible)
             {
                 // We serialize if there is a getter + not ignoring readonly properties.
                 ShouldSerialize = HasGetter && (HasSetter || !Options.IgnoreReadOnlyProperties);
@@ -222,8 +219,7 @@ namespace System.Text.Json
                 {
                     Debug.Assert(ClassType == ClassType.Enumerable ||
                         ClassType == ClassType.Dictionary ||
-                        ClassType == ClassType.IDictionaryConstructible ||
-                        ClassType == ClassType.KeyValuePair);
+                        ClassType == ClassType.IDictionaryConstructible);
                     _elementClassInfo = Options.GetOrAddClass(_elementType);
                 }
 
index 5d0bf83..1693fd9 100644 (file)
@@ -211,29 +211,6 @@ namespace System.Text.Json
             return (IDictionary)createRangeDelegate.Invoke(CreateGenericIEnumerableFromDictionary(sourceDictionary));
         }
 
-        public override ValueType CreateKeyValuePairInstance(ref ReadStack state, IDictionary sourceDictionary, JsonSerializerOptions options)
-        {
-            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> CreateGenericTRuntimePropertyIEnumerable(IList sourceList)
         {
             foreach (object item in sourceList)
index 2665a28..660d933 100644 (file)
@@ -44,7 +44,7 @@ namespace System.Text.Json
                 ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath);
             }
 
-            if (state.Current.KeyName == null && (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair))
+            if (state.Current.KeyName == null && (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructible))
             {
                 ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath);
                 return;
index ed7bba1..234b7bc 100644 (file)
@@ -45,7 +45,7 @@ namespace System.Text.Json.Serialization
                 ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath);
             }
 
-            if (state.Current.KeyName == null && (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair))
+            if (state.Current.KeyName == null && (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructible))
             {
                 ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath);
                 return;
index 5f220ee..02e5042 100644 (file)
@@ -37,7 +37,7 @@ namespace System.Text.Json
 
             // Verify that we don't have a multidimensional array.
             Type arrayType = jsonPropertyInfo.RuntimePropertyType;
-            if (!state.Current.IsProcessingKeyValuePair && !typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1))
+            if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1))
             {
                 ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType, reader, state.JsonPath);
             }
@@ -208,9 +208,7 @@ namespace System.Text.Json
                 dictionary[key] = value;
             }
             else if (state.Current.IsIDictionaryConstructible ||
-                (state.Current.IsIDictionaryConstructibleProperty && !setPropertyDirectly) ||
-                state.Current.IsKeyValuePair ||
-                (state.Current.IsKeyValuePairProperty && !setPropertyDirectly))
+                (state.Current.IsIDictionaryConstructibleProperty && !setPropertyDirectly))
             {
                 Debug.Assert(state.Current.TempDictionaryValues != null);
                 IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.TempDictionaryValues);
@@ -275,7 +273,7 @@ namespace System.Text.Json
                 Debug.Assert(!string.IsNullOrEmpty(key));
                 dictionary[key] = value;
             }
-            else if (state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
+            else if (state.Current.IsProcessingIDictionaryConstructible)
             {
                 Debug.Assert(state.Current.TempDictionaryValues != null);
                 IDictionary<string, TProperty> dictionary = (IDictionary<string, TProperty>)state.Current.TempDictionaryValues;
index 9d7f0fc..940ac0f 100644 (file)
@@ -40,7 +40,7 @@ namespace System.Text.Json
 
                 JsonClassInfo classInfo = state.Current.JsonClassInfo;
 
-                if (state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
+                if (state.Current.IsProcessingIDictionaryConstructible)
                 {
                     state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject();
                 }
@@ -58,7 +58,7 @@ namespace System.Text.Json
 
             state.Current.PropertyInitialized = true;
 
-            if (state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
+            if (state.Current.IsProcessingIDictionaryConstructible)
             {
                 JsonClassInfo dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType);
                 state.Current.TempDictionaryValues = (IDictionary)dictionaryClassInfo.CreateObject();
@@ -99,52 +99,13 @@ namespace System.Text.Json
                 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)
                 {
-                    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);
-                    }
+                    JsonDictionaryConverter converter = state.Current.JsonPropertyInfo.DictionaryConverter;
+                    value = converter.CreateFromDictionary(ref state, state.Current.TempDictionaryValues, options);
                 }
                 else
                 {
index a65edf7..0eb6f5f 100644 (file)
@@ -11,7 +11,7 @@ namespace System.Text.Json
     {
         private static void HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
         {
-            Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair);
+            Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingIDictionaryConstructible);
 
             if (state.Current.IsProcessingEnumerable)
             {
@@ -42,7 +42,7 @@ namespace System.Text.Json
                 }
             }
 
-            if (state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
+            if (state.Current.IsProcessingIDictionaryConstructible)
             {
                 state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject();
             }
@@ -60,7 +60,7 @@ namespace System.Text.Json
 
         private static void HandleEndObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
         {
-            Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair);
+            Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingIDictionaryConstructible);
 
             state.Current.JsonClassInfo.UpdateSortedPropertyCache(ref state.Current);
 
index 11fccd5..d4066fc 100644 (file)
@@ -23,7 +23,7 @@ namespace System.Text.Json
             Debug.Assert(state.Current.ReturnValue != default || state.Current.TempDictionaryValues != default);
             Debug.Assert(state.Current.JsonClassInfo != default);
 
-            if (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
+            if (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructible)
             {
                 if (ReferenceEquals(state.Current.JsonClassInfo.DataExtensionProperty, state.Current.JsonPropertyInfo))
                 {
@@ -56,9 +56,7 @@ namespace System.Text.Json
                         state.Current.IsDictionary ||
                         (state.Current.IsDictionaryProperty && 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.IsIDictionaryConstructibleProperty && state.Current.JsonPropertyInfo != null));
 
                     state.Current.KeyName = keyName;
                 }
index 699977d..cb45ac1 100644 (file)
@@ -66,7 +66,7 @@ namespace System.Text.Json
                                 break;
                             }
                         }
-                        else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
+                        else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructible)
                         {
                             HandleStartDictionary(options, ref reader, ref readStack);
                         }
@@ -81,7 +81,7 @@ namespace System.Text.Json
                         {
                             readStack.Pop();
                         }
-                        else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)
+                        else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructible)
                         {
                             HandleEndDictionary(options, ref reader, ref readStack);
                         }
index 32ca321..f7f8b03 100644 (file)
@@ -60,12 +60,6 @@ namespace System.Text.Json
                     // 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;
index 626c2df..97ab32e 100644 (file)
@@ -55,7 +55,6 @@ namespace System.Text.Json
         {
             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);
@@ -140,13 +139,6 @@ namespace System.Text.Json
 
                 // 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
             {
index b909cf6..497336e 100644 (file)
@@ -90,11 +90,33 @@ namespace System.Text.Json
             return result;
         }
 
-        private static void WriteCore(PooledByteBufferWriter output, object value, Type type, JsonSerializerOptions options)
+        private static string WriteValueCore(Utf8JsonWriter writer, object value, Type type, JsonSerializerOptions options)
         {
-            Debug.Assert(type != null || value == null);
+            if (options == null)
+            {
+                options = JsonSerializerOptions.s_defaultOptions;
+            }
+
+            string result;
+
+            using (var output = new PooledByteBufferWriter(options.DefaultBufferSize))
+            {
+                WriteCore(writer, output, value, type, options);
+                result = JsonReaderHelper.TranscodeHelper(output.WrittenMemory.Span);
+            }
+
+            return result;
+        }
 
+        private static void WriteCore(PooledByteBufferWriter output, object value, Type type, JsonSerializerOptions options)
+        {
             using var writer = new Utf8JsonWriter(output, options.GetWriterOptions());
+            WriteCore(writer, output, value, type, options);
+        }
+
+        private static void WriteCore(Utf8JsonWriter writer, PooledByteBufferWriter output, object value, Type type, JsonSerializerOptions options)
+        {
+            Debug.Assert(type != null || value == null);
 
             if (value == null)
             {
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Utf8JsonWriter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Utf8JsonWriter.cs
new file mode 100644 (file)
index 0000000..bc393a0
--- /dev/null
@@ -0,0 +1,33 @@
+// 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.
+
+namespace System.Text.Json
+{
+    public static partial class JsonSerializer
+    {
+        /// <summary>
+        /// Write one JSON value (including objects or arrays) to the provided writer.
+        /// </summary>
+        /// <param name="writer">The writer to write.</param>
+        /// <param name="value">The value to convert and write.</param>
+        /// <param name="options">Options to control the behavior.</param>
+        public static void WriteValue<TValue>(Utf8JsonWriter writer, TValue value, JsonSerializerOptions options = null)
+        {
+            WriteValueCore(writer, value, typeof(TValue), options);
+        }
+
+        /// <summary>
+        /// Write one JSON value (including objects or arrays) to the provided writer.
+        /// </summary>
+        /// <param name="writer"></param>
+        /// <param name="value">The value to convert and write.</param>
+        /// <param name="type">The type of the <paramref name="value"/> to convert.</param>
+        /// <param name="options">Options to control the behavior.</param>
+        public static void WriteValue(Utf8JsonWriter writer, object value, Type type, JsonSerializerOptions options = null)
+        {
+            VerifyValueAndType(value, type);
+            WriteValueCore(writer, value, type, options);
+        }
+    }
+}
index 9535301..931c9e7 100644 (file)
@@ -36,7 +36,6 @@ namespace System.Text.Json
                             finishedSerializing = true;
                             break;
                         case ClassType.Object:
-                        case ClassType.KeyValuePair:
                             finishedSerializing = WriteObject(options, writer, ref state);
                             break;
                         case ClassType.Dictionary:
index a5251ff..bb14f5f 100644 (file)
@@ -42,13 +42,13 @@ namespace System.Text.Json
 
         private static List<JsonConverter> GetDefaultConverters()
         {
-            const int NumberOfConverters = 1;
+            const int NumberOfConverters = 2;
 
             var converters = new List<JsonConverter>(NumberOfConverters);
 
             // Use a list for converters that implement CanConvert().
             converters.Add(new JsonConverterEnum(treatAsString: false));
-            // todo: converters.Add(new JsonConverterKeyValuePair());
+            converters.Add(new JsonKeyValuePairConverter());
 
             // We will likely add collection converters here in the future.
 
index aeebdef..b81e67e 100644 (file)
@@ -316,16 +316,6 @@ namespace System.Text.Json
 
         internal JsonPropertyInfo GetJsonPropertyInfoFromClassInfo(JsonClassInfo classInfo, JsonSerializerOptions options)
         {
-            if (classInfo.ClassType == ClassType.KeyValuePair)
-            {
-                return classInfo.GetPolicyPropertyOfKeyValuePair();
-            }
-
-            if (classInfo.ClassType != ClassType.Object)
-            {
-                return classInfo.GetPolicyProperty();
-            }
-
             Type objectType = classInfo.Type;
 
             if (!_objectJsonProperties.TryGetValue(objectType, out JsonPropertyInfo propertyInfo))
index 990fa51..081b784 100644 (file)
@@ -45,15 +45,12 @@ namespace System.Text.Json
 
         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 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;
 
@@ -62,12 +59,9 @@ namespace System.Text.Json
             !JsonPropertyInfo.IsPropertyPolicy &&
             JsonPropertyInfo.ClassType == ClassType.Enumerable;
 
-        public bool IsProcessingEnumerableOrDictionary => IsProcessingEnumerable || IsProcessingDictionary || IsProcessingIDictionaryConstructibleOrKeyValuePair;
-        public bool IsProcessingIDictionaryConstructibleOrKeyValuePair => IsProcessingIDictionaryConstructible || IsProcessingKeyValuePair;
-
+        public bool IsProcessingEnumerableOrDictionary => IsProcessingEnumerable || IsProcessingDictionary || IsProcessingIDictionaryConstructible;
         public bool IsProcessingDictionary => IsDictionary || IsDictionaryProperty;
         public bool IsProcessingIDictionaryConstructible => IsIDictionaryConstructible || IsIDictionaryConstructibleProperty;
-        public bool IsProcessingKeyValuePair => IsKeyValuePair || IsKeyValuePairProperty;
         public bool IsProcessingEnumerable => IsEnumerable || IsEnumerableProperty;
 
         public bool IsProcessingValue
@@ -122,10 +116,6 @@ namespace System.Text.Json
             {
                 JsonPropertyInfo = JsonClassInfo.GetPolicyProperty();
             }
-            else if (JsonClassInfo.ClassType == ClassType.KeyValuePair)
-            {
-                JsonPropertyInfo = JsonClassInfo.GetPolicyPropertyOfKeyValuePair();
-            }
         }
 
         public void Reset()
index e1b9dd8..0b1943b 100644 (file)
@@ -60,7 +60,7 @@ namespace System.Text.Json
             }
             else
             {
-                Debug.Assert(nextClassInfo.ClassType == ClassType.Object || nextClassInfo.ClassType == ClassType.KeyValuePair || nextClassInfo.ClassType == ClassType.Unknown);
+                Debug.Assert(nextClassInfo.ClassType == ClassType.Object || nextClassInfo.ClassType == ClassType.Unknown);
                 Current.PopStackOnEndObject = true;
             }
         }
index 3709b06..20fcd2d 100644 (file)
@@ -51,13 +51,6 @@ namespace System.Text.Json
                 JsonPropertyInfo = JsonClassInfo.GetPolicyProperty();
                 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++;
-            }
         }
 
         public void WriteObjectOrArrayStart(ClassType classType, Utf8JsonWriter writer, bool writeNull = false)
index 05b9b9c..bee53a1 100644 (file)
@@ -716,18 +716,11 @@ namespace System.Text.Json.Serialization.Tests
             Assert.Equal(0, result.Count());
         }
 
-        [Fact(Skip = "Disabled until we write a converter for KeyValuePair")]
-        public static void ReadPrimitiveKeyValuePair()
+        [Fact]
+        public static void ReadPrimitiveKeyValuePairFail()
         {
-            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: no Value
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<KeyValuePair<string, int>>(@"{""Key"": 123}"));
 
             // Invalid form: extra property
             Assert.Throws<JsonException>(() => JsonSerializer.Parse<KeyValuePair<string, int>>(@"{""Key"": ""Key"", ""Value"": 123, ""Value2"": 456}"));
@@ -736,18 +729,9 @@ namespace System.Text.Json.Serialization.Tests
             Assert.Throws<JsonException>(() => JsonSerializer.Parse<KeyValuePair<string, int>>(@"{""Key"": ""Key"", ""Val"": 123"));
         }
 
-        [Fact(Skip = "Disabled until we write a converter for KeyValuePair")]
         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}]");
+            List<KeyValuePair<string, int>> input = JsonSerializer.Parse<List<KeyValuePair<string, int>>>(@"""Key"":""Key"", ""Value"":[{""123"":123},{""456"": 456}]");
 
             Assert.Equal(2, input.Count);
             Assert.Equal("123", input[0].Key);
@@ -756,34 +740,39 @@ namespace System.Text.Json.Serialization.Tests
             Assert.Equal(456, input[1].Value);
         }
 
-        [Fact(Skip="Disabled until we write a converter for KeyValuePair")]
         public static void ReadKeyValuePairOfList()
         {
-            KeyValuePair<string, List<int>> input = JsonSerializer.Parse<KeyValuePair<string, List<int>>>(@"{""Key"":[1, 2, 3]}");
+            KeyValuePair<string, List<int>> 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]);
+        }
 
-            input = JsonSerializer.Parse<KeyValuePair<string, List<int>>>(@"{""Key"": ""Key"", ""Value"": [1, 2, 3]}");
+        [Fact]
+        public static void ReadKeyValuePairOfKeyValuePair()
+        {
+            KeyValuePair<string, KeyValuePair<int, int>> input = JsonSerializer.Parse<KeyValuePair<string, KeyValuePair<int, int>>>(@"{""Key"":""Key"", ""Value"":{""Key"":1, ""Value"":2}}");
 
             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]);
+            Assert.Equal(1, input.Value.Key);
+            Assert.Equal(2, input.Value.Value);
         }
 
-        [Fact(Skip = "Disabled until we write a converter for KeyValuePair")]
-        public static void ReadKeyValuePairOfKeyValuePair()
+        [Theory]
+        [InlineData(@"{""Key"":""Key"", ""Value"":{""Key"":1, ""Value"":2}}")]
+        [InlineData(@"{""Key"":""Key"", ""Value"":{""Value"":2, ""Key"":1}}")]
+        [InlineData(@"{""Value"":{""Key"":1, ""Value"":2}, ""Key"":""Key""}")]
+        [InlineData(@"{""Value"":{""Value"":2, ""Key"":1}, ""Key"":""Key""}")]
+        public static void ReadKeyValuePairOfKeyValuePair(string json)
         {
-            KeyValuePair<string, KeyValuePair<string, int>> input = JsonSerializer.Parse<KeyValuePair<string, KeyValuePair<string, int>>>(@"{""Key"":{""Key"":1}}");
+            KeyValuePair<string, KeyValuePair<int, int>> input = JsonSerializer.Parse<KeyValuePair<string, KeyValuePair<int, int>>>(json);
 
             Assert.Equal("Key", input.Key);
-            Assert.Equal("Key", input.Value.Key);
-            Assert.Equal(1, input.Value.Value);
+            Assert.Equal(1, input.Value.Key);
+            Assert.Equal(2, input.Value.Value);
         }
     }
 }
index e8a8818..1ce733b 100644 (file)
@@ -646,7 +646,7 @@ namespace System.Text.Json.Serialization.Tests
             Assert.Equal(@"[{""Key"":""123"",""Value"":123},{""Key"":""456"",""Value"":456}]", json);
         }
 
-        [Fact]
+        [Fact(Skip = "Disabled until writer enables property name to be set")]
         public static void WriteKeyValuePairOfList()
         {
             KeyValuePair<string, List<int>> input = new KeyValuePair<string, List<int>>("Key", new List<int> { 1, 2, 3 });