Added value type serialization support (dotnet/corefx#36506)
authorYoh Deadfall <yoh.deadfall@hotmail.com>
Mon, 20 May 2019 01:29:05 +0000 (04:29 +0300)
committerAhson Khan <ahkha@microsoft.com>
Mon, 20 May 2019 01:29:05 +0000 (18:29 -0700)
* Added value type serialization support

* Fixed coding style issue

Commit migrated from https://github.com/dotnet/corefx/commit/d70ba24c8ca8d8a4384ebe732b3cd748a22e9551

src/libraries/System.Text.Json/src/System.Text.Json.csproj
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/JsonPropertyInfoNullable.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/MemberAccessor.cs [new file with mode: 0644]
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMaterializer.cs
src/libraries/System.Text.Json/tests/Serialization/Object.ReadTests.cs
src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestStruct.cs [new file with mode: 0644]
src/libraries/System.Text.Json/tests/Serialization/TestData.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests.csproj

index 04c2ed9..585c7e4 100644 (file)
     <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\JsonSerializerOptions.cs" />
+    <Compile Include="System\Text\Json\Serialization\MemberAccessor.cs" />
     <Compile Include="System\Text\Json\Serialization\Policies\JsonValueConverter.cs" />
     <Compile Include="System\Text\Json\Serialization\PooledBufferWriter.cs" />
     <Compile Include="System\Text\Json\Serialization\PropertyRef.cs" />
index ba67427..7a9aa06 100644 (file)
@@ -16,8 +16,8 @@ namespace System.Text.Json.Serialization
     /// </summary>
     internal abstract class JsonPropertyInfoCommon<TClass, TDeclaredProperty, TRuntimeProperty> : JsonPropertyInfo
     {
-        public Func<TClass, TDeclaredProperty> Get { get; private set; }
-        public Action<TClass, TDeclaredProperty> Set { get; private set; }
+        public Func<object, TDeclaredProperty> Get { get; private set; }
+        public Action<object, TDeclaredProperty> Set { get; private set; }
 
         public JsonValueConverter<TRuntimeProperty> ValueConverter { get; internal set; }
 
@@ -36,13 +36,13 @@ namespace System.Text.Json.Serialization
                 if (propertyInfo.GetMethod?.IsPublic == true)
                 {
                     HasGetter = true;
-                    Get = (Func<TClass, TDeclaredProperty>)Delegate.CreateDelegate(typeof(Func<TClass, TDeclaredProperty>), propertyInfo.GetGetMethod());
+                    Get = MemberAccessor.CreatePropertyGetter<TClass, TDeclaredProperty>(propertyInfo);
                 }
 
                 if (propertyInfo.SetMethod?.IsPublic == true)
                 {
                     HasSetter = true;
-                    Set = (Action<TClass, TDeclaredProperty>)Delegate.CreateDelegate(typeof(Action<TClass, TDeclaredProperty>), propertyInfo.GetSetMethod());
+                    Set = MemberAccessor.CreatePropertySetter<TClass, TDeclaredProperty>(propertyInfo);
                 }
             }
             else
@@ -69,18 +69,18 @@ namespace System.Text.Json.Serialization
                 return obj;
             }
 
-            Debug.Assert(Get != null);
-            return Get((TClass)obj);
+            Debug.Assert(HasGetter);
+            return Get(obj);
         }
 
         public override void SetValueAsObject(object obj, object value)
         {
-            Debug.Assert(Set != null);
+            Debug.Assert(HasSetter);
             TDeclaredProperty typedValue = (TDeclaredProperty)value;
 
             if (typedValue != null || !IgnoreNullValues)
             {
-                Set((TClass)obj, (TDeclaredProperty)value);
+                Set(obj, typedValue);
             }
         }
 
index fa326d0..680f9c3 100644 (file)
@@ -37,8 +37,8 @@ namespace System.Text.Json.Serialization
                         // Null values were already handled.
                         Debug.Assert(value != null);
 
-                        Set((TClass)state.Current.ReturnValue, value);
-                    }
+                            Set(state.Current.ReturnValue, value);
+                        }
 
                     return;
                 }
@@ -73,7 +73,7 @@ namespace System.Text.Json.Serialization
             }
             else
             {
-                value = (TRuntimeProperty)Get((TClass)current.CurrentValue);
+                value = (TRuntimeProperty)Get(current.CurrentValue);
             }
 
             if (value == null)
