11using System ;
2+ using System . Collections ;
3+ using System . Collections . Generic ;
24using System . Linq ;
35using System . Linq . Expressions ;
46using System . Reflection ;
@@ -12,6 +14,8 @@ public static class ExpressionUtils
1214 // JObject.ToObject(value) method info
1315 private static readonly MethodInfo JObjectToObjectMethodInfo =
1416 typeof ( JObject ) . GetMethods ( ) . Single ( x => x . Name == "ToObject" && ! x . ContainsGenericParameters && x . GetParameters ( ) . Length == 1 ) ;
17+ private static readonly MethodInfo JArrayToObjectMethodInfo =
18+ typeof ( JArray ) . GetMethods ( ) . Single ( x => x . Name == "ToObject" && ! x . ContainsGenericParameters && x . GetParameters ( ) . Length == 1 ) ;
1519#else
1620 // JObject.ToObject(value) method info
1721 private static readonly MethodInfo JObjectToObjectMethodInfo =
@@ -20,6 +24,13 @@ public static class ExpressionUtils
2024 null ,
2125 CallingConventions . HasThis ,
2226 new [ ] { typeof ( Type ) } , null ) ;
27+
28+ private static readonly MethodInfo JArrayToObjectMethodInfo =
29+ typeof ( JArray ) . GetMethod ( "ToObject" ,
30+ BindingFlags . Instance | BindingFlags . Public ,
31+ null ,
32+ CallingConventions . HasThis ,
33+ new [ ] { typeof ( Type ) } , null ) ;
2334#endif
2435
2536 public static PropertyInfo GetPropertyInfo ( this LambdaExpression propertyExpression )
@@ -84,40 +95,50 @@ public static Func<TInstance, TResult> ToCompiledGetterFunc<TInstance, TResult>(
8495 return ( Func < TInstance , TResult > ) ToCompiledGetterDelegate ( pi , typeof ( TInstance ) , typeof ( TResult ) ) ;
8596 }
8697
98+ public static bool IsGenericType ( Type type )
99+ {
100+ return type . GetTypeInfo ( ) . IsGenericType ;
101+ }
102+
87103 public static Delegate ToCompiledSetterDelegate ( this PropertyInfo pi , Type tInstance , Type tValue )
88104 {
89105 if ( ! tValue . IsAssignableFrom ( pi . PropertyType ) && ! pi . PropertyType . IsAssignableFrom ( tValue ) )
90106 throw new InvalidOperationException ( $ "Unsupported type combination: { tValue } and { pi . GetType ( ) } .") ;
91107
92108 var instanceParameter = Expression . Parameter ( tInstance ) ;
93- var valueParameter = Expression . Parameter ( tValue ) ;
109+ var valueParameter = Expression . Parameter ( typeof ( object ) ) ;
94110
95- if ( Type . GetTypeCode ( tValue ) == TypeCode . Object )
111+ Expression exp ;
112+ if ( Type . GetTypeCode ( pi . PropertyType ) == TypeCode . Object )
96113 {
97- var canConvertToJObject = Expression . Equal ( Expression . TypeAs ( valueParameter , typeof ( JObject ) ) ,
98- Expression . Constant ( null ) ) ;
99-
100- var exp = Expression . IfThenElse ( canConvertToJObject ,
101- CreateSimpleTypeSetterExpression ( pi , instanceParameter , valueParameter ) ,
102- CreateJObjectTypeSetterExpression ( pi , instanceParameter , valueParameter ) ) ;
103-
104- return Expression . Lambda ( exp , instanceParameter , valueParameter ) . Compile ( ) ;
114+ if ( pi . PropertyType . GetInterfaces ( ) . Any ( x => x == typeof ( IEnumerable ) ) )
115+ {
116+ exp = CreateJArrayTypeSetterExpression ( pi , instanceParameter , valueParameter ) ;
117+ }
118+ else
119+ {
120+ var canConvertToJObject = Expression . Equal ( Expression . TypeAs ( valueParameter , typeof ( JObject ) ) ,
121+ Expression . Constant ( null ) ) ;
122+
123+ exp = Expression . IfThenElse ( canConvertToJObject ,
124+ CreateSimpleTypeSetterExpression ( pi , instanceParameter , valueParameter ) ,
125+ CreateJObjectTypeSetterExpression ( pi , instanceParameter , valueParameter ) ) ;
126+ }
105127 }
106128 else
107129 {
108- var exp = CreateSimpleTypeSetterExpression ( pi , instanceParameter , valueParameter ) ;
109- return Expression . Lambda ( exp , instanceParameter , valueParameter ) . Compile ( ) ;
130+ exp = CreateSimpleTypeSetterExpression ( pi , instanceParameter , valueParameter ) ;
110131 }
132+ return Expression . Lambda ( exp , instanceParameter , valueParameter ) . Compile ( ) ;
111133 }
112134
113- public static Action < TInstance , TValue > ToCompiledSetterAction < TInstance , TValue > ( this PropertyInfo pi )
135+ public static Action < TInstance , object > ToCompiledSetterAction < TInstance , TValue > ( this PropertyInfo pi )
114136 {
115- return ( Action < TInstance , TValue > ) ToCompiledSetterDelegate ( pi , typeof ( TInstance ) , typeof ( TValue ) ) ;
137+ return ( Action < TInstance , object > ) ToCompiledSetterDelegate ( pi , typeof ( TInstance ) , typeof ( TValue ) ) ;
116138 }
117139
118140 private static Expression CreateSimpleTypeSetterExpression ( PropertyInfo pi , ParameterExpression instanceParameter , ParameterExpression valueParameter )
119141 {
120-
121142 var mi = pi . GetSetMethod ( ) ;
122143 Expression valueExpression = valueParameter ;
123144
@@ -129,14 +150,27 @@ private static Expression CreateSimpleTypeSetterExpression(PropertyInfo pi, Para
129150 return body ;
130151 }
131152
132- private static Expression CreateJObjectTypeSetterExpression ( PropertyInfo pi , ParameterExpression instanceParameter , ParameterExpression valueParameter )
153+ private static Expression CreateJObjectTypeSetterExpression ( PropertyInfo pi ,
154+ ParameterExpression instanceParameter , ParameterExpression valueParameter )
155+ {
156+ return CreateJTokenSetterExpression < JObject > ( pi , instanceParameter , valueParameter , JObjectToObjectMethodInfo ) ;
157+ }
158+
159+ private static Expression CreateJArrayTypeSetterExpression ( PropertyInfo pi ,
160+ ParameterExpression instanceParameter , ParameterExpression valueParameter )
161+ {
162+ return CreateJTokenSetterExpression < JArray > ( pi , instanceParameter , valueParameter , JArrayToObjectMethodInfo ) ;
163+ }
164+
165+ private static Expression CreateJTokenSetterExpression < TJTokenType > ( PropertyInfo pi ,
166+ ParameterExpression instanceParameter , ParameterExpression valueParameter , MethodInfo method ) where TJTokenType : JToken
133167 {
134- // Use "(targetType) JObject.ToObject(value)" to get deserialized object
168+ // Use "(targetType) { JObject/JArray} .ToObject(value)" to get deserialized object
135169 var mi = pi . GetSetMethod ( ) ;
136170 var typeConstant = Expression . Constant ( pi . PropertyType ) ;
137171
138- var convertToJObjectExpression = Expression . Convert ( valueParameter , typeof ( JObject ) ) ;
139- var toObjectCall = Expression . Call ( convertToJObjectExpression , JObjectToObjectMethodInfo , typeConstant ) ;
172+ var convertToJObjectExpression = Expression . Convert ( valueParameter , typeof ( TJTokenType ) ) ;
173+ var toObjectCall = Expression . Call ( convertToJObjectExpression , method , typeConstant ) ;
140174 var convertToTargetTypeExpression = Expression . Convert ( toObjectCall , pi . PropertyType ) ;
141175 var body = Expression . Call ( instanceParameter , mi , convertToTargetTypeExpression ) ;
142176
0 commit comments