<PackageReference Include="Newtonsoft.Json" Version="6.0.4" />

ConvertUtils

static class ConvertUtils
using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data.SqlTypes; using System.Globalization; using System.Numerics; using System.Reflection; namespace Newtonsoft.Json.Utilities { internal static class ConvertUtils { internal struct TypeConvertKey : IEquatable<TypeConvertKey> { private readonly Type _initialType; private readonly Type _targetType; public Type InitialType => _initialType; public Type TargetType => _targetType; public TypeConvertKey(Type initialType, Type targetType) { _initialType = initialType; _targetType = targetType; } public override int GetHashCode() { return _initialType.GetHashCode() ^ _targetType.GetHashCode(); } public override bool Equals(object obj) { if (!(obj is TypeConvertKey)) return false; return Equals((TypeConvertKey)obj); } public bool Equals(TypeConvertKey other) { if (_initialType == other._initialType) return _targetType == other._targetType; return false; } } internal enum ConvertResult { Success, CannotConvertNull, NotInstantiableType, NoValidConversion } private static readonly Dictionary<Type, PrimitiveTypeCode> TypeCodeMap = new Dictionary<Type, PrimitiveTypeCode> { { typeof(char), PrimitiveTypeCode.Char }, { typeof(char?), PrimitiveTypeCode.CharNullable }, { typeof(bool), PrimitiveTypeCode.Boolean }, { typeof(bool?), PrimitiveTypeCode.BooleanNullable }, { typeof(sbyte), PrimitiveTypeCode.SByte }, { typeof(sbyte?), PrimitiveTypeCode.SByteNullable }, { typeof(short), PrimitiveTypeCode.Int16 }, { typeof(short?), PrimitiveTypeCode.Int16Nullable }, { typeof(ushort), PrimitiveTypeCode.UInt16 }, { typeof(ushort?), PrimitiveTypeCode.UInt16Nullable }, { typeof(int), PrimitiveTypeCode.Int32 }, { typeof(int?), PrimitiveTypeCode.Int32Nullable }, { typeof(byte), PrimitiveTypeCode.Byte }, { typeof(byte?), PrimitiveTypeCode.ByteNullable }, { typeof(uint), PrimitiveTypeCode.UInt32 }, { typeof(uint?), PrimitiveTypeCode.UInt32Nullable }, { typeof(long), PrimitiveTypeCode.Int64 }, { typeof(long?), PrimitiveTypeCode.Int64Nullable }, { typeof(ulong), PrimitiveTypeCode.UInt64 }, { typeof(ulong?), PrimitiveTypeCode.UInt64Nullable }, { typeof(float), PrimitiveTypeCode.Single }, { typeof(float?), PrimitiveTypeCode.SingleNullable }, { typeof(double), PrimitiveTypeCode.Double }, { typeof(double?), PrimitiveTypeCode.DoubleNullable }, { typeof(DateTime), PrimitiveTypeCode.DateTime }, { typeof(DateTime?), PrimitiveTypeCode.DateTimeNullable }, { typeof(DateTimeOffset), PrimitiveTypeCode.DateTimeOffset }, { typeof(DateTimeOffset?), PrimitiveTypeCode.DateTimeOffsetNullable }, { typeof(decimal), PrimitiveTypeCode.Decimal }, { typeof(decimal?), PrimitiveTypeCode.DecimalNullable }, { typeof(Guid), PrimitiveTypeCode.Guid }, { typeof(Guid?), PrimitiveTypeCode.GuidNullable }, { typeof(TimeSpan), PrimitiveTypeCode.TimeSpan }, { typeof(TimeSpan?), PrimitiveTypeCode.TimeSpanNullable }, { typeof(BigInteger), PrimitiveTypeCode.BigInteger }, { typeof(BigInteger?), PrimitiveTypeCode.BigIntegerNullable }, { typeof(Uri), PrimitiveTypeCode.Uri }, { typeof(string), PrimitiveTypeCode.String }, { typeof(byte[]), PrimitiveTypeCode.Bytes }, { typeof(DBNull), PrimitiveTypeCode.DBNull } }; private static readonly List<TypeInformation> PrimitiveTypeCodes = new List<TypeInformation> { new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.Empty }, new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.Object }, new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.DBNull }, new TypeInformation { Type = typeof(bool), TypeCode = PrimitiveTypeCode.Boolean }, new TypeInformation { Type = typeof(char), TypeCode = PrimitiveTypeCode.Char }, new TypeInformation { Type = typeof(sbyte), TypeCode = PrimitiveTypeCode.SByte }, new TypeInformation { Type = typeof(byte), TypeCode = PrimitiveTypeCode.Byte }, new TypeInformation { Type = typeof(short), TypeCode = PrimitiveTypeCode.Int16 }, new TypeInformation { Type = typeof(ushort), TypeCode = PrimitiveTypeCode.UInt16 }, new TypeInformation { Type = typeof(int), TypeCode = PrimitiveTypeCode.Int32 }, new TypeInformation { Type = typeof(uint), TypeCode = PrimitiveTypeCode.UInt32 }, new TypeInformation { Type = typeof(long), TypeCode = PrimitiveTypeCode.Int64 }, new TypeInformation { Type = typeof(ulong), TypeCode = PrimitiveTypeCode.UInt64 }, new TypeInformation { Type = typeof(float), TypeCode = PrimitiveTypeCode.Single }, new TypeInformation { Type = typeof(double), TypeCode = PrimitiveTypeCode.Double }, new TypeInformation { Type = typeof(decimal), TypeCode = PrimitiveTypeCode.Decimal }, new TypeInformation { Type = typeof(DateTime), TypeCode = PrimitiveTypeCode.DateTime }, new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.Empty }, new TypeInformation { Type = typeof(string), TypeCode = PrimitiveTypeCode.String } }; private static readonly ThreadSafeStore<TypeConvertKey, Func<object, object>> CastConverters = new ThreadSafeStore<TypeConvertKey, Func<object, object>>(CreateCastConverter); public static PrimitiveTypeCode GetTypeCode(Type t) { if (TypeCodeMap.TryGetValue(t, out PrimitiveTypeCode value)) return value; if (t.IsEnum()) return GetTypeCode(Enum.GetUnderlyingType(t)); if (ReflectionUtils.IsNullableType(t)) { Type underlyingType = Nullable.GetUnderlyingType(t); if (underlyingType.IsEnum()) { Type t2 = typeof(Nullable<>).MakeGenericType(Enum.GetUnderlyingType(underlyingType)); return GetTypeCode(t2); } } return PrimitiveTypeCode.Object; } public static PrimitiveTypeCode GetTypeCode(object o) { return GetTypeCode(o.GetType()); } public static TypeInformation GetTypeInformation(IConvertible convertable) { return PrimitiveTypeCodes[(int)convertable.GetTypeCode()]; } public static bool IsConvertible(Type t) { return typeof(IConvertible).IsAssignableFrom(t); } public static TimeSpan ParseTimeSpan(string input) { return TimeSpan.Parse(input, CultureInfo.InvariantCulture); } private static Func<object, object> CreateCastConverter(TypeConvertKey t) { MethodInfo method = t.TargetType.GetMethod("op_Implicit", new Type[1] { t.InitialType }); if (method == (MethodInfo)null) method = t.TargetType.GetMethod("op_Explicit", new Type[1] { t.InitialType }); if (method == (MethodInfo)null) return null; MethodCall<object, object> call = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(method); return (object o) => call(null, o); } internal static BigInteger ToBigInteger(object value) { if (value is BigInteger) return (BigInteger)value; if (value is string) return BigInteger.Parse((string)value, CultureInfo.InvariantCulture); if (value is float) return new BigInteger((float)value); if (value is double) return new BigInteger((double)value); if (value is decimal) return new BigInteger((decimal)value); if (value is int) return new BigInteger((int)value); if (value is long) return new BigInteger((long)value); if (value is uint) return new BigInteger((uint)value); if (value is ulong) return new BigInteger((ulong)value); if (value is byte[]) return new BigInteger((byte[])value); throw new InvalidCastException("Cannot convert {0} to BigInteger.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); } public static object FromBigInteger(BigInteger i, Type targetType) { if (targetType == typeof(decimal)) return (decimal)i; if (targetType == typeof(double)) return (double)i; if (targetType == typeof(float)) return (float)i; if (!(targetType == typeof(ulong))) try { return System.Convert.ChangeType((long)i, targetType, CultureInfo.InvariantCulture); } catch (Exception innerException) { throw new InvalidOperationException("Can not convert from BigInteger to {0}.".FormatWith(CultureInfo.InvariantCulture, targetType), innerException); } return (ulong)i; } public static object Convert(object initialValue, CultureInfo culture, Type targetType) { object value; switch (TryConvertInternal(initialValue, culture, targetType, out value)) { case ConvertResult.Success: return value; case ConvertResult.CannotConvertNull: throw new Exception("Can not convert null {0} into non-nullable {1}.".FormatWith(CultureInfo.InvariantCulture, initialValue.GetType(), targetType)); case ConvertResult.NotInstantiableType: throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), "targetType"); case ConvertResult.NoValidConversion: throw new InvalidOperationException("Can not convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, initialValue.GetType(), targetType)); default: throw new InvalidOperationException("Unexpected conversion result."); } } private static bool TryConvert(object initialValue, CultureInfo culture, Type targetType, out object value) { try { if (TryConvertInternal(initialValue, culture, targetType, out value) != 0) { value = null; return false; } return true; } catch { value = null; return false; } } private static ConvertResult TryConvertInternal(object initialValue, CultureInfo culture, Type targetType, out object value) { if (initialValue == null) throw new ArgumentNullException("initialValue"); if (ReflectionUtils.IsNullableType(targetType)) targetType = Nullable.GetUnderlyingType(targetType); Type type = initialValue.GetType(); if (targetType == type) { value = initialValue; return ConvertResult.Success; } if (IsConvertible(initialValue.GetType()) && IsConvertible(targetType)) { if (targetType.IsEnum()) { if (initialValue is string) { value = Enum.Parse(targetType, initialValue.ToString(), true); return ConvertResult.Success; } if (IsInteger(initialValue)) { value = Enum.ToObject(targetType, initialValue); return ConvertResult.Success; } } value = System.Convert.ChangeType(initialValue, targetType, culture); return ConvertResult.Success; } if (initialValue is DateTime && targetType == typeof(DateTimeOffset)) { value = new DateTimeOffset((DateTime)initialValue); return ConvertResult.Success; } if (initialValue is byte[] && targetType == typeof(Guid)) { value = new Guid((byte[])initialValue); return ConvertResult.Success; } if (initialValue is Guid && targetType == typeof(byte[])) { value = ((Guid)initialValue).ToByteArray(); return ConvertResult.Success; } if (initialValue is string) { if (targetType == typeof(Guid)) { value = new Guid((string)initialValue); return ConvertResult.Success; } if (targetType == typeof(Uri)) { value = new Uri((string)initialValue, UriKind.RelativeOrAbsolute); return ConvertResult.Success; } if (targetType == typeof(TimeSpan)) { value = ParseTimeSpan((string)initialValue); return ConvertResult.Success; } if (targetType == typeof(byte[])) { value = System.Convert.FromBase64String((string)initialValue); return ConvertResult.Success; } if (typeof(Type).IsAssignableFrom(targetType)) { value = Type.GetType((string)initialValue, true); return ConvertResult.Success; } } if (targetType == typeof(BigInteger)) { value = ToBigInteger(initialValue); return ConvertResult.Success; } if (initialValue is BigInteger) { value = FromBigInteger((BigInteger)initialValue, targetType); return ConvertResult.Success; } TypeConverter converter = GetConverter(type); if (converter != null && converter.CanConvertTo(targetType)) { value = converter.ConvertTo(null, culture, initialValue, targetType); return ConvertResult.Success; } TypeConverter converter2 = GetConverter(targetType); if (converter2 != null && converter2.CanConvertFrom(type)) { value = converter2.ConvertFrom(null, culture, initialValue); return ConvertResult.Success; } if (initialValue == DBNull.Value) { if (ReflectionUtils.IsNullable(targetType)) { value = EnsureTypeAssignable(null, type, targetType); return ConvertResult.Success; } value = null; return ConvertResult.CannotConvertNull; } if (initialValue is INullable) { value = EnsureTypeAssignable(ToValue((INullable)initialValue), type, targetType); return ConvertResult.Success; } if (targetType.IsInterface() || targetType.IsGenericTypeDefinition() || targetType.IsAbstract()) { value = null; return ConvertResult.NotInstantiableType; } value = null; return ConvertResult.NoValidConversion; } public static object ConvertOrCast(object initialValue, CultureInfo culture, Type targetType) { if (targetType == typeof(object)) return initialValue; if (initialValue == null && ReflectionUtils.IsNullable(targetType)) return null; if (TryConvert(initialValue, culture, targetType, out object value)) return value; return EnsureTypeAssignable(initialValue, ReflectionUtils.GetObjectType(initialValue), targetType); } private static object EnsureTypeAssignable(object value, Type initialType, Type targetType) { Type type = value?.GetType(); if (value != null) { if (targetType.IsAssignableFrom(type)) return value; Func<object, object> func = CastConverters.Get(new TypeConvertKey(type, targetType)); if (func != null) return func(value); } else if (ReflectionUtils.IsNullable(targetType)) { return null; } throw new ArgumentException("Could not cast or convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, (initialType != (Type)null) ? initialType.ToString() : "{null}", targetType)); } public static object ToValue(INullable nullableValue) { if (nullableValue == null) return null; if (nullableValue is SqlInt32) return ToValue((SqlInt32)nullableValue); if (nullableValue is SqlInt64) return ToValue((SqlInt64)nullableValue); if (nullableValue is SqlBoolean) return ToValue((SqlBoolean)nullableValue); if (nullableValue is SqlString) return ToValue((SqlString)nullableValue); if (nullableValue is SqlDateTime) return ToValue((SqlDateTime)nullableValue); throw new ArgumentException("Unsupported INullable type: {0}".FormatWith(CultureInfo.InvariantCulture, nullableValue.GetType())); } internal static TypeConverter GetConverter(Type t) { return JsonTypeReflector.GetTypeConverter(t); } public static bool IsInteger(object value) { switch (GetTypeCode(value)) { case PrimitiveTypeCode.SByte: case PrimitiveTypeCode.Int16: case PrimitiveTypeCode.UInt16: case PrimitiveTypeCode.Int32: case PrimitiveTypeCode.Byte: case PrimitiveTypeCode.UInt32: case PrimitiveTypeCode.Int64: case PrimitiveTypeCode.UInt64: return true; default: return false; } } public static ParseResult Int32TryParse(char[] chars, int start, int length, out int value) { value = 0; if (length == 0) return ParseResult.Invalid; bool flag = chars[start] == '-'; if (flag) { if (length == 1) return ParseResult.Invalid; start++; length--; } int num = start + length; for (int i = start; i < num; i++) { int num2 = chars[i] - 48; if (num2 < 0 || num2 > 9) return ParseResult.Invalid; int num3 = 10 * value - num2; if (num3 > value) { for (i++; i < num; i++) { num2 = chars[i] - 48; if (num2 < 0 || num2 > 9) return ParseResult.Invalid; } return ParseResult.Overflow; } value = num3; } if (!flag) { if (value == -2147483648) return ParseResult.Overflow; value = -value; } return ParseResult.Success; } public static ParseResult Int64TryParse(char[] chars, int start, int length, out long value) { value = 0; if (length == 0) return ParseResult.Invalid; bool flag = chars[start] == '-'; if (flag) { if (length == 1) return ParseResult.Invalid; start++; length--; } int num = start + length; for (int i = start; i < num; i++) { int num2 = chars[i] - 48; if (num2 < 0 || num2 > 9) return ParseResult.Invalid; long num3 = 10 * value - num2; if (num3 > value) { for (i++; i < num; i++) { num2 = chars[i] - 48; if (num2 < 0 || num2 > 9) return ParseResult.Invalid; } return ParseResult.Overflow; } value = num3; } if (!flag) { if (value == -9223372036854775808) return ParseResult.Overflow; value = -value; } return ParseResult.Success; } public static bool TryConvertGuid(string s, out Guid g) { return Guid.TryParse(s, out g); } } }