From 324c4e4bc59c9fb0c6e9495b40563f054ce64205 Mon Sep 17 00:00:00 2001 From: JamesNK Date: Fri, 1 Jan 2010 23:13:35 +0000 Subject: [PATCH] -JsonTextReader, BsonReader performance -Fix dictionary key encoding when serializing -Fix WrappedDictionary being set to a property when deserializing -Fix Silverlight serialization security error --- .../Converters/IsoDateTimeConverterTests.cs | 6 +- Src/Newtonsoft.Json.Tests/JsonConvertTest.cs | 3 + .../JsonTextReaderTest.cs | 140 ++++- .../JsonValidatingReaderTests.cs | 6 +- Src/Newtonsoft.Json.Tests/PerformanceTests.cs | 385 ++++++++------ .../Serialization/JsonSerializerTest.cs | 80 ++- Src/Newtonsoft.Json/Bson/BsonReader.cs | 60 +-- Src/Newtonsoft.Json/JsonConvert.cs | 101 +++- Src/Newtonsoft.Json/JsonReader.cs | 5 +- Src/Newtonsoft.Json/JsonSerializer.cs | 10 - Src/Newtonsoft.Json/JsonTextReader.cs | 492 +++++++++--------- Src/Newtonsoft.Json/JsonTextWriter.cs | 12 +- Src/Newtonsoft.Json/JsonWriter.cs | 20 +- .../Serialization/DefaultContractResolver.cs | 4 +- .../Serialization/DynamicValueProvider.cs | 16 +- .../Serialization/JsonArrayContract.cs | 2 +- .../Serialization/JsonDictionaryContract.cs | 2 +- .../Serialization/JsonPropertyCollection.cs | 31 +- .../JsonSerializerInternalReader.cs | 20 +- .../JsonSerializerInternalWriter.cs | 6 +- Src/Newtonsoft.Json/Utilities/ConvertUtils.cs | 4 + .../Utilities/JavaScriptUtils.cs | 81 ++- .../Utilities/LateBoundDelegateFactory.cs | 27 +- Src/Newtonsoft.Json/Utilities/StringBuffer.cs | 2 +- Src/Newtonsoft.Json/Utilities/StringUtils.cs | 22 +- 25 files changed, 971 insertions(+), 566 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Converters/IsoDateTimeConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/IsoDateTimeConverterTests.cs index 3b2558a7..092a14e7 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/IsoDateTimeConverterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/IsoDateTimeConverterTests.cs @@ -97,7 +97,11 @@ namespace Newtonsoft.Json.Tests.Converters [Test] public void SerializeCustomFormattedDateTime() { - IsoDateTimeConverter converter = new IsoDateTimeConverter() { DateTimeFormat = "dd/MM/yyyy" }; + IsoDateTimeConverter converter = new IsoDateTimeConverter + { + DateTimeFormat = "dd/MM/yyyy", + Culture = CultureInfo.InvariantCulture + }; string json = @"""09/12/2006"""; diff --git a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs index 3e0982f4..c4078950 100644 --- a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs @@ -152,6 +152,9 @@ now brown cow?", '"', true); result = JavaScriptUtils.ToEscapedJavaScriptString(@"Fred's ""cat"".", '\'', true); Assert.AreEqual(result, @"'Fred\'s ""cat"".'"); + + result = JavaScriptUtils.ToEscapedJavaScriptString("\u001farray\u003caddress"); + Assert.AreEqual(result, @"""\u001farray2010-01-21T11:12:16.0809174+13:00(503) 814-6335fff Street
2009-12-31T11:12:16.0809174+13:00(503) 814-6335array<address
2009-12-30T11:12:16.0809174+13:00(503) 814-6335array 2 address
34123123123.121Rick2010-01-01T12:12:16.0809174+13:00Val & asd11Val2 & asd13Val3 & asd14Markus egger ]><[, (2nd)"; + private const string JsonText = - @"{ - ""strings"": [ - ""Rick"", - ""Markus egger ][, (2nd)"", - ""Kevin McNeish"" - ], - ""dictionary"": { - ""Val asd1"": 1, - ""Val2"": 3, - ""Val3"": 4 - }, - ""Name"": ""Rick"", - ""Now"": ""\/Date(1220867547892+1200)\/"", - ""BigNumber"": 34123123123.121, - ""Address1"": { - ""Address"": ""fff Street"", - ""Phone"": ""(503) 814-6335"", - ""Entered"": ""\/Date(1222588347892+1300)\/"" - }, - ""Addresses"": [ - { - ""Address"": ""array address"", - ""Phone"": ""(503) 814-6335"", - ""Entered"": ""\/Date(1220777547892+1200)\/"" - }, - { - ""Address"": ""array 2 address"", - ""Phone"": ""(503) 814-6335"", - ""Entered"": ""\/Date(1220691147893+1200)\/"" - } - ] -}"; + @"{""strings"":[null,""Markus egger ]><[, (2nd)"",null],""dictionary"":{""Val & asd1"":1,""Val2 & asd1"":3,""Val3 & asd1"":4},""Name"":""Rick"",""Now"":""\/Date(1262301136080+1300)\/"",""BigNumber"":34123123123.121,""Address1"":{""Street"":""fff Street"",""Phone"":""(503) 814-6335"",""Entered"":""\/Date(1264025536080+1300)\/""},""Addresses"":[{""Street"":""\u001farray(SerializeMethod.JsonNet, JsonText); - BenchmarkDeserializeMethod(SerializeMethod.JsonNetBinary, MiscellaneousUtils.HexToBytes(BsonHex)); + BenchmarkDeserializeMethod(SerializeMethod.DataContractSerializer, XmlText); + BenchmarkDeserializeMethod(SerializeMethod.BinaryFormatter, MiscellaneousUtils.HexToBytes(BinaryFormatterHex)); BenchmarkDeserializeMethod(SerializeMethod.JavaScriptSerializer, JsonText); BenchmarkDeserializeMethod(SerializeMethod.DataContractJsonSerializer, JsonText); + BenchmarkDeserializeMethod(SerializeMethod.JsonNet, JsonText); + BenchmarkDeserializeMethod(SerializeMethod.JsonNetBinary, MiscellaneousUtils.HexToBytes(BsonHex)); } [Test] - public void SerializeDataSize() + public void SerializeSizeNormal() + { + SerializeSize(CreateSerializationObject()); + } + + [Test] + public void SerializeSizeData() { Image image = new Image(); image.Data = System.IO.File.ReadAllBytes(@"..\..\bunny_pancake.jpg"); image.FileName = "bunny_pancake.jpg"; image.Title = "I have no idea what you are talking about so here's a bunny with a pancake on its head"; - string json = JsonConvert.SerializeObject(image, Formatting.None); + SerializeSize(image); + } + + private void SerializeSize(object value) + { + string json = JsonConvert.SerializeObject(value, Formatting.None); byte[] jsonBytes = Encoding.UTF8.GetBytes(json); MemoryStream ms = new MemoryStream(); JsonSerializer serializer = new JsonSerializer(); BsonWriter writer = new BsonWriter(ms); - serializer.Serialize(writer, image); + serializer.Serialize(writer, value); writer.Flush(); byte[] bsonBytes = ms.ToArray(); - Console.WriteLine("Image size: {0} bytes", image.Data.Length); + DataContractSerializer dataContractSerializer = new DataContractSerializer(value.GetType()); + + ms = new MemoryStream(); + dataContractSerializer.WriteObject(ms, value); + + byte[] xmlBytes = ms.ToArray(); + + ms = new MemoryStream(); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(ms, value); + + byte[] binaryFormatterBytes = ms.ToArray(); + + Console.WriteLine("BinaryFormatter size: {0} bytes", binaryFormatterBytes.Length); Console.WriteLine("JSON size: {0} bytes", jsonBytes.Length); Console.WriteLine("BSON size: {0} bytes", bsonBytes.Length); + Console.WriteLine("XML size: {0} bytes", xmlBytes.Length); + } + + #region Serialize + private static readonly byte[] Buffer = new byte[4096]; + + public void BenchmarkSerializeMethod(SerializeMethod method, object value) + { + Serialize(method, value); + + Stopwatch timed = new Stopwatch(); + timed.Start(); + + string json = null; + for (int x = 0; x < Iterations; x++) + { + json = Serialize(method, value); + } + + timed.Stop(); + + Console.WriteLine("Serialize method: {0}", method); + Console.WriteLine("{0} ms", timed.ElapsedMilliseconds); + Console.WriteLine(json); + Console.WriteLine(); } private TestClass CreateSerializationObject() { TestClass test = new TestClass(); - test.Address1.Address = "fff Street"; + test.dictionary = new Dictionary { { "Val & asd1", 1 }, { "Val2 & asd1", 3 }, { "Val3 & asd1", 4 } }; + + + test.Address1.Street = "fff Street"; test.Address1.Entered = DateTime.Now.AddDays(20); test.BigNumber = 34123123123.121M; test.Now = DateTime.Now.AddHours(1); - test.strings = new List() { "Rick", "Markus egger ][, (2nd)", "Kevin McNeish" }; + test.strings = new List() { null, "Markus egger ]><[, (2nd)", null }; - cAddress address = new cAddress(); + Address address = new Address(); address.Entered = DateTime.Now.AddDays(-1); - address.Address = "array address"; + address.Street = "\u001farray\u003caddress"; test.Addresses.Add(address); - address = new cAddress(); + address = new Address(); address.Entered = DateTime.Now.AddDays(-2); - address.Address = "array 2 address"; + address.Street = "array 2 address"; test.Addresses.Add(address); return test; } @@ -169,36 +210,6 @@ namespace Newtonsoft.Json.Tests return output; } - public object DeserializeJsonNet(string json) - { - Type type = typeof(T); - - JsonSerializer serializer = new JsonSerializer(); - - serializer.NullValueHandling = NullValueHandling.Ignore; - - serializer.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace; - serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore; - serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - - return serializer.Deserialize(new StringReader(json), type); - } - - public object DeserializeJsonNetBinary(byte[] bson) - { - Type type = typeof(T); - - JsonSerializer serializer = new JsonSerializer(); - - serializer.NullValueHandling = NullValueHandling.Ignore; - - serializer.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace; - serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore; - serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - - return serializer.Deserialize(new BsonReader(new MemoryStream(bson)), type); - } - public string SerializeWebExtensions(object value) { JavaScriptSerializer ser = new JavaScriptSerializer(); @@ -206,18 +217,11 @@ namespace Newtonsoft.Json.Tests return ser.Serialize(value); } - public object DeserializeWebExtensions(string json) - { - JavaScriptSerializer ser = new JavaScriptSerializer(); - - return ser.Deserialize(json); - } - - public string SerializeDataContract(object value) + public string SerializeDataContractJson(object value) { DataContractJsonSerializer dataContractSerializer = new DataContractJsonSerializer(value.GetType()); - + MemoryStream ms = new MemoryStream(); dataContractSerializer.WriteObject(ms, value); @@ -229,18 +233,22 @@ namespace Newtonsoft.Json.Tests } } - public object DeserializeDataContract(string json) + public string SerializeDataContract(object value) { - DataContractJsonSerializer dataContractSerializer - = new DataContractJsonSerializer(typeof(T)); + DataContractSerializer dataContractSerializer + = new DataContractSerializer(value.GetType()); - MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream ms = new MemoryStream(); + dataContractSerializer.WriteObject(ms, value); - return dataContractSerializer.ReadObject(ms); + ms.Seek(0, SeekOrigin.Begin); + + using (StreamReader sr = new StreamReader(ms)) + { + return sr.ReadToEnd(); + } } - private static readonly byte[] Buffer = new byte[4096]; - private string Serialize(SerializeMethod method, object value) { string json; @@ -251,19 +259,27 @@ namespace Newtonsoft.Json.Tests json = JsonConvert.SerializeObject(value); break; case SerializeMethod.JsonNetBinary: - MemoryStream ms = new MemoryStream(Buffer); - JsonSerializer serializer = new JsonSerializer(); - BsonWriter writer = new BsonWriter(ms); - serializer.Serialize(writer, value); + { + MemoryStream ms = new MemoryStream(Buffer); + JsonSerializer serializer = new JsonSerializer(); + BsonWriter writer = new BsonWriter(ms); + serializer.Serialize(writer, value); - json = "Bytes = " + ms.Length; - break; + json = "Bytes = " + ms.Position; + break; + } case SerializeMethod.JavaScriptSerializer: json = SerializeWebExtensions(value); break; case SerializeMethod.DataContractJsonSerializer: + json = SerializeDataContractJson(value); + break; + case SerializeMethod.DataContractSerializer: json = SerializeDataContract(value); break; + case SerializeMethod.BinaryFormatter: + json = SerializeBinaryFormatter(value); + break; default: throw new ArgumentOutOfRangeException("method"); } @@ -271,33 +287,28 @@ namespace Newtonsoft.Json.Tests return json; } - public void BenchmarkSerializeMethod(SerializeMethod method, object value) + private string SerializeBinaryFormatter(object value) { - Serialize(method, value); + string json; + MemoryStream ms = new MemoryStream(Buffer); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(ms, value); - Stopwatch timed = new Stopwatch(); - timed.Start(); - - string json = null; - for (int x = 0; x < Iterations; x++) - { - json = Serialize(method, value); - } - - timed.Stop(); - - Console.WriteLine("Serialize method: {0}", method); - Console.WriteLine("{0} ms", timed.ElapsedMilliseconds); - Console.WriteLine(json); - Console.WriteLine(); + json = "Bytes = " + ms.Position; + //json = BitConverter.ToString(ms.ToArray()); + return json; } + #endregion + #region Deserialize public void BenchmarkDeserializeMethod(SerializeMethod method, object json) { + Deserialize(method, json); + Stopwatch timed = new Stopwatch(); timed.Start(); - object value = null; + T value = default(T); for (int x = 0; x < Iterations; x++) { value = Deserialize(method, json); @@ -311,35 +322,97 @@ namespace Newtonsoft.Json.Tests Console.WriteLine(); } - private object Deserialize(SerializeMethod method, object json) + public T DeserializeJsonNet(string json) { - object value; + Type type = typeof(T); + JsonSerializer serializer = new JsonSerializer(); + serializer.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace; + serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore; + serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + return (T)serializer.Deserialize(new StringReader(json), type); + + //JsonTextReader reader = new JsonTextReader(new StringReader(JsonText)); + //while (reader.Read()) + //{ + + //} + //return default(T); + } + + public T DeserializeJsonNetBinary(byte[] bson) + { + Type type = typeof(T); + + JsonSerializer serializer = new JsonSerializer(); + serializer.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace; + serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore; + serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + return (T)serializer.Deserialize(new BsonReader(new MemoryStream(bson)), type); + } + + public T DeserializeWebExtensions(string json) + { + JavaScriptSerializer ser = new JavaScriptSerializer(); + + return ser.Deserialize(json); + } + + public T DeserializeDataContractJson(string json) + { + DataContractJsonSerializer dataContractSerializer + = new DataContractJsonSerializer(typeof(T)); + + MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + return (T)dataContractSerializer.ReadObject(ms); + } + + private T Deserialize(SerializeMethod method, object json) + { switch (method) { case SerializeMethod.JsonNet: - value = DeserializeJsonNet((string) json); - break; + return DeserializeJsonNet((string) json); case SerializeMethod.JsonNetBinary: - value = DeserializeJsonNetBinary((byte[]) json); - break; + return DeserializeJsonNetBinary((byte[]) json); + case SerializeMethod.BinaryFormatter: + return DeserializeBinaryFormatter((byte[]) json); case SerializeMethod.JavaScriptSerializer: - value = DeserializeWebExtensions((string) json); - break; + return DeserializeWebExtensions((string) json); + case SerializeMethod.DataContractSerializer: + return DeserializeDataContract((string) json); case SerializeMethod.DataContractJsonSerializer: - value = DeserializeDataContract((string) json); - break; + return DeserializeDataContractJson((string) json); default: throw new ArgumentOutOfRangeException("method"); } - return value; } + + private T DeserializeDataContract(string xml) + { + MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + + DataContractSerializer serializer = new DataContractSerializer(typeof (T)); + return (T) serializer.ReadObject(ms); + } + + private T DeserializeBinaryFormatter(byte[] bytes) + { + BinaryFormatter formatter = new BinaryFormatter(); + return (T)formatter.Deserialize(new MemoryStream(bytes)); + } + #endregion } - + #region Classes + [Serializable] + [DataContract] public class TestClass { - + [DataMember] public string Name { get { return _Name; } @@ -347,7 +420,7 @@ namespace Newtonsoft.Json.Tests } private string _Name = "Rick"; - + [DataMember] public DateTime Now { get { return _Now; } @@ -355,7 +428,7 @@ namespace Newtonsoft.Json.Tests } private DateTime _Now = DateTime.Now; - + [DataMember] public decimal BigNumber { get { return _BigNumber; } @@ -363,42 +436,44 @@ namespace Newtonsoft.Json.Tests } private decimal _BigNumber = 1212121.22M; - - public cAddress Address1 + [DataMember] + public Address Address1 { get { return _Address1; } set { _Address1 = value; } } - private cAddress _Address1 = new cAddress(); + private Address _Address1 = new Address(); - - public List Addresses + [DataMember] + public List
Addresses { get { return _Addresses; } set { _Addresses = value; } } - private List _Addresses = new List(); + private List
_Addresses = new List
(); + [DataMember] + public List strings = new List(); - public List strings = new List() { "Rick", "Markus", "Kevin" }; - - - public Dictionary dictionary = new Dictionary { { "Val asd1", 1 }, { "Val2", 3 }, { "Val3", 4 } }; + [DataMember] + public Dictionary dictionary = new Dictionary(); } - public class cAddress + [Serializable] + [DataContract] + public class Address { - - public string Address + [DataMember] + public string Street { - get { return _Address; } - set { _Address = value; } + get { return _street; } + set { _street = value; } } - private string _Address = "32 Kaiea"; - + private string _street = "32 Kaiea"; + [DataMember] public string Phone { get { return _Phone; } @@ -406,14 +481,14 @@ namespace Newtonsoft.Json.Tests } private string _Phone = "(503) 814-6335"; - + [DataMember] public DateTime Entered { get { return _Entered; } set { _Entered = value; } } private DateTime _Entered = DateTime.Parse("01/01/2007", CultureInfo.CurrentCulture.DateTimeFormat); - } + #endregion } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 8e03b585..0d315837 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; #if !SILVERLIGHT && !PocketPC && !NET20 using System.ComponentModel.DataAnnotations; +using System.Web.Script.Serialization; #endif using System.Text; using NUnit.Framework; @@ -131,12 +132,21 @@ namespace Newtonsoft.Json.Tests.Serialization [Test] public void DeserializeJavaScriptDate() { - DateTime dateValue = new DateTime(2000, 3, 30); + DateTime dateValue = new DateTime(2010, 3, 30); Dictionary testDictionary = new Dictionary(); testDictionary["date"] = dateValue; string jsonText = JsonConvert.SerializeObject(testDictionary); +#if !PocketPC && !NET20 + MemoryStream ms = new MemoryStream(); + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Dictionary)); + serializer.WriteObject(ms, testDictionary); + + byte[] data = ms.ToArray(); + string output = Encoding.UTF8.GetString(data, 0, data.Length); +#endif + Dictionary deserializedDictionary = (Dictionary)JsonConvert.DeserializeObject(jsonText, typeof(Dictionary)); DateTime deserializedDate = (DateTime)deserializedDictionary["date"]; @@ -403,6 +413,37 @@ keyword such as type of business."" Assert.AreEqual(dataContractResult[0], jsonNetResult[0]); } + [Test] + public void BackslashEqivilence() + { + string json = @"[""vvv\/vvv\tvvv\""vvv\bvvv\nvvv\rvvv\\vvv\fvvv""]"; + +#if !SILVERLIGHT + JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); + List javaScriptSerializerResult = javaScriptSerializer.Deserialize>(json); +#endif + + DataContractJsonSerializer s = new DataContractJsonSerializer(typeof(List)); + List dataContractResult = (List)s.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(json))); + + List jsonNetResult = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(1, jsonNetResult.Count); + Assert.AreEqual(dataContractResult[0], jsonNetResult[0]); +#if !SILVERLIGHT + Assert.AreEqual(javaScriptSerializerResult[0], jsonNetResult[0]); +#endif + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = @"Bad JSON escape sequence: \j. Line 1, position 7.")] + public void InvalidBackslash() + { + string json = @"[""vvv\jvvv""]"; + + JsonConvert.DeserializeObject>(json); + } + [Test] public void DateTimeTest() { @@ -1068,9 +1109,6 @@ keyword such as type of business."" ] }"; - //JavaScriptSerializer serializer = new JavaScriptSerializer(); - //GoogleMapGeocoderStructure jsonGoogleMapGeocoder = serializer.Deserialize(json); - GoogleMapGeocoderStructure jsonGoogleMapGeocoder = JsonConvert.DeserializeObject(json); } @@ -2051,5 +2089,39 @@ keyword such as type of business."" PersonPropertyClass newPersonPropertyClass = JsonConvert.DeserializeObject(json); Assert.AreEqual(wagePerson.HourlyWage, ((WagePerson) newPersonPropertyClass.Person).HourlyWage); } + + public class ExistingValueClass + { + public Dictionary Dictionary { get; set; } + public List List { get; set; } + + public ExistingValueClass() + { + Dictionary = new Dictionary + { + {"existing", "yup"} + }; + List = new List + { + "existing" + }; + } + } + + [Test] + public void DeserializePopulateDictionaryAndList() + { + ExistingValueClass d = JsonConvert.DeserializeObject(@"{'Dictionary':{appended:'appended',existing:'new'}}"); + + Assert.IsNotNull(d); + Assert.IsNotNull(d.Dictionary); + Assert.AreEqual(typeof(Dictionary), d.Dictionary.GetType()); + Assert.AreEqual(typeof(List), d.List.GetType()); + Assert.AreEqual(2, d.Dictionary.Count); + Assert.AreEqual("new", d.Dictionary["existing"]); + Assert.AreEqual("appended", d.Dictionary["appended"]); + Assert.AreEqual(1, d.List.Count); + Assert.AreEqual("existing", d.List[0]); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Bson/BsonReader.cs b/Src/Newtonsoft.Json/Bson/BsonReader.cs index da3525df..7d5ebbe5 100644 --- a/Src/Newtonsoft.Json/Bson/BsonReader.cs +++ b/Src/Newtonsoft.Json/Bson/BsonReader.cs @@ -46,6 +46,8 @@ namespace Newtonsoft.Json.Bson private char[] _charBuffer; private BsonType _currentElementType; private BsonReaderState _bsonReaderState; + private ContainerContext _currentContext; + private const int MaxCharBytesSize = 128; private enum BsonReaderState @@ -63,20 +65,9 @@ namespace Newtonsoft.Json.Bson private class ContainerContext { - public JTokenType Type { get; private set; } - public int Length { get; set; } - private int _position; - public int Position - { - get { return _position; } - set - { - if (value > Length && Length != 0) - throw new JsonReaderException("Read past end of current container context."); - - _position = value; - } - } + public readonly JTokenType Type; + public int Length; + public int Position; public ContainerContext(JTokenType type) { @@ -188,7 +179,7 @@ namespace Newtonsoft.Json.Bson _bsonReaderState = BsonReaderState.CodeWScopeScopeObject; ContainerContext newContext = new ContainerContext(JTokenType.Object); - _stack.Add(newContext); + PushContext(newContext); newContext.Length = ReadInt32(); return true; @@ -270,7 +261,7 @@ namespace Newtonsoft.Json.Bson SetToken(token); ContainerContext newContext = new ContainerContext(type); - _stack.Add(newContext); + PushContext(newContext); newContext.Length = ReadInt32(); return true; } @@ -292,7 +283,7 @@ namespace Newtonsoft.Json.Bson ReadType(_currentElementType); return true; case State.PostValue: - ContainerContext context = GetCurrentContext(); + ContainerContext context = _currentContext; if (context == null) return false; @@ -317,8 +308,8 @@ namespace Newtonsoft.Json.Bson if (ReadByte() != 0) throw new JsonReaderException("Unexpected end of object byte value."); - _stack.RemoveAt(_stack.Count - 1); - if (_stack.Count != 0) + PopContext(); + if (_currentContext != null) MovePosition(context.Length); JsonToken endToken = (context.Type == JTokenType.Object) ? JsonToken.EndObject : JsonToken.EndArray; @@ -344,6 +335,21 @@ namespace Newtonsoft.Json.Bson return false; } + private void PopContext() + { + _stack.RemoveAt(_stack.Count - 1); + if (_stack.Count == 0) + _currentContext = null; + else + _currentContext = _stack[_stack.Count - 1]; + } + + private void PushContext(ContainerContext newContext) + { + _stack.Add(newContext); + _currentContext = newContext; + } + private byte ReadByte() { MovePosition(1); @@ -366,7 +372,7 @@ namespace Newtonsoft.Json.Bson SetToken(JsonToken.StartObject); ContainerContext newContext = new ContainerContext(JTokenType.Object); - _stack.Add(newContext); + PushContext(newContext); newContext.Length = ReadInt32(); break; } @@ -375,7 +381,7 @@ namespace Newtonsoft.Json.Bson SetToken(JsonToken.StartArray); ContainerContext newContext = new ContainerContext(JTokenType.Array); - _stack.Add(newContext); + PushContext(newContext); newContext.Length = ReadInt32(); break; } @@ -561,19 +567,9 @@ namespace Newtonsoft.Json.Bson return (BsonType)_reader.ReadSByte(); } - private ContainerContext GetCurrentContext() - { - int count = _stack.Count; - if (count == 0) - return null; - - return _stack[count - 1]; - } - private void MovePosition(int count) { - ContainerContext context = GetCurrentContext(); - context.Position += count; + _currentContext.Position += count; } private byte[] ReadBytes(int count) diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 0041749d..835dab90 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -29,6 +29,7 @@ using System.Globalization; using Newtonsoft.Json.Utilities; using System.Xml; using Newtonsoft.Json.Converters; +using System.Text; namespace Newtonsoft.Json { @@ -96,14 +97,11 @@ namespace Newtonsoft.Json /// A JSON string representation of the . public static string ToString(DateTime value) { - TimeSpan utcOffset; -#if !PocketPC && !NET20 - utcOffset = TimeZoneInfo.Local.GetUtcOffset(value); -#else - utcOffset = TimeZone.CurrentTimeZone.GetUtcOffset(value); -#endif - - return ToStringInternal(value, utcOffset, value.Kind); + using (StringWriter writer = StringUtils.CreateStringWriter(64)) + { + WriteDateTimeString(writer, value, GetUtcOffset(value), value.Kind); + return writer.ToString(); + } } #if !PocketPC && !NET20 @@ -114,33 +112,95 @@ namespace Newtonsoft.Json /// A JSON string representation of the . public static string ToString(DateTimeOffset value) { - return ToStringInternal(value.UtcDateTime, value.Offset, DateTimeKind.Local); + using (StringWriter writer = StringUtils.CreateStringWriter(64)) + { + WriteDateTimeString(writer, value.UtcDateTime, value.Offset, DateTimeKind.Local); + return writer.ToString(); + } } #endif - internal static string ToStringInternal(DateTime value, TimeSpan offset, DateTimeKind kind) + private static TimeSpan GetUtcOffset(DateTime dateTime) { - long javaScriptTicks = ConvertDateTimeToJavaScriptTicks(value); +#if SILVERLIGHT + return TimeZoneInfo.Local.GetUtcOffset(dateTime); +#else + return TimeZone.CurrentTimeZone.GetUtcOffset(dateTime); +#endif + } - string timezoneOffset; + internal static void WriteDateTimeString(TextWriter writer, DateTime value) + { + WriteDateTimeString(writer, value, GetUtcOffset(value), value.Kind); + } + + internal static void WriteDateTimeString(TextWriter writer, DateTime value, TimeSpan offset, DateTimeKind kind) + { + long javaScriptTicks = ConvertDateTimeToJavaScriptTicks(value, offset); + + writer.Write(@"""\/Date("); + writer.Write(javaScriptTicks); + switch (kind) { case DateTimeKind.Local: case DateTimeKind.Unspecified: - timezoneOffset = offset.Hours.ToString("+00;-00", CultureInfo.InvariantCulture) + offset.Minutes.ToString("00;00", CultureInfo.InvariantCulture); - break; - default: - timezoneOffset = string.Empty; + writer.Write((offset.Ticks >= 0L) ? "+" : "-"); + + int absHours = Math.Abs(offset.Hours); + if (absHours < 10) + writer.Write(0); + writer.Write(absHours); + int absMinutes = Math.Abs(offset.Minutes); + if (absMinutes < 10) + writer.Write(0); + writer.Write(absMinutes); break; } - return @"""\/Date(" + javaScriptTicks.ToString(CultureInfo.InvariantCulture) + timezoneOffset + @")\/"""; + + writer.Write(@")\/"""); + } + + private static long ToUniversalTicks(DateTime dateTime) + { + if (dateTime.Kind == DateTimeKind.Utc) + return dateTime.Ticks; + + return ToUniversalTicks(dateTime, GetUtcOffset(dateTime)); + } + + private static long ToUniversalTicks(DateTime dateTime, TimeSpan offset) + { + if (dateTime.Kind == DateTimeKind.Utc) + return dateTime.Ticks; + + long ticks = dateTime.Ticks - offset.Ticks; + if (ticks > 3155378975999999999L) + return 3155378975999999999L; + + if (ticks < 0L) + return 0L; + + return ticks; + } + + internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime, TimeSpan offset) + { + long universialTicks = ToUniversalTicks(dateTime, offset); + + return UniversialTicksToJavaScriptTicks(universialTicks); } internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime) { - DateTime utcDateTime = dateTime.ToUniversalTime(); + long universialTicks = ToUniversalTicks(dateTime); - long javaScriptTicks = (utcDateTime.Ticks - InitialJavaScriptDateTicks) / 10000; + return UniversialTicksToJavaScriptTicks(universialTicks); + } + + private static long UniversialTicksToJavaScriptTicks(long universialTicks) + { + long javaScriptTicks = (universialTicks - InitialJavaScriptDateTicks) / 10000; return javaScriptTicks; } @@ -524,7 +584,8 @@ namespace Newtonsoft.Json { JsonSerializer jsonSerializer = JsonSerializer.Create(settings); - StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); + StringBuilder sb = new StringBuilder(128); + StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) { jsonWriter.Formatting = formatting; diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index c6a3b762..97e1d447 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -102,6 +102,7 @@ namespace Newtonsoft.Json private Type _valueType; private char _quoteChar; private State _currentState; + private JTokenType _currentTypeContext; /// /// Gets the current reader state. @@ -182,6 +183,7 @@ namespace Newtonsoft.Json { _stack.Add(value); _top++; + _currentTypeContext = value; } private JTokenType Pop() @@ -189,13 +191,14 @@ namespace Newtonsoft.Json JTokenType value = Peek(); _stack.RemoveAt(_stack.Count - 1); _top--; + _currentTypeContext = _stack[_top - 1]; return value; } private JTokenType Peek() { - return _stack[_top - 1]; + return _currentTypeContext; } /// diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 9f5ba7c0..58255ee5 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -418,16 +418,6 @@ namespace Newtonsoft.Json serializerWriter.Serialize(jsonWriter, value); } - internal bool HasClassConverter(JsonContract contract, out JsonConverter converter) - { - if (contract == null) - throw new ArgumentNullException("contract"); - - converter = contract.Converter; - - return (converter != null); - } - internal bool HasMatchingConverter(Type type, out JsonConverter matchingConverter) { if (HasMatchingConverter(_converters, type, out matchingConverter)) diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index 373049d5..74b2f293 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -40,10 +40,10 @@ namespace Newtonsoft.Json { private readonly TextReader _reader; private readonly StringBuffer _buffer; - private char _currentChar; + private char? _lastChar; private int _currentLinePosition; private int _currentLineNumber; - private bool _currentCharCarriageReturn; + private bool _end; /// /// Initializes a new instance of the class with the specified . @@ -61,7 +61,7 @@ namespace Newtonsoft.Json private void ParseString(char quote) { - ReadStringIntoBuffer(quote, null); + ReadStringIntoBuffer(quote); string text = _buffer.ToString(); _buffer.Position = 0; @@ -77,23 +77,24 @@ namespace Newtonsoft.Json } } - private void ReadStringIntoBuffer(char quote, int? untilPosition) + private void ReadStringIntoBuffer(char quote) { - bool stringTerminated = false; - bool hexNumber = false; - int hexCount = 0; - - while (!stringTerminated && (untilPosition == null || _buffer.Position < untilPosition) && MoveNext()) + while (true) { - if (hexNumber) - hexCount++; + char currentChar = MoveNext(); - switch (_currentChar) + switch (currentChar) { + case '\0': + if (_end) + throw CreateJsonReaderException("Unterminated string. Expected delimiter: {0}. Line {1}, position {2}.", quote, _currentLineNumber, _currentLinePosition); + + _buffer.Append('\0'); + break; case '\\': - if (MoveNext()) + if ((currentChar = MoveNext()) != '\0' || !_end) { - switch (_currentChar) + switch (currentChar) { case 'b': _buffer.Append('\b'); @@ -110,13 +111,29 @@ namespace Newtonsoft.Json case 'r': _buffer.Append('\r'); break; + case '\\': + _buffer.Append('\\'); + break; + case '"': + case '\'': + case '/': + _buffer.Append(currentChar); + break; case 'u': - // note the start of a hex character - hexNumber = true; + char[] hexValues = new char[4]; + for (int i = 0; i < hexValues.Length; i++) + { + if ((currentChar = MoveNext()) != '\0' || !_end) + hexValues[i] = currentChar; + else + throw CreateJsonReaderException("Unexpected end while parsing unicode character. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + + char hexChar = Convert.ToChar(int.Parse(new string(hexValues), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo)); + _buffer.Append(hexChar); break; default: - _buffer.Append(_currentChar); - break; + throw CreateJsonReaderException("Bad JSON escape sequence: {0}. Line {1}, position {2}.", @"\" + currentChar, _currentLineNumber, _currentLinePosition); } } else @@ -126,34 +143,20 @@ namespace Newtonsoft.Json break; case '"': case '\'': - if (_currentChar == quote) - stringTerminated = true; + if (currentChar == quote) + { + return; + } else - goto default; + { + _buffer.Append(currentChar); + } break; default: - _buffer.Append(_currentChar); + _buffer.Append(currentChar); break; } - - if (hexCount == 4) - { - // remove hex characters from buffer, convert to char and then add - string hexString = _buffer.ToString(_buffer.Position - 4, 4); - char hexChar = Convert.ToChar(int.Parse(hexString, NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo)); - - _buffer.Position = _buffer.Position - 4; - _buffer.Append(hexChar); - - hexNumber = false; - hexCount = 0; - } } - - if (!stringTerminated) - throw CreateJsonReaderException("Unterminated string. Expected delimiter: {0}. Line {1}, position {2}.", quote, _currentLineNumber, _currentLinePosition); - - ClearCurrentChar(); } private JsonReaderException CreateJsonReaderException(string format, params object[] args) @@ -162,41 +165,6 @@ namespace Newtonsoft.Json return new JsonReaderException(message, null, _currentLineNumber, _currentLinePosition); } - /// - /// Sets the current token and value. - /// - /// The new token. - /// The value. - protected override void SetToken(JsonToken newToken, object value) - { - base.SetToken(newToken, value); - - switch (newToken) - { - case JsonToken.StartObject: - ClearCurrentChar(); - break; - case JsonToken.StartArray: - ClearCurrentChar(); - break; - case JsonToken.StartConstructor: - ClearCurrentChar(); - break; - case JsonToken.EndObject: - ClearCurrentChar(); - break; - case JsonToken.EndArray: - ClearCurrentChar(); - break; - case JsonToken.EndConstructor: - ClearCurrentChar(); - break; - case JsonToken.PropertyName: - ClearCurrentChar(); - break; - } - } - private void ParseDate(string text) { string value = text.Substring(6, text.Length - 8); @@ -236,34 +204,32 @@ namespace Newtonsoft.Json private const int LineFeedValue = StringUtils.LineFeed; private const int CarriageReturnValue = StringUtils.CarriageReturn; - private bool MoveNext() + private char MoveNext() { int value = _reader.Read(); switch (value) { case -1: - return false; + _end = true; + return '\0'; case CarriageReturnValue: + if (_reader.Peek() == LineFeedValue) + _reader.Read(); + _currentLineNumber++; _currentLinePosition = 0; - _currentCharCarriageReturn = true; break; case LineFeedValue: - if (!_currentCharCarriageReturn) - _currentLineNumber++; - + _currentLineNumber++; _currentLinePosition = 0; - _currentCharCarriageReturn = false; break; default: _currentLinePosition++; - _currentCharCarriageReturn = false; break; } - _currentChar = (char)value; - return true; + return (char)value; } private bool HasNext() @@ -271,14 +237,9 @@ namespace Newtonsoft.Json return (_reader.Peek() != -1); } - private char PeekNext() + private int PeekNext() { - return (char)_reader.Peek(); - } - - private void ClearCurrentChar() - { - _currentChar = '\0'; + return _reader.Peek(); } /// @@ -291,11 +252,19 @@ namespace Newtonsoft.Json { while (true) { - if (_currentChar == '\0') + char currentChar; + if (_lastChar != null) { - if (!MoveNext()) - return false; + currentChar = _lastChar.Value; + _lastChar = null; } + else + { + currentChar = MoveNext(); + } + + if (currentChar == '\0' && _end) + return false; switch (CurrentState) { @@ -305,16 +274,16 @@ namespace Newtonsoft.Json case State.ArrayStart: case State.Constructor: case State.ConstructorStart: - return ParseValue(); + return ParseValue(currentChar); case State.Complete: break; case State.Object: case State.ObjectStart: - return ParseObject(); + return ParseObject(currentChar); case State.PostValue: // returns true if it hits // end of object or array - if (ParsePostValue()) + if (ParsePostValue(currentChar)) return true; break; case State.Closed: @@ -337,14 +306,23 @@ namespace Newtonsoft.Json { while (true) { - if (_currentChar == '\0') + char currentChar; + if (_lastChar != null) { - if (!MoveNext()) - throw CreateJsonReaderException("Unexpected end: Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + currentChar = _lastChar.Value; + _lastChar = null; } + else + { + currentChar = MoveNext(); + } + + if (currentChar == '\0' && _end) + throw CreateJsonReaderException("Unexpected end when reading bytes: Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); switch (CurrentState) { + case State.PostValue: case State.Start: case State.Property: case State.Array: @@ -353,11 +331,11 @@ namespace Newtonsoft.Json case State.ConstructorStart: do { - switch (_currentChar) + switch (currentChar) { case '"': case '\'': - ReadStringIntoBuffer(_currentChar, null); + ReadStringIntoBuffer(currentChar); byte[] data = Convert.FromBase64CharArray(_buffer.GetInternalBuffer(), 0, _buffer.Position); _buffer.Position = 0; @@ -368,19 +346,27 @@ namespace Newtonsoft.Json case 'n': ParseNull(); return null; + case ' ': + case StringUtils.Tab: + case StringUtils.LineFeed: + case StringUtils.CarriageReturn: + // eat + break; + case ',': + SetStateBasedOnCurrent(); + break; default: - if (char.IsWhiteSpace(_currentChar)) + if (char.IsWhiteSpace(currentChar)) { // eat } else { - throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); + throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); } break; } - } while (MoveNext()); - ParseValue(); + } while ((currentChar = MoveNext()) != '\0' || !_end); break; default: throw CreateJsonReaderException("Unexpected state: {0}. Line {1}, position {2}.", CurrentState, _currentLineNumber, _currentLinePosition); @@ -388,115 +374,114 @@ namespace Newtonsoft.Json } } - private bool ParsePostValue() + private bool ParsePostValue(char currentChar) { do { - switch (_currentChar) + switch (currentChar) { case '}': SetToken(JsonToken.EndObject); - ClearCurrentChar(); return true; case ']': SetToken(JsonToken.EndArray); - ClearCurrentChar(); return true; case ')': SetToken(JsonToken.EndConstructor); - ClearCurrentChar(); return true; case '/': ParseComment(); return true; case ',': - // finished paring + // finished parsing SetStateBasedOnCurrent(); - ClearCurrentChar(); return false; case ' ': - // eat whitespace - ClearCurrentChar(); - break; - default: - if (char.IsWhiteSpace(_currentChar)) - { - // eat whitespace - ClearCurrentChar(); - } - else - { - throw CreateJsonReaderException("After parsing a value an unexpected character was encoutered: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); - } - break; - } - } while (MoveNext()); - - return false; - } - - private bool ParseObject() - { - do - { - switch (_currentChar) - { - case '}': - SetToken(JsonToken.EndObject); - return true; - case '/': - ParseComment(); - return true; - case ',': - SetToken(JsonToken.Undefined); - return true; - case ' ': + case StringUtils.Tab: + case StringUtils.LineFeed: + case StringUtils.CarriageReturn: // eat break; default: - if (char.IsWhiteSpace(_currentChar)) + if (char.IsWhiteSpace(currentChar)) { // eat } else { - return ParseProperty(); + throw CreateJsonReaderException("After parsing a value an unexpected character was encoutered: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); } break; } - } while (MoveNext()); + } while ((currentChar = MoveNext()) != '\0' || !_end); return false; } - private bool ParseProperty() + private bool ParseObject(char currentChar) { + do + { + switch (currentChar) + { + case '}': + SetToken(JsonToken.EndObject); + return true; + case '/': + ParseComment(); + return true; + case ' ': + case StringUtils.Tab: + case StringUtils.LineFeed: + case StringUtils.CarriageReturn: + // eat + break; + default: + if (char.IsWhiteSpace(currentChar)) + { + // eat + } + else + { + return ParseProperty(currentChar); + } + break; + } + } while ((currentChar = MoveNext()) != '\0' || !_end); + + return false; + } + + private bool ParseProperty(char firstChar) + { + char currentChar = firstChar; char quoteChar; - if (ValidIdentifierChar(_currentChar)) + if (ValidIdentifierChar(currentChar)) { quoteChar = '\0'; - ParseUnquotedProperty(); + currentChar = ParseUnquotedProperty(currentChar); } - else if (_currentChar == '"' || _currentChar == '\'') + else if (currentChar == '"' || currentChar == '\'') { - quoteChar = _currentChar; - ParseQuotedProperty(_currentChar); + quoteChar = currentChar; + ReadStringIntoBuffer(quoteChar); + currentChar = MoveNext(); } else { - throw CreateJsonReaderException("Invalid property identifier character: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); + throw CreateJsonReaderException("Invalid property identifier character: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); } - if (_currentChar != ':') + if (currentChar != ':') { - MoveNext(); + currentChar = MoveNext(); // finished property. skip any whitespace and move to colon - EatWhitespace(false); + EatWhitespace(currentChar, false, out currentChar); - if (_currentChar != ':') - throw CreateJsonReaderException("Invalid character after parsing property name. Expected ':' but got: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); + if (currentChar != ':') + throw CreateJsonReaderException("Invalid character after parsing property name. Expected ':' but got: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); } SetToken(JsonToken.PropertyName, _buffer.ToString()); @@ -506,60 +491,46 @@ namespace Newtonsoft.Json return true; } - private void ParseQuotedProperty(char quoteChar) - { - // parse property name until quoted char is hit - while (MoveNext()) - { - if (_currentChar == quoteChar) - { - return; - } - else - { - _buffer.Append(_currentChar); - } - } - - throw CreateJsonReaderException("Unclosed quoted property. Expected: {0}. Line {1}, position {2}.", quoteChar, _currentLineNumber, _currentLinePosition); - } - private bool ValidIdentifierChar(char value) { - return (char.IsLetterOrDigit(_currentChar) || _currentChar == '_' || _currentChar == '$'); + return (char.IsLetterOrDigit(value) || value == '_' || value == '$'); } - private void ParseUnquotedProperty() + private char ParseUnquotedProperty(char firstChar) { // parse unquoted property name until whitespace or colon - _buffer.Append(_currentChar); + _buffer.Append(firstChar); - while (MoveNext()) + char currentChar; + + while ((currentChar = MoveNext()) != '\0' || !_end) { - if (char.IsWhiteSpace(_currentChar) || _currentChar == ':') + if (char.IsWhiteSpace(currentChar) || currentChar == ':') { - break; + return currentChar; } - else if (ValidIdentifierChar(_currentChar)) + else if (ValidIdentifierChar(currentChar)) { - _buffer.Append(_currentChar); + _buffer.Append(currentChar); } else { - throw CreateJsonReaderException("Invalid JavaScript property identifier character: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); + throw CreateJsonReaderException("Invalid JavaScript property identifier character: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); } } + + throw CreateJsonReaderException("Unexpected end when parsing unquoted property name. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); } - private bool ParseValue() + private bool ParseValue(char currentChar) { do { - switch (_currentChar) + switch (currentChar) { case '"': case '\'': - ParseString(_currentChar); + ParseString(currentChar); return true; case 't': ParseTrue(); @@ -570,14 +541,14 @@ namespace Newtonsoft.Json case 'n': if (HasNext()) { - char next = PeekNext(); + char next = (char)PeekNext(); if (next == 'u') ParseNull(); else if (next == 'e') ParseConstructor(); else - throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); + throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); } else { @@ -594,7 +565,7 @@ namespace Newtonsoft.Json if (PeekNext() == 'I') ParseNumberNegativeInfinity(); else - ParseNumber(); + ParseNumber(currentChar); return true; case '/': ParseComment(); @@ -616,63 +587,70 @@ namespace Newtonsoft.Json return true; case ',': SetToken(JsonToken.Undefined); - //ClearCurrentChar(); return true; case ')': SetToken(JsonToken.EndConstructor); return true; case ' ': + case StringUtils.Tab: + case StringUtils.LineFeed: + case StringUtils.CarriageReturn: // eat break; default: - if (char.IsWhiteSpace(_currentChar)) + if (char.IsWhiteSpace(currentChar)) { // eat } - else if (char.IsNumber(_currentChar) || _currentChar == '-' || _currentChar == '.') + else if (char.IsNumber(currentChar) || currentChar == '-' || currentChar == '.') { - ParseNumber(); + ParseNumber(currentChar); return true; } else { - throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); + throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); } break; } - } while (MoveNext()); + } while ((currentChar = MoveNext()) != '\0' || !_end); return false; } - private bool EatWhitespace(bool oneOrMore) + private bool EatWhitespace(char initialChar, bool oneOrMore, out char finalChar) { bool whitespace = false; - while (_currentChar == ' ' || char.IsWhiteSpace(_currentChar)) + char currentChar = initialChar; + while (currentChar == ' ' || char.IsWhiteSpace(currentChar)) { whitespace = true; - MoveNext(); + currentChar = MoveNext(); } + finalChar = currentChar; + return (!oneOrMore || whitespace); } private void ParseConstructor() { - if (MatchValue("new", true)) + if (MatchValue('n', "new", true)) { - if (EatWhitespace(true)) + char currentChar = MoveNext(); + + if (EatWhitespace(currentChar, true, out currentChar)) { - while (char.IsLetter(_currentChar)) + while (char.IsLetter(currentChar)) { - _buffer.Append(_currentChar); - MoveNext(); + _buffer.Append(currentChar); + currentChar = MoveNext(); } - EatWhitespace(false); + EatWhitespace(currentChar, false, out currentChar); - if (_currentChar != '(') - throw CreateJsonReaderException("Unexpected character while parsing constructor: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); + if (currentChar != '(') + throw CreateJsonReaderException("Unexpected character while parsing constructor: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); string constructorName = _buffer.ToString(); _buffer.Position = 0; @@ -682,22 +660,25 @@ namespace Newtonsoft.Json } } - private void ParseNumber() + private void ParseNumber(char firstChar) { + char currentChar = firstChar; + // parse until seperator character or end bool end = false; do { - if (CurrentIsSeperator()) + if (IsSeperator(currentChar)) + { end = true; + _lastChar = currentChar; + } else - _buffer.Append(_currentChar); + { + _buffer.Append(currentChar); + } - } while (!end && MoveNext()); - - // hit the end of the reader before the number ended. clear the last number value - if (!end) - ClearCurrentChar(); + } while (!end && ((currentChar = MoveNext()) != '\0' || !_end)); string number = _buffer.ToString(); object numberValue; @@ -706,12 +687,12 @@ namespace Newtonsoft.Json if (number.IndexOf(".", StringComparison.OrdinalIgnoreCase) == -1 && number.IndexOf("e", StringComparison.OrdinalIgnoreCase) == -1) { - numberValue = Convert.ToInt64(_buffer.ToString(), CultureInfo.InvariantCulture); + numberValue = Convert.ToInt64(number, CultureInfo.InvariantCulture); numberType = JsonToken.Integer; } else { - numberValue = Convert.ToDouble(_buffer.ToString(), CultureInfo.InvariantCulture); + numberValue = Convert.ToDouble(number, CultureInfo.InvariantCulture); numberType = JsonToken.Float; } @@ -724,30 +705,30 @@ namespace Newtonsoft.Json { // should have already parsed / character before reaching this method - MoveNext(); + char currentChar = MoveNext(); - if (_currentChar == '*') + if (currentChar == '*') { - while (MoveNext()) + while ((currentChar = MoveNext()) != '\0' || !_end) { - if (_currentChar == '*') + if (currentChar == '*') { - if (MoveNext()) + if ((currentChar = MoveNext()) != '\0' || !_end) { - if (_currentChar == '/') + if (currentChar == '/') { break; } else { _buffer.Append('*'); - _buffer.Append(_currentChar); + _buffer.Append(currentChar); } } } else { - _buffer.Append(_currentChar); + _buffer.Append(currentChar); } } } @@ -759,30 +740,30 @@ namespace Newtonsoft.Json SetToken(JsonToken.Comment, _buffer.ToString()); _buffer.Position = 0; - - ClearCurrentChar(); } - private bool MatchValue(string value) + private bool MatchValue(char firstChar, string value) { + char currentChar = firstChar; + int i = 0; do { - if (_currentChar != value[i]) + if (currentChar != value[i]) { break; } i++; } - while (i < value.Length && MoveNext()); + while (i < value.Length && ((currentChar = MoveNext()) != '\0' || !_end)); return (i == value.Length); } - private bool MatchValue(string value, bool noTrailingNonSeperatorCharacters) + private bool MatchValue(char firstChar, string value, bool noTrailingNonSeperatorCharacters) { // will match value and then move to the next character, checking that it is a seperator character - bool match = MatchValue(value); + bool match = MatchValue(firstChar, value); if (!noTrailingNonSeperatorCharacters) { @@ -790,17 +771,17 @@ namespace Newtonsoft.Json } else { - bool matchAndNoTrainingNonSeperatorCharacters = (match && (!MoveNext() || CurrentIsSeperator())); - if (!CurrentIsSeperator()) - ClearCurrentChar(); + int c = PeekNext(); + char next = (c != -1) ? (char) c : '\0'; + bool matchAndNoTrainingNonSeperatorCharacters = (match && (next == '\0' || IsSeperator(next))); return matchAndNoTrainingNonSeperatorCharacters; } } - private bool CurrentIsSeperator() + private bool IsSeperator(char c) { - switch (_currentChar) + switch (c) { case '}': case ']': @@ -814,9 +795,12 @@ namespace Newtonsoft.Json return true; break; case ' ': + case StringUtils.Tab: + case StringUtils.LineFeed: + case StringUtils.CarriageReturn: return true; default: - if (char.IsWhiteSpace(_currentChar)) + if (char.IsWhiteSpace(c)) return true; break; } @@ -829,7 +813,7 @@ namespace Newtonsoft.Json // check characters equal 'true' // and that it is followed by either a seperator character // or the text ends - if (MatchValue(JsonConvert.True, true)) + if (MatchValue('t', JsonConvert.True, true)) { SetToken(JsonToken.Boolean, true); } @@ -841,7 +825,7 @@ namespace Newtonsoft.Json private void ParseNull() { - if (MatchValue(JsonConvert.Null, true)) + if (MatchValue('n', JsonConvert.Null, true)) { SetToken(JsonToken.Null); } @@ -853,7 +837,7 @@ namespace Newtonsoft.Json private void ParseUndefined() { - if (MatchValue(JsonConvert.Undefined, true)) + if (MatchValue('u', JsonConvert.Undefined, true)) { SetToken(JsonToken.Undefined); } @@ -865,7 +849,7 @@ namespace Newtonsoft.Json private void ParseFalse() { - if (MatchValue(JsonConvert.False, true)) + if (MatchValue('f', JsonConvert.False, true)) { SetToken(JsonToken.Boolean, false); } @@ -877,7 +861,7 @@ namespace Newtonsoft.Json private void ParseNumberNegativeInfinity() { - if (MatchValue(JsonConvert.NegativeInfinity, true)) + if (MatchValue('-', JsonConvert.NegativeInfinity, true)) { SetToken(JsonToken.Float, double.NegativeInfinity); } @@ -889,7 +873,7 @@ namespace Newtonsoft.Json private void ParseNumberPositiveInfinity() { - if (MatchValue(JsonConvert.PositiveInfinity, true)) + if (MatchValue('I', JsonConvert.PositiveInfinity, true)) { SetToken(JsonToken.Float, double.PositiveInfinity); } @@ -901,7 +885,7 @@ namespace Newtonsoft.Json private void ParseNumberNaN() { - if (MatchValue(JsonConvert.NaN, true)) + if (MatchValue('N', JsonConvert.NaN, true)) { SetToken(JsonToken.Float, double.NaN); } @@ -964,4 +948,4 @@ namespace Newtonsoft.Json get { return _currentLinePosition; } } } -} +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonTextWriter.cs b/Src/Newtonsoft.Json/JsonTextWriter.cs index df444f76..df7b7d85 100644 --- a/Src/Newtonsoft.Json/JsonTextWriter.cs +++ b/Src/Newtonsoft.Json/JsonTextWriter.cs @@ -288,7 +288,15 @@ namespace Newtonsoft.Json public override void WriteValue(string value) { base.WriteValue(value); - WriteValueInternal((value != null) ? JsonConvert.ToString(value, _quoteChar) : JsonConvert.Null, JsonToken.String); + if (value == null) + WriteValueInternal(JsonConvert.Null, JsonToken.Null); + else + { + JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true); + //_writer.Write(JavaScriptUtils.EscapeJavaScriptString(value, _quoteChar, false)); + //WriteValueInternal(, JsonToken.String); + //JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true); + } } /// @@ -428,7 +436,7 @@ namespace Newtonsoft.Json public override void WriteValue(DateTime value) { base.WriteValue(value); - WriteValueInternal(JsonConvert.ToString(value), JsonToken.Date); + JsonConvert.WriteDateTimeString(_writer, value); } /// diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index 2a7ddc6f..3b5b9884 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -107,17 +107,17 @@ namespace Newtonsoft.Json } // array that gives a new state based on the current state an the token being written - private static readonly State[,] stateArray = { + private static readonly State[][] stateArray = new[] { // Start PropertyName ObjectStart Object ArrayStart Array ConstructorStart Constructor Closed Error // -/* None */{ State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error }, -/* StartObject */{ State.ObjectStart, State.ObjectStart, State.Error, State.Error, State.ObjectStart, State.ObjectStart, State.ObjectStart, State.ObjectStart, State.Error, State.Error }, -/* StartArray */{ State.ArrayStart, State.ArrayStart, State.Error, State.Error, State.ArrayStart, State.ArrayStart, State.ArrayStart, State.ArrayStart, State.Error, State.Error }, -/* StartConstructor */{ State.ConstructorStart, State.ConstructorStart, State.Error, State.Error, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.Error, State.Error }, -/* StartProperty */{ State.Property, State.Error, State.Property, State.Property, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error }, -/* Comment */{ State.Start, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Constructor, State.Constructor, State.Error, State.Error }, -/* Raw */{ State.Start, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Constructor, State.Constructor, State.Error, State.Error }, -/* Value */{ State.Start, State.Object, State.Error, State.Error, State.Array, State.Array, State.Constructor, State.Constructor, State.Error, State.Error }, +/* None */new[]{ State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error }, +/* StartObject */new[]{ State.ObjectStart, State.ObjectStart, State.Error, State.Error, State.ObjectStart, State.ObjectStart, State.ObjectStart, State.ObjectStart, State.Error, State.Error }, +/* StartArray */new[]{ State.ArrayStart, State.ArrayStart, State.Error, State.Error, State.ArrayStart, State.ArrayStart, State.ArrayStart, State.ArrayStart, State.Error, State.Error }, +/* StartConstructor */new[]{ State.ConstructorStart, State.ConstructorStart, State.Error, State.Error, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.Error, State.Error }, +/* StartProperty */new[]{ State.Property, State.Error, State.Property, State.Property, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error }, +/* Comment */new[]{ State.Start, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Constructor, State.Constructor, State.Error, State.Error }, +/* Raw */new[]{ State.Start, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Constructor, State.Constructor, State.Error, State.Error }, +/* Value */new[]{ State.Start, State.Object, State.Error, State.Error, State.Array, State.Array, State.Constructor, State.Constructor, State.Error, State.Error }, }; private int _top; @@ -586,7 +586,7 @@ namespace Newtonsoft.Json } // gets new state based on the current state and what is being written - State newState = stateArray[token, (int)_currentState]; + State newState = stateArray[token][(int)_currentState]; if (newState == State.Error) throw new JsonWriterException("Token {0} in state {1} would result in an invalid JavaScript object.".FormatWith(CultureInfo.InvariantCulture, tokenBeingWritten.ToString(), _currentState.ToString())); diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 9868d3b1..b4f41e02 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -209,7 +209,7 @@ namespace Newtonsoft.Json.Serialization if (ReflectionUtils.HasDefaultConstructor(contract.CreatedType, true) || contract.CreatedType.IsValueType) { -#if !PocketPC +#if !PocketPC && !SILVERLIGHT contract.DefaultCreator = LateBoundDelegateFactory.CreateDefaultConstructor(contract.CreatedType); #else ConstructorInfo constructorInfo = ReflectionUtils.GetDefaultConstructor(contract.CreatedType, true); @@ -414,7 +414,7 @@ namespace Newtonsoft.Json.Serialization { JsonProperty property = new JsonProperty(); property.PropertyType = ReflectionUtils.GetMemberUnderlyingType(member); -#if !PocketPC +#if !PocketPC && !SILVERLIGHT property.ValueProvider = new DynamicValueProvider(member); #else property.ValueProvider = new ReflectionValueProvider(member); diff --git a/Src/Newtonsoft.Json/Serialization/DynamicValueProvider.cs b/Src/Newtonsoft.Json/Serialization/DynamicValueProvider.cs index d0ad41cb..41736ed5 100644 --- a/Src/Newtonsoft.Json/Serialization/DynamicValueProvider.cs +++ b/Src/Newtonsoft.Json/Serialization/DynamicValueProvider.cs @@ -23,7 +23,7 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion -#if !PocketPC +#if !PocketPC && !SILVERLIGHT using System; using System.Collections.Generic; using System.Linq; @@ -65,6 +65,20 @@ namespace Newtonsoft.Json.Serialization if (_setter == null) _setter = LateBoundDelegateFactory.CreateSet(_memberInfo); +#if DEBUG + // dynamic method doesn't check whether the type is 'legal' to set + // add this check for unit tests + if (value == null) + { + if (!ReflectionUtils.IsNullable(ReflectionUtils.GetMemberUnderlyingType(_memberInfo))) + throw new Exception("Incompatible value. Cannot set {0} to null.".FormatWith(CultureInfo.InvariantCulture, _memberInfo)); + } + else if (!ReflectionUtils.GetMemberUnderlyingType(_memberInfo).IsAssignableFrom(value.GetType())) + { + throw new Exception("Incompatible value. Cannot set {0} to type {1}.".FormatWith(CultureInfo.InvariantCulture, _memberInfo, value.GetType())); + } +#endif + _setter(target, value); } catch (Exception ex) diff --git a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs index cdae6528..9f45fdfb 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs @@ -82,7 +82,7 @@ namespace Newtonsoft.Json.Serialization _genericWrapperType = ReflectionUtils.MakeGenericType(typeof(CollectionWrapper<>), CollectionItemType); ConstructorInfo genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { _genericCollectionDefinitionType }); -#if !PocketPC +#if !PocketPC && !SILVERLIGHT _genericWrapperCreator = LateBoundDelegateFactory.CreateMethodHandler(genericWrapperConstructor); #else _genericWrapperCreator = (target, args) => genericWrapperConstructor.Invoke(new[] { args[0] }); diff --git a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs index 7a345a9f..04b2b9d1 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs @@ -85,7 +85,7 @@ namespace Newtonsoft.Json.Serialization _genericWrapperType = ReflectionUtils.MakeGenericType(typeof(DictionaryWrapper<,>), DictionaryKeyType, DictionaryValueType); ConstructorInfo genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { _genericCollectionDefinitionType }); -#if !PocketPC +#if !PocketPC && !SILVERLIGHT _genericWrapperCreator = LateBoundDelegateFactory.CreateMethodHandler(genericWrapperConstructor); #else _genericWrapperCreator = (target, args) => genericWrapperConstructor.Invoke(new[] { args[0] }); diff --git a/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs b/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs index 2a3aeaba..8bf17c95 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs @@ -85,43 +85,38 @@ namespace Newtonsoft.Json.Serialization } /// - /// Tries to get the closest matching object. + /// Gets the closest matching object. /// First attempts to get an exact case match of propertyName and then /// a case insensitive match. /// /// Name of the property. - /// A matching property if found. - /// A flag indicating whether a match was found. - public bool TryGetClosestMatchProperty(string propertyName, out JsonProperty property) + /// A matching property if found. + public JsonProperty GetClosestMatchProperty(string propertyName) { - if (TryGetProperty(propertyName, StringComparison.Ordinal, out property)) - return true; - if (TryGetProperty(propertyName, StringComparison.OrdinalIgnoreCase, out property)) - return true; + JsonProperty property = GetProperty(propertyName, StringComparison.Ordinal); + if (property == null) + property = GetProperty(propertyName, StringComparison.OrdinalIgnoreCase); - return false; + return property; } /// - /// Tries to get a property by property name. + /// Gets a property by property name. /// /// The name of the property to get. /// Type property name string comparison. - /// A matching property if found. - /// A flag indicating whether a match was found. - public bool TryGetProperty(string propertyName, StringComparison comparisonType, out JsonProperty matchingProperty) + /// A matching property if found. + public JsonProperty GetProperty(string propertyName, StringComparison comparisonType) { foreach (JsonProperty property in this) { - if (string.Compare(propertyName, property.PropertyName, comparisonType) == 0) + if (string.Equals(propertyName, property.PropertyName, comparisonType)) { - matchingProperty = property; - return true; + return property; } } - matchingProperty = default(JsonProperty); - return false; + return null; } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 4c38583d..5ad164da 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -171,8 +171,8 @@ namespace Newtonsoft.Json.Serialization if (memberConverter != null) return memberConverter.ReadJson(reader, objectType, GetInternalSerializer()); - if (objectType != null && Serializer.HasClassConverter(contract, out converter)) - return converter.ReadJson(reader, objectType, GetInternalSerializer()); + if (contract != null && contract.Converter != null) + return contract.Converter.ReadJson(reader, objectType, GetInternalSerializer()); if (objectType != null && Serializer.HasMatchingConverter(objectType, out converter)) return converter.ReadJson(reader, objectType, GetInternalSerializer()); @@ -472,7 +472,7 @@ namespace Newtonsoft.Json.Serialization return dictionary.UnderlyingDictionary; } - private IDictionary PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, string id) + private object PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, string id) { if (id != null) Serializer.ReferenceResolver.AddReference(id, dictionary.UnderlyingDictionary); @@ -491,7 +491,7 @@ namespace Newtonsoft.Json.Serialization try { - dictionary.Add(keyValue, CreateValue(reader, contract.DictionaryValueType, GetContractSafe(contract.DictionaryValueType), null, null)); + dictionary[keyValue] = CreateValue(reader, contract.DictionaryValueType, GetContractSafe(contract.DictionaryValueType), null, null); } catch (Exception ex) { @@ -504,7 +504,7 @@ namespace Newtonsoft.Json.Serialization case JsonToken.EndObject: contract.InvokeOnDeserialized(dictionary.UnderlyingDictionary); - return dictionary; + return dictionary.UnderlyingDictionary; default: throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); } @@ -615,10 +615,11 @@ namespace Newtonsoft.Json.Serialization if (!reader.Read()) throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); - JsonProperty property; // attempt exact case match first // then try match ignoring case - if (contract.Properties.TryGetClosestMatchProperty(memberName, out property)) + JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName); + + if (property != null) { if (!property.Ignored) { @@ -693,10 +694,11 @@ namespace Newtonsoft.Json.Serialization case JsonToken.PropertyName: string memberName = reader.Value.ToString(); - JsonProperty property; // attempt exact case match first // then try match ignoring case - if (!contract.Properties.TryGetClosestMatchProperty(memberName, out property)) + JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName); + + if (property == null) { if (Serializer.MissingMemberHandling == MissingMemberHandling.Error) throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType.Name)); diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 6ed820aa..44e6d0fc 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -91,7 +91,7 @@ namespace Newtonsoft.Json.Serialization } if (converter != null - || Serializer.HasClassConverter(contract, out converter) + || ((converter = contract.Converter) != null) || Serializer.HasMatchingConverter(contract.UnderlyingType, out converter)) { SerializeConvertable(writer, converter, value, contract); @@ -165,7 +165,7 @@ namespace Newtonsoft.Json.Serialization return Serializer.ReferenceResolver.IsReferenced(value); } - private void WriteMemberInfoProperty(JsonWriter writer, object value, object memberValue, JsonProperty property, JsonContract contract) + private void WriteMemberInfoProperty(JsonWriter writer, object memberValue, JsonProperty property, JsonContract contract) { string propertyName = property.PropertyName; JsonConverter memberConverter = property.MemberConverter; @@ -295,7 +295,7 @@ namespace Newtonsoft.Json.Serialization object memberValue = property.ValueProvider.GetValue(value); JsonContract memberContract = GetContractSafe(memberValue); - WriteMemberInfoProperty(writer, value, memberValue, property, memberContract); + WriteMemberInfoProperty(writer, memberValue, property, memberContract); } } catch (Exception ex) diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 9bb213a1..65de5bce 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -314,6 +314,10 @@ namespace Newtonsoft.Json.Utilities public static object ConvertOrCast(object initialValue, CultureInfo culture, Type targetType) { object convertedValue; + + if (initialValue == null && ReflectionUtils.IsNullable(targetType)) + return null; + if (TryConvert(initialValue, culture, targetType, out convertedValue)) return convertedValue; diff --git a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs index 1bb723aa..b5023943 100644 --- a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs @@ -35,68 +35,100 @@ namespace Newtonsoft.Json.Utilities { internal static class JavaScriptUtils { - public static void WriteEscapedJavaScriptString(StringBuilder writer, string value, char delimiter, bool appendDelimiters) + public static void WriteEscapedJavaScriptString(TextWriter writer, string value, char delimiter, bool appendDelimiters) { // leading delimiter if (appendDelimiters) - writer.Append(delimiter); + writer.Write(delimiter); if (value != null) { + int lastWritePosition = 0; + int skipped = 0; + char[] chars = null; + for (int i = 0; i < value.Length; i++) { char c = value[i]; + string escapedValue; switch (c) { case '\t': - writer.Append(@"\t"); + escapedValue = @"\t"; break; case '\n': - writer.Append(@"\n"); + escapedValue = @"\n"; break; case '\r': - writer.Append(@"\r"); + escapedValue = @"\r"; break; case '\f': - writer.Append(@"\f"); + escapedValue = @"\f"; break; case '\b': - writer.Append(@"\b"); + escapedValue = @"\b"; break; case '\\': - writer.Append(@"\\"); + escapedValue = @"\\"; break; case '\u0085': // Next Line - writer.Append(@"\u0085"); + escapedValue = @"\u0085"; break; case '\u2028': // Line Separator - writer.Append(@"\u2028"); + escapedValue = @"\u2028"; break; case '\u2029': // Paragraph Separator - writer.Append(@"\u2029"); + escapedValue = @"\u2029"; break; case '\'': // only escape if this charater is being used as the delimiter - writer.Append((delimiter == '\'') ? @"\'" : @"'"); + escapedValue = (delimiter == '\'') ? @"\'" : null; break; case '"': // only escape if this charater is being used as the delimiter - writer.Append((delimiter == '"') ? "\\\"" : @""""); + escapedValue = (delimiter == '"') ? "\\\"" : null; break; default: - if (c > '\u001f') - writer.Append(c); - else - StringUtils.WriteCharAsUnicode(writer, c); + escapedValue = (c <= '\u001f') ? StringUtils.ToCharAsUnicode(c) : null; break; } + + if (escapedValue != null) + { + if (chars == null) + chars = value.ToCharArray(); + + // write skipped text + if (skipped > 0) + { + writer.Write(chars, lastWritePosition, skipped); + skipped = 0; + } + + // write escaped value and note position + writer.Write(escapedValue); + lastWritePosition = i + 1; + } + else + { + skipped++; + } + } + + // write any remaining skipped text + if (skipped > 0) + { + if (lastWritePosition == 0) + writer.Write(value); + else + writer.Write(chars, lastWritePosition, skipped); } } // trailing delimiter if (appendDelimiters) - writer.Append(delimiter); + writer.Write(delimiter); } public static string ToEscapedJavaScriptString(string value) @@ -106,14 +138,11 @@ namespace Newtonsoft.Json.Utilities public static string ToEscapedJavaScriptString(string value, char delimiter, bool appendDelimiters) { - int length = value.Length; - if (appendDelimiters) - length += 2; - - StringBuilder builder = new StringBuilder(length); - - WriteEscapedJavaScriptString(builder, value, delimiter, appendDelimiters); - return builder.ToString(); + using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(value) ?? 16)) + { + WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters); + return w.ToString(); + } } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/LateBoundDelegateFactory.cs b/Src/Newtonsoft.Json/Utilities/LateBoundDelegateFactory.cs index 8d9d6956..fee1a3a5 100644 --- a/Src/Newtonsoft.Json/Utilities/LateBoundDelegateFactory.cs +++ b/Src/Newtonsoft.Json/Utilities/LateBoundDelegateFactory.cs @@ -1,4 +1,29 @@ -#if !PocketPC +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC && !SILVERLIGHT using System; using System.Collections.Generic; using System.Linq; diff --git a/Src/Newtonsoft.Json/Utilities/StringBuffer.cs b/Src/Newtonsoft.Json/Utilities/StringBuffer.cs index 22897f40..747e6d0a 100644 --- a/Src/Newtonsoft.Json/Utilities/StringBuffer.cs +++ b/Src/Newtonsoft.Json/Utilities/StringBuffer.cs @@ -56,7 +56,7 @@ namespace Newtonsoft.Json.Utilities public void Append(char value) { // test if the buffer array is large enough to take the value - if (_position + 1 > _buffer.Length) + if (_position == _buffer.Length) { EnsureSize(1); } diff --git a/Src/Newtonsoft.Json/Utilities/StringUtils.cs b/Src/Newtonsoft.Json/Utilities/StringUtils.cs index 79b19a89..df5b7ee1 100644 --- a/Src/Newtonsoft.Json/Utilities/StringUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/StringUtils.cs @@ -325,13 +325,15 @@ namespace Newtonsoft.Json.Utilities public static string ToCharAsUnicode(char c) { - StringBuilder builder = new StringBuilder(); - WriteCharAsUnicode(builder, c); + char h1 = MathUtils.IntToHex((c >> 12) & '\x000f'); + char h2 = MathUtils.IntToHex((c >> 8) & '\x000f'); + char h3 = MathUtils.IntToHex((c >> 4) & '\x000f'); + char h4 = MathUtils.IntToHex(c & '\x000f'); - return builder.ToString(); + return new string(new[] { '\\', 'u', h1, h2, h3, h4 }); } - public static void WriteCharAsUnicode(StringBuilder writer, char c) + public static void WriteCharAsUnicode(TextWriter writer, char c) { ValidationUtils.ArgumentNotNull(writer, "writer"); @@ -340,12 +342,12 @@ namespace Newtonsoft.Json.Utilities char h3 = MathUtils.IntToHex((c >> 4) & '\x000f'); char h4 = MathUtils.IntToHex(c & '\x000f'); - writer.Append('\\'); - writer.Append('u'); - writer.Append(h1); - writer.Append(h2); - writer.Append(h3); - writer.Append(h4); + writer.Write('\\'); + writer.Write('u'); + writer.Write(h1); + writer.Write(h2); + writer.Write(h3); + writer.Write(h4); } public static TSource ForgivingCaseSensitiveFind(this IEnumerable source, Func valueSelector, string testValue)