From ac6c65f48aa4833eac0626acf93f740f6d00a9b6 Mon Sep 17 00:00:00 2001 From: JamesNK Date: Sat, 8 Jan 2011 04:39:33 +0000 Subject: [PATCH] -Updated nuspec file for 4.0 -Added Load and Parse helper methods to JToken -Fixed performance issues when deserializing with partial type names -Fixed deserializing certain decimal values on classes with non-default constructors --- Build/Newtonsoft.Json.nuspec | 3 +- .../JsonTextReaderTest.cs | 32 ++++++++-- Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs | 16 ++++- .../Serialization/JsonSerializerTest.cs | 28 +++++++++ Src/Newtonsoft.Json/Linq/JArray.cs | 4 +- Src/Newtonsoft.Json/Linq/JConstructor.cs | 2 +- Src/Newtonsoft.Json/Linq/JObject.cs | 4 +- Src/Newtonsoft.Json/Linq/JProperty.cs | 2 +- Src/Newtonsoft.Json/Linq/JToken.cs | 26 +++++++++ .../DefaultSerializationBinder.cs | 58 ++++++++++++++++--- .../JsonSerializerInternalReader.cs | 8 ++- 11 files changed, 159 insertions(+), 24 deletions(-) diff --git a/Build/Newtonsoft.Json.nuspec b/Build/Newtonsoft.Json.nuspec index d40d466d..727ff23c 100644 --- a/Build/Newtonsoft.Json.nuspec +++ b/Build/Newtonsoft.Json.nuspec @@ -2,7 +2,8 @@ Newtonsoft.Json - 3.5.8 + Newtonsoft.Json + 4.0.1 James Newton-King Json.NET is a popular high-performance JSON framework for .NET en-US diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index 6a57fa6c..f80566fc 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -203,7 +203,7 @@ Parameter name: reader")] { new JsonTextReader(null); } - + [Test] [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unterminated string. Expected delimiter: '. Line 1, position 3.")] public void UnexpectedEndOfString() @@ -220,7 +220,7 @@ Parameter name: reader")] Assert.AreEqual("h\0i", reader.Value); } - + [Test] [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unexpected end while parsing unicode character. Line 1, position 7.")] public void UnexpectedEndOfHex() @@ -228,7 +228,7 @@ Parameter name: reader")] JsonReader reader = new JsonTextReader(new StringReader(@"'h\u006")); reader.Read(); } - + [Test] [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unterminated string. Expected delimiter: '. Line 1, position 3.")] public void UnexpectedEndOfControlCharacter() @@ -494,10 +494,10 @@ bye", reader.Value); string json = @"[""\u003c"",""\u5f20""]"; JsonTextReader reader = new JsonTextReader(new StringReader(json)); - + reader.Read(); Assert.AreEqual(JsonToken.StartArray, reader.TokenType); - + reader.Read(); Assert.AreEqual("<", reader.Value); @@ -782,5 +782,27 @@ bye", reader.Value); Assert.AreEqual(JsonToken.EndObject, reader.TokenType); } #endif + + [Test] + public void ReadAsDecimal() + { + string json = @"{""decimal"":-7.92281625142643E+28}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + decimal? d = reader.ReadAsDecimal(); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(typeof(decimal), reader.ValueType); + Assert.AreEqual(-79228162514264300000000000000m, d); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs index 1114d284..c7cf491f 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -60,7 +60,7 @@ namespace Newtonsoft.Json.Tests.Linq Assert.IsTrue(JToken.DeepEquals(new JValue(1), c.Values().ElementAt(0))); JValue v; - + v = (JValue)JToken.ReadFrom(new JsonTextReader(new StringReader(@"""stringvalue"""))); Assert.AreEqual("stringvalue", (string)v); @@ -71,6 +71,20 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(1.1, (double)v); } + [Test] + public void Load() + { + JObject o = (JObject)JToken.Load(new JsonTextReader(new StringReader("{'pie':true}"))); + Assert.AreEqual(true, (bool)o["pie"]); + } + + [Test] + public void Parse() + { + JObject o = (JObject)JToken.Parse("{'pie':true}"); + Assert.AreEqual(true, (bool)o["pie"]); + } + [Test] public void Parent() { diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index e31d98a9..176fd8e1 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -4015,5 +4015,33 @@ keyword such as type of business."" Assert.AreEqual("Success", deserializedResponse.Name); Assert.IsTrue(deserializedResponse.Data.DeepEquals(response.Data)); } + + public abstract class Test + { + public abstract T Value { get; set; } + } + + [JsonObject(MemberSerialization.OptIn)] + public class DecimalTest : Test + { + protected DecimalTest() { } + public DecimalTest(decimal val) + { + Value = val; + } + + [JsonProperty] + public override decimal Value { get; set; } + } + + [Test] + public void OnError() + { + var data = new DecimalTest(decimal.MinValue); + var json = JsonConvert.SerializeObject(data); + var obj = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(decimal.MinValue, obj.Value); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JArray.cs b/Src/Newtonsoft.Json/Linq/JArray.cs index 6b4dc5f1..c8d478ef 100644 --- a/Src/Newtonsoft.Json/Linq/JArray.cs +++ b/Src/Newtonsoft.Json/Linq/JArray.cs @@ -98,7 +98,7 @@ namespace Newtonsoft.Json.Linq /// /// A that will be read for the content of the . /// A that contains the JSON that was read from the specified . - public static JArray Load(JsonReader reader) + public static new JArray Load(JsonReader reader) { if (reader.TokenType == JsonToken.None) { @@ -121,7 +121,7 @@ namespace Newtonsoft.Json.Linq /// /// A that contains JSON. /// A populated from the string that contains JSON. - public static JArray Parse(string json) + public static new JArray Parse(string json) { JsonReader jsonReader = new JsonTextReader(new StringReader(json)); diff --git a/Src/Newtonsoft.Json/Linq/JConstructor.cs b/Src/Newtonsoft.Json/Linq/JConstructor.cs index 0f8193c4..a8755463 100644 --- a/Src/Newtonsoft.Json/Linq/JConstructor.cs +++ b/Src/Newtonsoft.Json/Linq/JConstructor.cs @@ -171,7 +171,7 @@ namespace Newtonsoft.Json.Linq /// /// A that will be read for the content of the . /// A that contains the JSON that was read from the specified . - public static JConstructor Load(JsonReader reader) + public static new JConstructor Load(JsonReader reader) { if (reader.TokenType == JsonToken.None) { diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 47066457..69946f50 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -248,7 +248,7 @@ namespace Newtonsoft.Json.Linq /// /// A that will be read for the content of the . /// A that contains the JSON that was read from the specified . - public static JObject Load(JsonReader reader) + public static new JObject Load(JsonReader reader) { ValidationUtils.ArgumentNotNull(reader, "reader"); @@ -276,7 +276,7 @@ namespace Newtonsoft.Json.Linq /// /// A that contains JSON. /// A populated from the string that contains JSON. - public static JObject Parse(string json) + public static new JObject Parse(string json) { JsonReader jsonReader = new JsonTextReader(new StringReader(json)); diff --git a/Src/Newtonsoft.Json/Linq/JProperty.cs b/Src/Newtonsoft.Json/Linq/JProperty.cs index 845ee2fb..748a3ee3 100644 --- a/Src/Newtonsoft.Json/Linq/JProperty.cs +++ b/Src/Newtonsoft.Json/Linq/JProperty.cs @@ -245,7 +245,7 @@ namespace Newtonsoft.Json.Linq /// /// A that will be read for the content of the . /// A that contains the JSON that was read from the specified . - public static JProperty Load(JsonReader reader) + public static new JProperty Load(JsonReader reader) { if (reader.TokenType == JsonToken.None) { diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 8e63c80b..4e07dc6a 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -1215,6 +1215,32 @@ namespace Newtonsoft.Json.Linq throw new Exception("Error reading JToken from JsonReader. Unexpected token: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); } + /// + /// Load a from a string that contains JSON. + /// + /// A that contains JSON. + /// A populated from the string that contains JSON. + public static JToken Parse(string json) + { + JsonReader jsonReader = new JsonTextReader(new StringReader(json)); + + return Load(jsonReader); + } + + /// + /// Creates a from a . + /// + /// An positioned at the token to read into this . + /// + /// An that contains the token and its descendant tokens + /// that were read from the reader. The runtime type of the token is determined + /// by the token type of the first token encountered in the reader. + /// + public static JToken Load(JsonReader reader) + { + return ReadFrom(reader); + } + internal void SetLineInfo(IJsonLineInfo lineInfo) { if (lineInfo == null || !lineInfo.HasLineInfo()) diff --git a/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs b/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs index c4b6145d..a1b7ddf2 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs @@ -38,16 +38,13 @@ namespace Newtonsoft.Json.Serialization { internal static readonly DefaultSerializationBinder Instance = new DefaultSerializationBinder(); - /// - /// When overridden in a derived class, controls the binding of a serialized object to a type. - /// - /// Specifies the name of the serialized object. - /// Specifies the name of the serialized object. - /// - /// The type of the object the formatter creates a new instance of. - /// - public override Type BindToType(string assemblyName, string typeName) + private readonly ThreadSafeStore _typeCache = new ThreadSafeStore(GetTypeFromTypeNameKey); + + private static Type GetTypeFromTypeNameKey(TypeNameKey typeNameKey) { + string assemblyName = typeNameKey.AssemblyName; + string typeName = typeNameKey.TypeName; + if (assemblyName != null) { Assembly assembly; @@ -76,5 +73,48 @@ namespace Newtonsoft.Json.Serialization return Type.GetType(typeName); } } + + internal struct TypeNameKey : IEquatable + { + internal readonly string AssemblyName; + internal readonly string TypeName; + + public TypeNameKey(string assemblyName, string typeName) + { + AssemblyName = assemblyName; + TypeName = typeName; + } + + public override int GetHashCode() + { + return ((AssemblyName != null) ? AssemblyName.GetHashCode() : 0) ^ ((TypeName != null) ? TypeName.GetHashCode() : 0); + } + + public override bool Equals(object obj) + { + if (!(obj is TypeNameKey)) + return false; + + return Equals((TypeNameKey)obj); + } + + public bool Equals(TypeNameKey other) + { + return (AssemblyName == other.AssemblyName && TypeName == other.TypeName); + } + } + + /// + /// When overridden in a derived class, controls the binding of a serialized object to a type. + /// + /// Specifies the name of the serialized object. + /// Specifies the name of the serialized object. + /// + /// The type of the object the formatter creates a new instance of. + /// + public override Type BindToType(string assemblyName, string typeName) + { + return _typeCache.Get(new TypeNameKey(assemblyName, typeName)); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 0d2b0837..2a8e4949 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -863,8 +863,6 @@ namespace Newtonsoft.Json.Serialization { case JsonToken.PropertyName: string memberName = reader.Value.ToString(); - if (!reader.Read()) - throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); // attempt exact case match first // then try match ignoring case @@ -872,6 +870,9 @@ namespace Newtonsoft.Json.Serialization if (property != null) { + if (!ReadForType(reader, property.PropertyType, property.Converter)) + throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + if (!property.Ignored) propertyValues[property] = CreateValueProperty(reader, property, null, true, null); else @@ -879,6 +880,9 @@ namespace Newtonsoft.Json.Serialization } else { + if (!reader.Read()) + throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + if (Serializer.MissingMemberHandling == MissingMemberHandling.Error) throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name));