-Improved cast operator deserialize performance

This commit is contained in:
James Newton-King
2014-04-10 00:24:21 +12:00
parent 6cca4e72be
commit 9e2581be74
5 changed files with 172 additions and 56 deletions
@@ -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)]
+119 -54
View File
@@ -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