예제 #1
0
        /// <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
        }
예제 #2
0
        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));
        }
예제 #3
0
 public static object CoerceType(Type targetType, object value)
 {
     return(JsonReader.CoerceType(targetType, value, -1, false));
 }
예제 #4
0
        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);
            }
        }
예제 #5
0
        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());
        }
예제 #6
0
        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));
        }