-Improved cast operator deserialize performance
This commit is contained in:
@@ -275,6 +275,7 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
Console.WriteLine(jsonText);
|
||||
}
|
||||
|
||||
#if !NET20
|
||||
[Test]
|
||||
public void DeserializeDateTimeOffset()
|
||||
{
|
||||
@@ -292,5 +293,6 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
Assert.AreEqual(dto, test);
|
||||
Assert.AreEqual(dto.ToString("o"), test.ToString("o"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -75,4 +75,4 @@ using System.Security;
|
||||
// by using the '*' as shown below:
|
||||
|
||||
[assembly: AssemblyVersion("6.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("6.0.1.17131")]
|
||||
[assembly: AssemblyFileVersion("6.0.2.17209")]
|
||||
|
||||
@@ -548,6 +548,55 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
Assert.AreEqual(RegexOptions.CultureInvariant, r2.Options);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionOperator()
|
||||
{
|
||||
// Creating a simple dictionary that has a non-string key
|
||||
var dictStore = new Dictionary<DictionaryKeyCast, int>();
|
||||
for (var i = 0; i < 800; i++)
|
||||
{
|
||||
dictStore.Add(new DictionaryKeyCast(i.ToString(CultureInfo.InvariantCulture), i), i);
|
||||
}
|
||||
var settings = new JsonSerializerSettings { Formatting = Formatting.Indented };
|
||||
var jsonSerializer = JsonSerializer.Create(settings);
|
||||
var ms = new MemoryStream();
|
||||
|
||||
var streamWriter = new StreamWriter(ms);
|
||||
jsonSerializer.Serialize(streamWriter, dictStore);
|
||||
streamWriter.Flush();
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
var deserialize = jsonSerializer.Deserialize(new StreamReader(ms), typeof(Dictionary<DictionaryKeyCast, int>));
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.WriteLine("Time elapsed: " + stopWatch.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
internal class DictionaryKeyCast
|
||||
{
|
||||
private String _name;
|
||||
private int _number;
|
||||
|
||||
public DictionaryKeyCast(String name, int number)
|
||||
{
|
||||
_name = name;
|
||||
_number = number;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _name + " " + _number;
|
||||
}
|
||||
|
||||
public static implicit operator DictionaryKeyCast(string dictionaryKey)
|
||||
{
|
||||
var strings = dictionaryKey.Split(' ');
|
||||
return new DictionaryKeyCast(strings[0], Convert.ToInt32(strings[1]));
|
||||
}
|
||||
}
|
||||
|
||||
#if !NET20
|
||||
[DataContract]
|
||||
public class BaseDataContractWithHidden
|
||||
|
||||
@@ -91,5 +91,5 @@ using System.Security;
|
||||
// by using the '*' as shown below:
|
||||
|
||||
[assembly: AssemblyVersion("6.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("6.0.1.17203")]
|
||||
[assembly: AssemblyFileVersion("6.0.2.17209")]
|
||||
[assembly: CLSCompliant(true)]
|
||||
|
||||
@@ -345,15 +345,51 @@ namespace Newtonsoft.Json.Utilities
|
||||
}
|
||||
#endif
|
||||
|
||||
#region Convert
|
||||
/// <summary>
|
||||
/// Converts the value to the specified type.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The value to convert.</param>
|
||||
/// <param name="culture">The culture to use when converting.</param>
|
||||
/// <param name="targetType">The type to convert the value to.</param>
|
||||
/// <returns>The converted type.</returns>
|
||||
#region TryConvert
|
||||
internal enum ConvertResult
|
||||
{
|
||||
Success,
|
||||
CannotConvertNull,
|
||||
NotInstantiableType,
|
||||
NoValidConversion
|
||||
}
|
||||
|
||||
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) == ConvertResult.Success)
|
||||
return true;
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
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");
|
||||
@@ -364,7 +400,10 @@ namespace Newtonsoft.Json.Utilities
|
||||
Type initialType = initialValue.GetType();
|
||||
|
||||
if (targetType == initialType)
|
||||
return initialValue;
|
||||
{
|
||||
value = initialValue;
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
|
||||
// use Convert.ChangeType if both types are IConvertible
|
||||
if (ConvertUtils.IsConvertible(initialValue.GetType()) && ConvertUtils.IsConvertible(targetType))
|
||||
@@ -372,41 +411,75 @@ namespace Newtonsoft.Json.Utilities
|
||||
if (targetType.IsEnum())
|
||||
{
|
||||
if (initialValue is string)
|
||||
return Enum.Parse(targetType, initialValue.ToString(), true);
|
||||
{
|
||||
value = Enum.Parse(targetType, initialValue.ToString(), true);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
else if (IsInteger(initialValue))
|
||||
return Enum.ToObject(targetType, initialValue);
|
||||
{
|
||||
value = Enum.ToObject(targetType, initialValue);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
}
|
||||
|
||||
return System.Convert.ChangeType(initialValue, targetType, culture);
|
||||
value = System.Convert.ChangeType(initialValue, targetType, culture);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
|
||||
#if !NET20
|
||||
if (initialValue is DateTime && targetType == typeof(DateTimeOffset))
|
||||
return new DateTimeOffset((DateTime)initialValue);
|
||||
{
|
||||
value = new DateTimeOffset((DateTime)initialValue);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (initialValue is byte[] && targetType == typeof(Guid))
|
||||
return new Guid((byte[])initialValue);
|
||||
{
|
||||
value = new Guid((byte[])initialValue);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
|
||||
if (initialValue is string)
|
||||
{
|
||||
if (targetType == typeof(Guid))
|
||||
return new Guid((string)initialValue);
|
||||
{
|
||||
value = new Guid((string)initialValue);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
if (targetType == typeof(Uri))
|
||||
return new Uri((string)initialValue, UriKind.RelativeOrAbsolute);
|
||||
{
|
||||
value = new Uri((string)initialValue, UriKind.RelativeOrAbsolute);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
if (targetType == typeof(TimeSpan))
|
||||
return ParseTimeSpan((string)initialValue);
|
||||
if (targetType == typeof (byte[]))
|
||||
return System.Convert.FromBase64String((string)initialValue);
|
||||
{
|
||||
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))
|
||||
return Type.GetType((string)initialValue, true);
|
||||
{
|
||||
value = Type.GetType((string)initialValue, true);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
}
|
||||
|
||||
#if !(NET20 || NET35 || PORTABLE40 || PORTABLE)
|
||||
if (targetType == typeof(BigInteger))
|
||||
return ToBigInteger(initialValue);
|
||||
{
|
||||
value = ToBigInteger(initialValue);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
if (initialValue is BigInteger)
|
||||
return FromBigInteger((BigInteger)initialValue, targetType);
|
||||
{
|
||||
value = FromBigInteger((BigInteger)initialValue, targetType);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !(NETFX_CORE || PORTABLE40 || PORTABLE)
|
||||
@@ -414,58 +487,50 @@ namespace Newtonsoft.Json.Utilities
|
||||
TypeConverter toConverter = GetConverter(initialType);
|
||||
|
||||
if (toConverter != null && toConverter.CanConvertTo(targetType))
|
||||
return toConverter.ConvertTo(null, culture, initialValue, targetType);
|
||||
{
|
||||
value = toConverter.ConvertTo(null, culture, initialValue, targetType);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
|
||||
TypeConverter fromConverter = GetConverter(targetType);
|
||||
|
||||
if (fromConverter != null && fromConverter.CanConvertFrom(initialType))
|
||||
return fromConverter.ConvertFrom(null, culture, initialValue);
|
||||
{
|
||||
value = fromConverter.ConvertFrom(null, culture, initialValue);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
#endif
|
||||
#if !(NETFX_CORE || PORTABLE40 || PORTABLE)
|
||||
// handle DBNull and INullable
|
||||
if (initialValue == DBNull.Value)
|
||||
{
|
||||
if (ReflectionUtils.IsNullable(targetType))
|
||||
return EnsureTypeAssignable(null, initialType, targetType);
|
||||
{
|
||||
value = EnsureTypeAssignable(null, initialType, targetType);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
|
||||
throw new Exception("Can not convert null {0} into non-nullable {1}.".FormatWith(CultureInfo.InvariantCulture, initialType, targetType));
|
||||
// cannot convert null to non-nullable
|
||||
value = null;
|
||||
return ConvertResult.CannotConvertNull;
|
||||
}
|
||||
#endif
|
||||
#if !(NETFX_CORE || PORTABLE40 || PORTABLE)
|
||||
if (initialValue is INullable)
|
||||
return EnsureTypeAssignable(ToValue((INullable)initialValue), initialType, targetType);
|
||||
{
|
||||
value = EnsureTypeAssignable(ToValue((INullable)initialValue), initialType, targetType);
|
||||
return ConvertResult.Success;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (targetType.IsInterface() || targetType.IsGenericTypeDefinition() || targetType.IsAbstract())
|
||||
throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), "targetType");
|
||||
|
||||
throw new InvalidOperationException("Can not convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, initialType, targetType));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TryConvert
|
||||
/// <summary>
|
||||
/// Converts the value to the specified type.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The value to convert.</param>
|
||||
/// <param name="culture">The culture to use when converting.</param>
|
||||
/// <param name="targetType">The type to convert the value to.</param>
|
||||
/// <param name="convertedValue">The converted value if the conversion was successful or the default value of <c>T</c> if it failed.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if <c>initialValue</c> was converted successfully; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool TryConvert(object initialValue, CultureInfo culture, Type targetType, out object convertedValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
convertedValue = Convert(initialValue, culture, targetType);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
convertedValue = null;
|
||||
return false;
|
||||
value = null;
|
||||
return ConvertResult.NotInstantiableType;
|
||||
}
|
||||
|
||||
value = null;
|
||||
return ConvertResult.NoValidConversion;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
Reference in New Issue
Block a user