From 57ccaf00bffd7deb72a8c508a3c4cf4a79098a83 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 24 Aug 2014 23:33:08 +1200 Subject: [PATCH] -Improved Portable40 assembly performance by using compiled expression reflection -Fixed converting string JValues to enums with JToken.ToObject --- .../Converters/XmlNodeConverterTest.cs | 37 ++++++++++++++++ Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs | 6 +++ Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs | 42 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 2 +- ...xpressionReflectionDelegateFactoryTests.cs | 3 +- Src/Newtonsoft.Json/JsonConvert.cs | 3 +- Src/Newtonsoft.Json/JsonWriter.cs | 2 +- Src/Newtonsoft.Json/Linq/IJEnumerable.cs | 2 +- Src/Newtonsoft.Json/Linq/JToken.cs | 16 ++++++- Src/Newtonsoft.Json/Linq/JValue.cs | 2 +- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/ExpressionValueProvider.cs | 2 +- .../Serialization/JsonTypeReflector.cs | 4 +- Src/Newtonsoft.Json/Utilities/ConvertUtils.cs | 21 +++++++--- .../ExpressionReflectionDelegateFactory.cs | 2 +- 15 files changed, 125 insertions(+), 21 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs b/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs index d89f11e2..d701192e 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs @@ -137,8 +137,45 @@ namespace Newtonsoft.Json.Tests.Converters Assert.IsNull(foo2.Bar); } + + [Test] + public void MultipleNamespacesXDocument() + { + string xml = @""; + + XDocument d = XDocument.Parse(xml); + + string json = JsonConvert.SerializeObject(d, Formatting.Indented); + + Console.WriteLine(json); + + XDocument doc = JsonConvert.DeserializeObject(json); + + Console.WriteLine(doc.ToString()); + + Assert.AreEqual(xml, doc.ToString()); + } #endif + [Test] + public void MultipleNamespacesXmlDocument() + { + string xml = @""; + + XmlDocument d = new XmlDocument(); + d.LoadXml(xml); + + string json = JsonConvert.SerializeObject(d, Formatting.Indented); + + Console.WriteLine(json); + + XmlDocument doc = JsonConvert.DeserializeObject(json); + + Console.WriteLine(doc.OuterXml); + + Assert.AreEqual(xml, doc.OuterXml); + } + [Test] public void SerializeXmlElement() { diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs index 91319040..17d73bbd 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -444,6 +444,9 @@ namespace Newtonsoft.Json.Tests.Linq ExceptionAssert.Throws("Can not convert Date to SByte.", () => { var i = (sbyte?)new JValue(DateTime.Now); }); ExceptionAssert.Throws("Can not convert Date to SByte.", () => { var i = (sbyte)new JValue(DateTime.Now); }); + + ExceptionAssert.Throws("Could not convert 'Ordinal1' to StringComparison.", () => { var i = (new JValue("Ordinal1")).ToObject(); }); + ExceptionAssert.Throws("Could not convert 'Ordinal1' to StringComparison.", () => { var i = (new JValue("Ordinal1")).ToObject(); }); } [Test] @@ -492,6 +495,9 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(new Guid("A34B2080-B5F0-488E-834D-45D44ECB9E5C"), (new JValue(new Guid("A34B2080-B5F0-488E-834D-45D44ECB9E5C")).ToObject(typeof(Guid)))); Assert.AreEqual(new Guid("A34B2080-B5F0-488E-834D-45D44ECB9E5C"), (new JValue(new Guid("A34B2080-B5F0-488E-834D-45D44ECB9E5C")).ToObject(typeof(Guid?)))); Assert.AreEqual(new Uri("http://www.google.com/"), (new JValue(new Uri("http://www.google.com/")).ToObject(typeof(Uri)))); + Assert.AreEqual(StringComparison.Ordinal, (new JValue("Ordinal").ToObject(typeof(StringComparison)))); + Assert.AreEqual(StringComparison.Ordinal, (new JValue("Ordinal").ToObject(typeof(StringComparison?)))); + Assert.AreEqual(null, (JValue.CreateNull().ToObject(typeof(StringComparison?)))); } [Test] diff --git a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs index 042150a8..4d13e910 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs @@ -755,5 +755,47 @@ namespace Newtonsoft.Json.Tests.Linq ""name"": ""Hello World"" }", json); } + +#if !(NET20 || NET35 || PORTABLE40) + [Test] + public void EnumTests() + { + JValue v = new JValue(StringComparison.Ordinal); + Assert.AreEqual(JTokenType.Integer, v.Type); + + string s = v.ToString(); + Assert.AreEqual("Ordinal", s); + + StringComparison e = v.ToObject(); + Assert.AreEqual(StringComparison.Ordinal, e); + + dynamic d = new JValue(StringComparison.CurrentCultureIgnoreCase); + StringComparison e2 = (StringComparison)d; + Assert.AreEqual(StringComparison.CurrentCultureIgnoreCase, e2); + + string s1 = d.ToString(); + Assert.AreEqual("CurrentCultureIgnoreCase", s1); + + string s2 = (string)d; + Assert.AreEqual("CurrentCultureIgnoreCase", s2); + + d = new JValue("OrdinalIgnoreCase"); + StringComparison e3 = (StringComparison)d; + Assert.AreEqual(StringComparison.OrdinalIgnoreCase, e3); + + v = new JValue("ORDINAL"); + d = v; + StringComparison e4 = (StringComparison)d; + Assert.AreEqual(StringComparison.Ordinal, e4); + + StringComparison e5 = v.ToObject(); + Assert.AreEqual(StringComparison.Ordinal, e5); + + v = new JValue((int)StringComparison.InvariantCultureIgnoreCase); + Assert.AreEqual(JTokenType.Integer, v.Type); + StringComparison e6 = v.ToObject(); + Assert.AreEqual(StringComparison.InvariantCultureIgnoreCase, e6); + } +#endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 13ad467a..b2213523 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.4.17603")] +[assembly: AssemblyFileVersion("6.0.4.17624")] diff --git a/Src/Newtonsoft.Json.Tests/Utilities/ExpressionReflectionDelegateFactoryTests.cs b/Src/Newtonsoft.Json.Tests/Utilities/ExpressionReflectionDelegateFactoryTests.cs index 72f310c3..6129509b 100644 --- a/Src/Newtonsoft.Json.Tests/Utilities/ExpressionReflectionDelegateFactoryTests.cs +++ b/Src/Newtonsoft.Json.Tests/Utilities/ExpressionReflectionDelegateFactoryTests.cs @@ -23,7 +23,7 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion -#if !(PORTABLE40 || NET20 || NET35) +#if !(NET20 || NET35) using System; using System.Diagnostics; using System.Reflection; @@ -299,5 +299,4 @@ namespace Newtonsoft.Json.Tests.Utilities } } } - #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 647a6cbb..81d50a15 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -40,7 +40,6 @@ using Newtonsoft.Json.Serialization; using System.Text; #if !(NET20 || PORTABLE40) using System.Xml.Linq; - #endif namespace Newtonsoft.Json @@ -434,7 +433,7 @@ namespace Newtonsoft.Json if (value == null) return Null; - PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(value); + PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(value.GetType()); switch (typeCode) { diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index 6a645572..a1cd1888 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -1226,7 +1226,7 @@ namespace Newtonsoft.Json throw CreateUnsupportedTypeException(this, value); #endif - WriteValue(this, ConvertUtils.GetTypeCode(value), value); + WriteValue(this, ConvertUtils.GetTypeCode(value.GetType()), value); } } #endregion diff --git a/Src/Newtonsoft.Json/Linq/IJEnumerable.cs b/Src/Newtonsoft.Json/Linq/IJEnumerable.cs index c5c03aff..fff8c2e6 100644 --- a/Src/Newtonsoft.Json/Linq/IJEnumerable.cs +++ b/Src/Newtonsoft.Json/Linq/IJEnumerable.cs @@ -32,7 +32,7 @@ namespace Newtonsoft.Json.Linq /// /// The type of token public interface IJEnumerable< -#if !(NET20 || NET35 || PORTABLE40) +#if !(NET20 || NET35) out #endif T> : IEnumerable where T : JToken diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 550683df..4596bf3b 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -1701,7 +1701,21 @@ namespace Newtonsoft.Json.Linq { if (JsonConvert.DefaultSettings == null) { - PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(objectType); + bool isEnum; + PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(objectType, out isEnum); + + if (isEnum && Type == JTokenType.String) + { + Type enumType = objectType.IsEnum() ? objectType : Nullable.GetUnderlyingType(objectType); + try + { + return Enum.Parse(enumType, (string)this, true); + } + catch (Exception ex) + { + throw new ArgumentException("Could not convert '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, (string)this, enumType.Name), ex); + } + } switch (typeCode) { diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index cf6f7d65..4c21afd5 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -872,7 +872,7 @@ namespace Newtonsoft.Json.Linq return ReflectionUtils.IsNullable(binder.Type); } - result = ConvertUtils.Convert(instance.Value, CultureInfo.InvariantCulture, binder.Type); + result = ConvertUtils.Convert(value, CultureInfo.InvariantCulture, binder.Type); return true; } diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 5e88bb9a..52fe5f60 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -91,5 +91,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.4.17603")] +[assembly: AssemblyFileVersion("6.0.4.17624")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs b/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs index 47102d5e..ebb929e3 100644 --- a/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs +++ b/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs @@ -23,7 +23,7 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion -#if !(NET20 || NET35 || PORTABLE40) +#if !(NET20 || NET35) using System; using System.Collections.Generic; #if NET20 diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index bbe63876..8f8b0f9d 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -403,10 +403,8 @@ namespace Newtonsoft.Json.Serialization return DynamicReflectionDelegateFactory.Instance; return LateBoundReflectionDelegateFactory.Instance; -#elif !(PORTABLE40) - return ExpressionReflectionDelegateFactory.Instance; #else - return LateBoundReflectionDelegateFactory.Instance; + return ExpressionReflectionDelegateFactory.Instance; #endif } } diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 3fbb3849..c9dd18f5 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -187,13 +187,25 @@ namespace Newtonsoft.Json.Utilities #endif public static PrimitiveTypeCode GetTypeCode(Type t) + { + bool isEnum; + return GetTypeCode(t, out isEnum); + } + + public static PrimitiveTypeCode GetTypeCode(Type t, out bool isEnum) { PrimitiveTypeCode typeCode; if (TypeCodeMap.TryGetValue(t, out typeCode)) + { + isEnum = false; return typeCode; + } if (t.IsEnum()) + { + isEnum = true; return GetTypeCode(Enum.GetUnderlyingType(t)); + } // performance? if (ReflectionUtils.IsNullableType(t)) @@ -202,18 +214,15 @@ namespace Newtonsoft.Json.Utilities if (nonNullable.IsEnum()) { Type nullableUnderlyingType = typeof(Nullable<>).MakeGenericType(Enum.GetUnderlyingType(nonNullable)); + isEnum = true; return GetTypeCode(nullableUnderlyingType); } } + isEnum = false; return PrimitiveTypeCode.Object; } - public static PrimitiveTypeCode GetTypeCode(object o) - { - return GetTypeCode(o.GetType()); - } - #if !(NETFX_CORE || PORTABLE) public static TypeInformation GetTypeInformation(IConvertible convertable) { @@ -623,7 +632,7 @@ namespace Newtonsoft.Json.Utilities public static bool IsInteger(object value) { - switch (GetTypeCode(value)) + switch (GetTypeCode(value.GetType())) { case PrimitiveTypeCode.SByte: case PrimitiveTypeCode.Byte: diff --git a/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs b/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs index 9e309f2b..d6f2dbbf 100644 --- a/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs +++ b/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs @@ -23,7 +23,7 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion -#if !(PORTABLE40 || NET20 || NET35) +#if !(NET20 || NET35) using System; using System.Linq.Expressions; using System.Reflection;