index 0a476b1..9034f18 100644 (file)
@@ -30,7 +30,7 @@ namespace System.Text.Json.Serialization
                 }
                 else
                 {
-                    Set((TClass)state.Current.ReturnValue, value);
+                    Set(state.Current.ReturnValue, value);
                 }
 
                 return;
@@ -72,7 +72,7 @@ namespace System.Text.Json.Serialization
                 }
                 else
                 {
-                    value = Get((TClass)current.CurrentValue);
+                    value = Get(current.CurrentValue);
                 }
 
                 if (value == null)
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/MemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/MemberAccessor.cs
new file mode 100644 (file)
index 0000000..167d812
--- /dev/null
@@ -0,0 +1,93 @@
+// 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.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace System.Text.Json.Serialization
+{
+    internal static class MemberAccessor
+    {
+        private delegate TProperty GetProperty<TClass, TProperty>(TClass obj);
+        private delegate TProperty GetPropertyByRef<TClass, TProperty>(ref TClass obj);
+
+        private delegate void SetProperty<TClass, TProperty>(TClass obj, TProperty value);
+        private delegate void SetPropertyByRef<TClass, TProperty>(ref TClass obj, TProperty value);
+
+        private delegate Func<object, TProperty> GetPropertyByRefFactory<TClass, TProperty>(GetPropertyByRef<TClass, TProperty> set);
+        private delegate Action<object, TProperty> SetPropertyByRefFactory<TClass, TProperty>(SetPropertyByRef<TClass, TProperty> set);
+
+        private static readonly MethodInfo s_createStructPropertyGetterMethod = new GetPropertyByRefFactory<int, int>(CreateStructPropertyGetter)
+            .Method.GetGenericMethodDefinition();
+
+        private static readonly MethodInfo s_createStructPropertySetterMethod = new SetPropertyByRefFactory<int, int>(CreateStructPropertySetter)
+            .Method.GetGenericMethodDefinition();
+
+        internal static Func<object, TProperty> CreatePropertyGetter<TClass, TProperty>(PropertyInfo propertyInfo)
+        {
+            MethodInfo getMethodInfo = propertyInfo.GetGetMethod();
+
+            if (typeof(TClass).IsValueType)
+            {
+                var factory = CreateDelegate<GetPropertyByRefFactory<TClass, TProperty>>(s_createStructPropertyGetterMethod.MakeGenericMethod(typeof(TClass), typeof(TProperty)));
+                var propertyGetter = CreateDelegate<GetPropertyByRef<TClass, TProperty>>(getMethodInfo);
+
+                return factory(propertyGetter);
+            }
+            else
+            {
+                var propertyGetter = CreateDelegate<GetProperty<TClass, TProperty>>(getMethodInfo);
+                return delegate (object obj)
+                {
+                    return propertyGetter((TClass)obj);
+                };
+            }
+        }
+
+        internal static Action<object, TProperty> CreatePropertySetter<TClass, TProperty>(PropertyInfo propertyInfo)
+        {
+            MethodInfo setMethodInfo = propertyInfo.GetSetMethod();
+
+            if (typeof(TClass).IsValueType)
+            {
+                var factory = CreateDelegate<SetPropertyByRefFactory<TClass, TProperty>>(s_createStructPropertySetterMethod.MakeGenericMethod(typeof(TClass), typeof(TProperty)));
+                var propertySetter = CreateDelegate<SetPropertyByRef<TClass, TProperty>>(setMethodInfo);
+
+                return factory(propertySetter);
+            }
+            else
+            {
+                var propertySetter = CreateDelegate<SetProperty<TClass, TProperty>>(setMethodInfo);
+                return delegate (object obj, TProperty value)
+                {
+                    propertySetter((TClass)obj, value);
+                };
+            }
+        }
+
+        private static TDelegate CreateDelegate<TDelegate>(MethodInfo methodInfo)
+            where TDelegate : Delegate
+        {
+            return (TDelegate)Delegate.CreateDelegate(typeof(TDelegate), methodInfo);
+        }
+
+        private static Func<object, TProperty> CreateStructPropertyGetter<TClass, TProperty>(GetPropertyByRef<TClass, TProperty> get)
+            where TClass : struct
+        {
+            return delegate (object obj)
+            {
+                return get(ref Unsafe.Unbox<TClass>(obj));
+            };
+        }
+
+        private static Action<object, TProperty> CreateStructPropertySetter<TClass, TProperty>(SetPropertyByRef<TClass, TProperty> set)
+            where TClass : struct
+        {
+            return delegate (object obj, TProperty value)
+            {
+                set(ref Unsafe.Unbox<TClass>(obj), value);
+            };
+        }
+    }
+}
index be5dec1..7af144a 100644 (file)
@@ -15,22 +15,36 @@ namespace System.Text.Json.Serialization
         public override JsonClassInfo.ConstructorDelegate CreateConstructor(Type type)
         {
             Debug.Assert(type != null);
-
             ConstructorInfo realMethod = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, binder: null, Type.EmptyTypes, modifiers: null);
