--- /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.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);
+ };
+ }
+ }
+}
--- /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.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]);
+ }
+ }
+}