private object CoerceList(Type targetType, Type arrayType, IEnumerable value) { if (targetType.IsArray) { return this.CoerceArray(targetType.GetElementType(), value); } // targetType serializes as a JSON array but is not an array // assume is an ICollection / IEnumerable with AddRange, Add, // or custom Constructor with which we can populate it // many ICollection types take an IEnumerable or ICollection // as a constructor argument. look through constructors for // a compatible match. ConstructorInfo[] ctors = targetType.GetConstructors(); ConstructorInfo defaultCtor = null; foreach (ConstructorInfo ctor in ctors) { ParameterInfo[] paramList = ctor.GetParameters(); if (paramList.Length == 0) { // save for in case cannot find closer match defaultCtor = ctor; continue; } if (paramList.Length == 1 && TypeCoercionUtility.GetTypeInfo(paramList[0].ParameterType).IsAssignableFrom(TypeCoercionUtility.GetTypeInfo(arrayType))) { try { // invoke first constructor that can take this value as an argument return ctor.Invoke( new object[] { value } ); } catch { // there might exist a better match continue; } } } if (ConstructorInfo.Equals (defaultCtor, null)) { throw new JsonTypeCoercionException ( String.Format (TypeCoercionUtility.ErrorDefaultCtor, new System.Object[] { targetType.FullName })); } object collection; try { // always try-catch Invoke() to expose real exception collection = defaultCtor.Invoke(null); } catch (TargetInvocationException ex) { if (ex.InnerException != null) { throw new JsonTypeCoercionException(ex.InnerException.Message, ex.InnerException); } throw new JsonTypeCoercionException("Error instantiating " + targetType.FullName, ex); } // many ICollection types have an AddRange method // which adds all items at once #if WINDOWS_STORE /** \todo Not sure if this finds the correct methods */ MethodInfo method = TCU.GetTypeInfo(targetType).GetDeclaredMethod("AddRange"); #else MethodInfo method = TypeCoercionUtility.GetTypeInfo(targetType).GetMethod("AddRange"); #endif ParameterInfo[] parameters = (MethodInfo.Equals (method, null)) ? null : method.GetParameters(); Type paramType = (parameters == null || parameters.Length != 1) ? null : parameters[0].ParameterType; if (!Type.Equals (paramType, null) && TypeCoercionUtility.GetTypeInfo(paramType).IsAssignableFrom (TypeCoercionUtility.GetTypeInfo(arrayType))) { try { // always try-catch Invoke() to expose real exception // add all members in one method method.Invoke( collection, new object[] { value }); } catch (TargetInvocationException ex) { if (ex.InnerException != null) { throw new JsonTypeCoercionException(ex.InnerException.Message, ex.InnerException); } throw new JsonTypeCoercionException("Error calling AddRange on " + targetType.FullName, ex); } return collection; } else { // many ICollection types have an Add method // which adds items one at a time #if WINDOWS_STORE /** \todo Not sure if this finds the correct methods */ method = TCU.GetTypeInfo(targetType).GetDeclaredMethod("Add"); #else method = TypeCoercionUtility.GetTypeInfo(targetType).GetMethod("Add"); #endif parameters = (MethodInfo.Equals (method, null)) ? null : method.GetParameters(); paramType = (parameters == null || parameters.Length != 1) ? null : parameters[0].ParameterType; if (!Type.Equals (paramType, null)) { // loop through adding items to collection foreach (object item in value) { try { // always try-catch Invoke() to expose real exception method.Invoke ( collection, new object[] { this.CoerceType (paramType, item) }); } catch (TargetInvocationException ex) { if (ex.InnerException != null) { throw new JsonTypeCoercionException(ex.InnerException.Message, ex.InnerException); } throw new JsonTypeCoercionException("Error calling Add on " + targetType.FullName, ex); } } return collection; } } try { // fall back to basics return Convert.ChangeType(value, targetType); } catch (Exception ex) { throw new JsonTypeCoercionException(String.Format("Error converting {0} to {1}", new System.Object[] {value.GetType().FullName, targetType.FullName}), ex); } }