/// <summary> /// Helper method to set value of either property or field /// </summary> /// <param name="result"></param> /// <param name="memberType"></param> /// <param name="memberInfo"></param> /// <param name="value"></param> private void SetMemberValue(Object result, Type memberType, MemberInfo memberInfo, object value) { if (memberInfo is PropertyInfo) { // set value of public property ((PropertyInfo)memberInfo).SetValue( result, JsonReader.CoerceType(memberType, value, this.index, this.AllowNullValueTypes), null); } else if (memberInfo is FieldInfo) { // set value of public field ((FieldInfo)memberInfo).SetValue( result, JsonReader.CoerceType(memberType, value, this.index, this.AllowNullValueTypes)); } // all other values are ignored }
private static object CoerceType(Type targetType, object value, int index, bool allowNullValueTypes) { bool isNullable = JsonReader.IsNullable(targetType); if (value == null) { if (allowNullValueTypes && targetType.IsValueType && !isNullable) { throw new JsonDeserializationException(String.Format(JsonReader.ErrorNullValueType, targetType.FullName), index); } return(value); } if (isNullable) { // nullable types have a real underlying struct Type[] genericArgs = targetType.GetGenericArguments(); if (genericArgs.Length == 1) { targetType = genericArgs[0]; } } Type actualType = value.GetType(); if (targetType.IsAssignableFrom(actualType)) { return(value); } if (targetType.IsEnum) { if (!Enum.IsDefined(targetType, value)) { // if isn't a defined value perhaps it is the JsonName foreach (FieldInfo field in targetType.GetFields()) { string jsonName = JsonNameAttribute.GetJsonName(field); if (((string)value).Equals(jsonName)) { value = field.Name; break; } } } if (value is String) { return(Enum.Parse(targetType, (string)value)); } else { return(Convert.ChangeType(value, targetType)); } } if (actualType.IsArray && !targetType.IsArray) { return(JsonReader.CoerceArray(targetType, actualType, value, index, allowNullValueTypes)); } if (value is String) { if (targetType == typeof(DateTime)) { DateTime date; if (DateTime.TryParse( (string)value, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.RoundtripKind | DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.NoCurrentDateDefault, out date)) { return(date); } } else if (targetType == typeof(Guid)) { // try-catch is pointless since will throw upon generic conversion return(new Guid((string)value)); } else if (targetType == typeof(Uri)) { Uri uri; if (Uri.TryCreate((string)value, UriKind.RelativeOrAbsolute, out uri)) { return(uri); } } else if (targetType == typeof(Version)) { // try-catch is pointless since will throw upon generic conversion return(new Version((string)value)); } } else if (targetType == typeof(TimeSpan)) { return(new TimeSpan((long)JsonReader.CoerceType(typeof(Int64), value, index, allowNullValueTypes))); } TypeConverter converter = TypeDescriptor.GetConverter(targetType); if (converter.CanConvertFrom(actualType)) { return(converter.ConvertFrom(value)); } converter = TypeDescriptor.GetConverter(actualType); if (converter.CanConvertTo(targetType)) { return(converter.ConvertTo(value, targetType)); } return(Convert.ChangeType(value, targetType)); }
public static object CoerceType(Type targetType, object value) { return(JsonReader.CoerceType(targetType, value, -1, false)); }
private object ReadNumber(Type expectedType) { bool hasDecimal = false; bool hasExponent = false; int start = this.index; int precision = 0; int exponent = 0; // optional minus part if (this.Source[this.index] == JsonReader.OperatorNegate) { // consume sign this.index++; if (this.index >= this.SourceLength || !Char.IsDigit(this.Source[this.index])) { throw new JsonDeserializationException(JsonReader.ErrorIllegalNumber, this.index); } } // integer part while ((this.index < this.SourceLength) && Char.IsDigit(this.Source[this.index])) { // consume digit this.index++; } // optional decimal part if ((this.index < this.SourceLength) && (this.Source[this.index] == '.')) { hasDecimal = true; // consume decimal this.index++; if (this.index >= this.SourceLength || !Char.IsDigit(this.Source[this.index])) { throw new JsonDeserializationException(JsonReader.ErrorIllegalNumber, this.index); } // fraction part while (this.index < this.SourceLength && Char.IsDigit(this.Source[this.index])) { // consume digit this.index++; } } // note the number of significant digits precision = this.index - start - (hasDecimal ? 1 : 0); // optional exponent part if (this.index < this.SourceLength && (this.Source[this.index] == 'e' || this.Source[this.index] == 'E')) { hasExponent = true; // consume 'e' this.index++; if (this.index >= this.SourceLength) { throw new JsonDeserializationException(JsonReader.ErrorIllegalNumber, this.index); } int expStart = this.index; // optional minus/plus part if (this.Source[this.index] == JsonReader.OperatorNegate || this.Source[this.index] == JsonReader.OperatorUnaryPlus) { // consume sign this.index++; if (this.index >= this.SourceLength || !Char.IsDigit(this.Source[this.index])) { throw new JsonDeserializationException(JsonReader.ErrorIllegalNumber, this.index); } } else { if (!Char.IsDigit(this.Source[this.index])) { throw new JsonDeserializationException(JsonReader.ErrorIllegalNumber, this.index); } } // exp part while (this.index < this.SourceLength && Char.IsDigit(this.Source[this.index])) { // consume digit this.index++; } Int32.TryParse(this.Source.Substring(expStart, this.index - expStart), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out exponent); } // at this point, we have the full number string and know its characteristics string numberString = this.Source.Substring(start, this.index - start); if (!hasDecimal && !hasExponent && precision < 19) { // is Integer value // parse as most flexible decimal number = Decimal.Parse( numberString, NumberStyles.Integer, NumberFormatInfo.InvariantInfo); if (expectedType != null) { return(JsonReader.CoerceType(expectedType, number, this.index, this.AllowNullValueTypes)); } if (number >= Int32.MinValue && number <= Int32.MaxValue) { // use most common return((int)number); } if (number >= Int64.MinValue && number <= Int64.MaxValue) { // use more flexible return((long)number); } // use most flexible return(number); } else { // is Floating Point value if (expectedType == typeof(Decimal)) { // special case since Double does not convert to Decimal return(Decimal.Parse( numberString, NumberStyles.Float, NumberFormatInfo.InvariantInfo)); } // use native EcmaScript number (IEEE 754) double number = Double.Parse( numberString, NumberStyles.Float, NumberFormatInfo.InvariantInfo); if (expectedType != null) { return(JsonReader.CoerceType(expectedType, number, this.index, this.AllowNullValueTypes)); } return(number); } }
private object ReadString(Type expectedType) { if (this.Source[this.index] != JsonReader.OperatorStringDelim && this.Source[this.index] != JsonReader.OperatorStringDelimAlt) { throw new JsonDeserializationException(JsonReader.ErrorExpectedString, this.index); } char startStringDelim = this.Source[this.index]; // consume opening quote this.index++; if (this.index >= this.SourceLength) { throw new JsonDeserializationException(JsonReader.ErrorUnterminatedString, this.index); } int start = this.index; StringBuilder builder = new StringBuilder(); while (this.Source[this.index] != startStringDelim) { if (this.Source[this.index] == JsonReader.OperatorCharEscape) { // copy chunk before decoding builder.Append(this.Source, start, this.index - start); // consume escape char this.index++; if (this.index >= this.SourceLength) { throw new JsonDeserializationException(JsonReader.ErrorUnterminatedString, this.index); } // decode switch (this.Source[this.index]) { case '0': { // don't allow NULL char '\0' // causes CStrings to terminate break; } case 'b': { // backspace builder.Append('\b'); break; } case 'f': { // formfeed builder.Append('\f'); break; } case 'n': { // newline builder.Append('\n'); break; } case 'r': { // carriage return builder.Append('\r'); break; } case 't': { // tab builder.Append('\t'); break; } case 'u': { // Unicode escape sequence // e.g. Copyright: "\u00A9" // unicode ordinal int utf16; if (this.index + 4 < this.SourceLength && Int32.TryParse( this.Source.Substring(this.index + 1, 4), NumberStyles.AllowHexSpecifier, NumberFormatInfo.InvariantInfo, out utf16)) { builder.Append(Char.ConvertFromUtf32(utf16)); this.index += 4; } else { // using FireFox style recovery, if not a valid hex // escape sequence then treat as single escaped 'u' // followed by rest of string builder.Append(this.Source[this.index]); } break; } default: { builder.Append(Source[this.index]); break; } } this.index++; if (this.index >= this.SourceLength) { throw new JsonDeserializationException(JsonReader.ErrorUnterminatedString, this.index); } start = this.index; } else { // next char index++; if (this.index >= this.SourceLength) { throw new JsonDeserializationException(JsonReader.ErrorUnterminatedString, this.index); } } } // copy rest of string builder.Append(this.Source, start, this.index - start); // consume closing quote this.index++; if (expectedType != null && expectedType != typeof(String)) { return(JsonReader.CoerceType(expectedType, builder.ToString(), this.index, this.AllowNullValueTypes)); } return(builder.ToString()); }
private static object CoerceArray(Type targetType, Type arrayType, object value, int index, bool allowNullValueTypes) { // 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 ConstructorInfo ctor = targetType.GetConstructor(Type.EmptyTypes); if (ctor == null) { throw new JsonDeserializationException(JsonReader.ErrorDefaultCtor, index); } object collection = ctor.Invoke(null); Array arrayValue = (Array)value; // many ICollection types have an AddRange method // which adds all items at once MethodInfo method = targetType.GetMethod("AddRange"); ParameterInfo[] parameters = (method == null) ? null : method.GetParameters(); Type paramType = (parameters == null || parameters.Length != 1) ? null : parameters[0].ParameterType; if (paramType != null && paramType.IsAssignableFrom(arrayType)) { // add all members in one method method.Invoke( collection, new object[] { arrayValue }); return(collection); } else { // many ICollection types have an Add method // which adds items one at a time method = targetType.GetMethod("Add"); parameters = (method == null) ? null : method.GetParameters(); paramType = (parameters == null || parameters.Length != 1) ? null : parameters[0].ParameterType; if (paramType != null) { // loop through adding items to collection foreach (object item in arrayValue) { method.Invoke( collection, new object[] { JsonReader.CoerceType(paramType, item, index, allowNullValueTypes) }); } return(collection); } } // many ICollection types take an IEnumerable or ICollection // as a constructor argument. look through constructors for // a compatible match. ConstructorInfo[] ctors = targetType.GetConstructors(); foreach (ConstructorInfo ctor2 in ctors) { ParameterInfo[] paramList = ctor2.GetParameters(); if (paramList.Length == 1 && paramList[0].ParameterType.IsAssignableFrom(arrayType)) { try { // invoke first constructor that can take this value as an argument return(ctor2.Invoke( new object[] { value } )); } catch { // there might exist a better match continue; } } } return(Convert.ChangeType(value, targetType)); }