-            if (realMethod == null)
+
+            if (realMethod == null && !type.IsValueType)
             {
                 return null;
             }
 
             var dynamicMethod = new DynamicMethod(
-                realMethod.Name,
-                type,
+                ConstructorInfo.ConstructorName,
+                typeof(object),
                 Type.EmptyTypes,
                 typeof(ReflectionEmitMaterializer).Module,
                 skipVisibility: true);
 
             ILGenerator generator = dynamicMethod.GetILGenerator();
-            generator.Emit(OpCodes.Newobj, realMethod);
+
+            if (realMethod == null)
+            {
+                LocalBuilder local = generator.DeclareLocal(type);
+
+                generator.Emit(OpCodes.Ldloca_S, local);
+                generator.Emit(OpCodes.Initobj, type);
+                generator.Emit(OpCodes.Ldloc, local);
+                generator.Emit(OpCodes.Box, type);
+            }
+            else
+            {
+                generator.Emit(OpCodes.Newobj, realMethod);
+            }
+
             generator.Emit(OpCodes.Ret);
 
             return (JsonClassInfo.ConstructorDelegate)dynamicMethod.CreateDelegate(typeof(JsonClassInfo.ConstructorDelegate));
index 85ff792..ee821b3 100644 (file)
@@ -9,6 +9,13 @@ namespace System.Text.Json.Serialization.Tests
     public static partial class ObjectTests
     {
         [Fact]
+        public static void ReadSimpleStruct()
+        {
+            SimpleTestStruct obj = JsonSerializer.Parse<SimpleTestStruct>(SimpleTestStruct.s_json);
+            obj.Verify();
+        }
+
+        [Fact]
         public static void ReadSimpleClass()
         {
             SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(SimpleTestClass.s_json);
diff --git a/src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestStruct.cs b/src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestStruct.cs
new file mode 100644 (file)
index 0000000..a5fac97
--- /dev/null
@@ -0,0 +1,203 @@
+// 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.Linq;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+    public class SimpleTestStruct : ITestClass
+    {
+        public short MyInt16 { get; set; }
+        public int MyInt32 { get; set; }
+        public long MyInt64 { get; set; }
+        public ushort MyUInt16 { get; set; }
+        public uint MyUInt32 { get; set; }
+        public ulong MyUInt64 { get; set; }
+        public byte MyByte { get; set; }
+        public sbyte MySByte { get; set; }
+        public char MyChar { get; set; }
+        public string MyString { get; set; }
+        public decimal MyDecimal { get; set; }
+        public bool MyBooleanTrue { get; set; }
+        public bool MyBooleanFalse { get; set; }
+        public float MySingle { get; set; }
+        public double MyDouble { get; set; }
+        public DateTime MyDateTime { get; set; }
+        public DateTimeOffset MyDateTimeOffset { get; set; }
+        public SampleEnum MyEnum { get; set; }
+        public short[] MyInt16Array { get; set; }
+        public int[] MyInt32Array { get; set; }
+        public long[] MyInt64Array { get; set; }
+        public ushort[] MyUInt16Array { get; set; }
+        public uint[] MyUInt32Array { get; set; }
+        public ulong[] MyUInt64Array { get; set; }
+        public byte[] MyByteArray { get; set; }
+        public sbyte[] MySByteArray { get; set; }
+        public char[] MyCharArray { get; set; }
+        public string[] MyStringArray { get; set; }
+        public decimal[] MyDecimalArray { get; set; }
+        public bool[] MyBooleanTrueArray { get; set; }
+        public bool[] MyBooleanFalseArray { get; set; }
+        public float[] MySingleArray { get; set; }
+        public double[] MyDoubleArray { get; set; }
+        public DateTime[] MyDateTimeArray { get; set; }
+        public DateTimeOffset[] MyDateTimeOffsetArray { get; set; }
+        public SampleEnum[] MyEnumArray { get; set; }
+        public List<string> MyStringList { 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 static readonly string s_json = $"{{{s_partialJsonProperties},{s_partialJsonArrays}}}";
+        public static readonly string s_json_flipped = $"{{{s_partialJsonArrays},{s_partialJsonProperties}}}";
+
+        private const string s_partialJsonProperties =
+                @"""MyInt16"" : 1," +
+                @"""MyInt32"" : 2," +
+                @"""MyInt64"" : 3," +
+                @"""MyUInt16"" : 4," +
+                @"""MyUInt32"" : 5," +
+                @"""MyUInt64"" : 6," +
+                @"""MyByte"" : 7," +
+                @"""MySByte"" : 8," +
+                @"""MyChar"" : ""a""," +
+                @"""MyString"" : ""Hello""," +
+                @"""MyBooleanTrue"" : true," +
+                @"""MyBooleanFalse"" : false," +
+                @"""MySingle"" : 1.1," +
+                @"""MyDouble"" : 2.2," +
+                @"""MyDecimal"" : 3.3," +
+                @"""MyDateTime"" : ""2019-01-30T12:01:02.0000000Z""," +
+                @"""MyDateTimeOffset"" : ""2019-01-30T12:01:02.0000000+01:00""," +
+                @"""MyEnum"" : 2"; // int by default
+
+        private const string s_partialJsonArrays =
+                @"""MyInt16Array"" : [1]," +
+                @"""MyInt32Array"" : [2]," +
+                @"""MyInt64Array"" : [3]," +
+                @"""MyUInt16Array"" : [4]," +
+                @"""MyUInt32Array"" : [5]," +
+                @"""MyUInt64Array"" : [6]," +
+                @"""MyByteArray"" : [7]," +
+                @"""MySByteArray"" : [8]," +
+                @"""MyCharArray"" : [""a""]," +
+                @"""MyStringArray"" : [""Hello""]," +
+                @"""MyBooleanTrueArray"" : [true]," +
+                @"""MyBooleanFalseArray"" : [false]," +
+                @"""MySingleArray"" : [1.1]," +
+                @"""MyDoubleArray"" : [2.2]," +
+                @"""MyDecimalArray"" : [3.3]," +
+                @"""MyDateTimeArray"" : [""2019-01-30T12:01:02.0000000Z""]," +
+                @"""MyDateTimeOffsetArray"" : [""2019-01-30T12:01:02.0000000+01:00""]," +
+                @"""MyEnumArray"" : [2]," + // int by default
+                @"""MyStringList"" : [""Hello""]," +
+                @"""MyStringIEnumerableT"" : [""Hello""]," +
+                @"""MyStringIListT"" : [""Hello""]," +
+                @"""MyStringICollectionT"" : [""Hello""]," +
+                @"""MyStringIReadOnlyCollectionT"" : [""Hello""]," +
+                @"""MyStringIReadOnlyListT"" : [""Hello""]";
+
+        public static readonly byte[] s_data = Encoding.UTF8.GetBytes(s_json);
+
+        public void Initialize()
+        {
+            MyInt16 = 1;
+            MyInt32 = 2;
+            MyInt64 = 3;
+            MyUInt16 = 4;
+            MyUInt32 = 5;
+            MyUInt64 = 6;
+            MyByte = 7;
+            MySByte = 8;
+            MyChar = 'a';
+            MyString = "Hello";
+            MyBooleanTrue = true;
+            MyBooleanFalse = false;
+            MySingle = 1.1f;
+            MyDouble = 2.2d;
+            MyDecimal = 3.3m;
+            MyDateTime = new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc);
+            MyDateTimeOffset = new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0));
+            MyEnum = SampleEnum.Two;
+
+            MyInt16Array = new short[] { 1 };
+            MyInt32Array = new int[] { 2 };
+            MyInt64Array = new long[] { 3 };
+            MyUInt16Array = new ushort[] { 4 };
+            MyUInt32Array = new uint[] { 5 };
+            MyUInt64Array = new ulong[] { 6 };
+            MyByteArray = new byte[] { 7 };
+            MySByteArray = new sbyte[] { 8 };
+            MyCharArray = new char[] { 'a' };
+            MyStringArray = new string[] { "Hello" };
+            MyBooleanTrueArray = new bool[] { true };
+            MyBooleanFalseArray = new bool[] { false };
+            MySingleArray = new float[] { 1.1f };
+            MyDoubleArray = new double[] { 2.2d };
+            MyDecimalArray = new decimal[] { 3.3m };
+            MyDateTimeArray = new DateTime[] { new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc) };
+            MyDateTimeOffsetArray = new DateTimeOffset[] { new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)) };
+            MyEnumArray = new SampleEnum[] { SampleEnum.Two };
+
+            MyStringList = new List<string>() { "Hello" };
+            MyStringIEnumerableT = new string[] { "Hello" };
+            MyStringIListT = new string[] { "Hello" };
+            MyStringICollectionT = new string[] { "Hello" };
+            MyStringIReadOnlyCollectionT = new string[] { "Hello" };
+            MyStringIReadOnlyListT = new string[] { "Hello" };
+        }
+
+        public void Verify()
+        {
+            Assert.Equal((short)1, MyInt16);
+            Assert.Equal((int)2, MyInt32);
+            Assert.Equal((long)3, MyInt64);
+            Assert.Equal((ushort)4, MyUInt16);
+            Assert.Equal((uint)5, MyUInt32);
+            Assert.Equal((ulong)6, MyUInt64);
+            Assert.Equal((byte)7, MyByte);
+            Assert.Equal((sbyte)8, MySByte);
+            Assert.Equal('a', MyChar);
+            Assert.Equal("Hello", MyString);
+            Assert.Equal(3.3m, MyDecimal);
+            Assert.Equal(false, MyBooleanFalse);
+            Assert.Equal(true, MyBooleanTrue);
+            Assert.Equal(1.1f, MySingle);
+            Assert.Equal(2.2d, MyDouble);
+            Assert.Equal(new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc), MyDateTime);
+            Assert.Equal(new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)), MyDateTimeOffset);
+            Assert.Equal(SampleEnum.Two, MyEnum);
+
+            Assert.Equal((short)1, MyInt16Array[0]);
+            Assert.Equal((int)2, MyInt32Array[0]);
+            Assert.Equal((long)3, MyInt64Array[0]);
+            Assert.Equal((ushort)4, MyUInt16Array[0]);
+            Assert.Equal((uint)5, MyUInt32Array[0]);
+            Assert.Equal((ulong)6, MyUInt64Array[0]);
+            Assert.Equal((byte)7, MyByteArray[0]);
+            Assert.Equal((sbyte)8, MySByteArray[0]);
+            Assert.Equal('a', MyCharArray[0]);
+            Assert.Equal("Hello", MyStringArray[0]);
+            Assert.Equal(3.3m, MyDecimalArray[0]);
+            Assert.Equal(false, MyBooleanFalseArray[0]);
+            Assert.Equal(true, MyBooleanTrueArray[0]);
+            Assert.Equal(1.1f, MySingleArray[0]);
+            Assert.Equal(2.2d, MyDoubleArray[0]);
+            Assert.Equal(new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc), MyDateTimeArray[0]);
+            Assert.Equal(new DateTimeOffset(2019, 1, 30, 12, 1, 2, new TimeSpan(1, 0, 0)), MyDateTimeOffsetArray[0]);
+            Assert.Equal(SampleEnum.Two, MyEnumArray[0]);
+
+            Assert.Equal("Hello", MyStringList[0]);
+            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]);
+        }
+    }
+}
index ebf962f..8be65ec 100644 (file)
@@ -12,6 +12,7 @@ namespace System.Text.Json.Serialization.Tests
         {
             get
             {
+                yield return new object[] { typeof(SimpleTestStruct), SimpleTestStruct.s_data };
                 yield return new object[] { typeof(SimpleTestClass), SimpleTestClass.s_data };
                 yield return new object[] { typeof(SimpleTestClassWithNullables), SimpleTestClassWithNullables.s_data };
                 yield return new object[] { typeof(SimpleTestClassWithNulls), SimpleTestClassWithNulls.s_data };
@@ -46,6 +47,7 @@ namespace System.Text.Json.Serialization.Tests
         {
             get
             {
+                yield return new object[] { new SimpleTestStruct() };
                 yield return new object[] { new SimpleTestClass() };
                 yield return new object[] { new SimpleTestClassWithNullables() };
                 yield return new object[] { new SimpleTestClassWithNulls() };
index 3f0bb1c..137c99a 100644 (file)
@@ -50,6 +50,7 @@
     <Compile Include="Serialization\TestClasses.SimpleTestClassWithObject.cs" />
     <Compile Include="Serialization\TestClasses.SimpleTestClassWithObjectArrays.cs" />
     <Compile Include="Serialization\TestClasses.SimpleTestClassWithSimpleObject.cs" />
+    <Compile Include="Serialization\TestClasses.SimpleTestStruct.cs" />
     <Compile Include="Serialization\TestData.cs" />
     <Compile Include="Serialization\Value.ReadTests.cs" />
     <Compile Include="Serialization\Value.ReadTests.GenericCollections.cs" />