diff --git a/Doc/Performance.aml b/Doc/Performance.aml new file mode 100644 index 00000000..ce89d2ff --- /dev/null +++ b/Doc/Performance.aml @@ -0,0 +1,84 @@ + + + + + Out of the box Json.NET is faster than DataContractJsonSerializer and JavaScriptSerializer. + Here are some tips to make it go even faster. + +
+ Optimize Memory Usage + + To keep an application consistantly fast it is important to minimize the + amount of time the .NET framework spends performing +garbage collection +http://msdn.microsoft.com/en-us/library/ms973837.aspx +_blank +. + Allocating too many objects, or allocating very large objects can slow down or even + halt an application while garbage collection is in progress. + + To minimize memory usage and the number of objects allocated Json.NET supports + serializing and deserializing directly to a stream. Reading or writing JSON a piece at a time instead of having + the entire JSON string loaded into memory is especially important when working with JSON + documents greater than 85kb in size to avoid the JSON string ending up in the +large object heap +http://msdn.microsoft.com/en-us/magazine/cc534993.aspx +_blank +. + + + + + +
+
+ JsonConverters + + Passing a T:Newtonsoft.Json.JsonConverter to SerializeObject or DeserializeObject provides a simple way to completely + change how an object is serialized. There is however a small overhead; the CanConvert method is called for every + type to check whether serialization should be handled by that JsonConverter. + There are a couple of ways to continue to use JsonConverters without any overhead. The simplest way + is to specify the JsonConverter using the T:Newtonsoft.Json.JsonConverterAttribute. This attribute tells the serializer + to always use that converter when serializing and deserializing the type, without the check. + + + + If the class you want to convert isn't your own and you're unable to use an attribute a JsonConverter can still be used by + creating your own T:Newtonsoft.Json.Serialization.IContractResolver. + + + + The IContractResolver in the example above will set all DateTimes to use the JavaScriptDateConverter. + +
+
+ Manually Serialize + + The absolute fastest way to read and write JSON is to use JsonTextReader and JsonTextWriter directly. Using a reader or writer + directly skips any of the overhead from a serializer such as reflection. + + + + If performance is important and you don't mind more code to get it then this is your best choice. + +
+
+ Benchmarks + + + + + Json.NET Performance + + + +
+ + T:Newtonsoft.Json.JsonSerializer + T:Newtonsoft.Json.JsonConverter + T:Newtonsoft.Json.JsonConverterAttribute + T:Newtonsoft.Json.JsonTextWriter + T:Newtonsoft.Json.JsonTextReader + +
+
\ No newline at end of file diff --git a/Doc/doc.content b/Doc/doc.content index a7d32cd5..47ec2e05 100644 --- a/Doc/doc.content +++ b/Doc/doc.content @@ -21,7 +21,8 @@ - + + \ No newline at end of file diff --git a/Doc/doc.shfbproj b/Doc/doc.shfbproj index af9f3ede..581fb1a9 100644 --- a/Doc/doc.shfbproj +++ b/Doc/doc.shfbproj @@ -141,6 +141,7 @@ + @@ -165,6 +166,10 @@ + + performance + performance + logo logo diff --git a/Doc/performance.png b/Doc/performance.png new file mode 100644 index 00000000..cd63d1bd Binary files /dev/null and b/Doc/performance.png differ diff --git a/Src/Newtonsoft.Json.Tests/Documentation/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/Documentation/PerformanceTests.cs new file mode 100644 index 00000000..b0058366 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Documentation/PerformanceTests.cs @@ -0,0 +1,218 @@ +#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 !(NET35 || NET20 || PORTABLE) +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Dynamic; +using System.IO; +using System.Linq; +using System.Net; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +#if !NETFX_CORE +using NUnit.Framework; +#else +using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; +using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute; +using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute; +#endif +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Tests.TestObjects; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Tests.Documentation +{ + public class HttpClient + { + public Task GetStringAsync(string requestUri) + { + return null; + } + + public Task GetStreamAsync(string requestUri) + { + return null; + } + } + + #region JsonConverterAttribute + [JsonConverter(typeof(PersonConverter))] + public class Person + { + public Person() + { + Likes = new List(); + } + + public string Name { get; set; } + public IList Likes { get; private set; } + } + #endregion + + #region JsonConverterContractResolver + public class ConverterContractResolver : DefaultContractResolver + { + protected override JsonContract CreateContract(Type objectType) + { + JsonContract contract = base.CreateContract(objectType); + + if (objectType == typeof(DateTime) || objectType == typeof(DateTimeOffset)) + contract.Converter = new JavaScriptDateTimeConverter(); + + return contract; + } + } + #endregion + + public class PersonConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return null; + } + + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(Person)); + } + } + + [TestFixture] + public class PerformanceTests : TestFixtureBase + { + [Test] + public void ConverterContractResolverTest() + { + string json = JsonConvert.SerializeObject(new DateTime(2000, 10, 10, 10, 10, 10, DateTimeKind.Utc), new JsonSerializerSettings + { + ContractResolver = new ConverterContractResolver() + }); + + Console.WriteLine(json); + } + + public void DeserializeString() + { + #region DeserializeString + HttpClient client = new HttpClient(); + + // read the json into a string + // string could potentially be very large and cause memory problems + string json = client.GetStringAsync("http://www.test.com/large.json").Result; + + Person p = JsonConvert.DeserializeObject(json); + #endregion + } + + public void DeserializeStream() + { + #region DeserializeStream + HttpClient client = new HttpClient(); + + using (Stream s = client.GetStreamAsync("http://www.test.com/large.json").Result) + using (StreamReader sr = new StreamReader(s)) + using (JsonReader reader = new JsonTextReader(sr)) + { + JsonSerializer serializer = new JsonSerializer(); + + // read the json from a stream + // json size doesn't matter because only a small piece is read at a time from the HTTP request + Person p = serializer.Deserialize(reader); + } + #endregion + } + } + + public static class PersonWriter + { + #region ReaderWriter + public static string ToJson(this Person p) + { + StringWriter sw = new StringWriter(); + JsonTextWriter writer = new JsonTextWriter(sw); + + // { + writer.WriteStartObject(); + + // "name" : "Jerry" + writer.WritePropertyName("name"); + writer.WriteValue(p.Name); + + // "likes": ["Comedy", "Superman"] + writer.WritePropertyName("likes"); + writer.WriteStartArray(); + foreach (string like in p.Likes) + { + writer.WriteValue(like); + } + writer.WriteEndArray(); + + // } + writer.WriteEndObject(); + + return sw.ToString(); + } + #endregion + + public static Person ToPerson(this string s) + { + StringReader sr = new StringReader(s); + JsonTextReader reader = new JsonTextReader(sr); + + Person p = new Person(); + + // { + reader.Read(); + // "name" + reader.Read(); + // "Jerry" + p.Name = reader.ReadAsString(); + // "likes" + reader.Read(); + // [ + reader.Read(); + // "Comedy", "Superman", ] + while (reader.Read() && reader.TokenType != JsonToken.EndArray) + { + p.Likes.Add((string)reader.Value); + } + // } + reader.Read(); + + return p; + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs index 704faf76..12983fc7 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -1881,6 +1881,24 @@ Parameter name: arrayIndex", Assert.IsTrue(JToken.DeepEquals(b, a)); } + [Test] + public void GetValueBlogExample() + { + JObject o = JObject.Parse(@"{ + 'name': 'Lower', + 'NAME': 'Upper' + }"); + + string exactMatch = (string)o.GetValue("NAME", StringComparison.OrdinalIgnoreCase); + // Upper + + string ignoreCase = (string)o.GetValue("Name", StringComparison.OrdinalIgnoreCase); + // Lower + + Assert.AreEqual("Upper", exactMatch); + Assert.AreEqual("Lower", ignoreCase); + } + [Test] public void GetValue() { diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 6885929d..afa8eb6d 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -117,6 +117,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index 9db01802..7bcc6a18 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -33,7 +33,6 @@ using System.Linq; using System.Runtime.Serialization; using System.Web.Script.Serialization; using System.Xml.Linq; -using System.Xml.Serialization; using Newtonsoft.Json.Utilities; using NUnit.Framework; using System.Runtime.Serialization.Json; @@ -123,6 +122,8 @@ namespace Newtonsoft.Json.Tests TestClass test = CreateSerializationObject(); SerializeTests(test); + + Deserialize(); } private void SerializeTests(object value) @@ -153,6 +154,7 @@ namespace Newtonsoft.Json.Tests BenchmarkDeserializeMethod(SerializeMethod.JavaScriptSerializer, json); BenchmarkDeserializeMethod(SerializeMethod.DataContractJsonSerializer, json); BenchmarkDeserializeMethod(SerializeMethod.JsonNet, json); + BenchmarkDeserializeMethod(SerializeMethod.JsonNetManual, json); } [Test] @@ -658,12 +660,94 @@ namespace Newtonsoft.Json.Tests var value = (T) serializer.Deserialize(new StringReader(json), type); return value; - //JsonTextReader reader = new JsonTextReader(new StringReader(JsonText)); - //while (reader.Read()) - //{ + } - //} - //return default(T); + public TestClass DeserializeJsonNetManual(string json) + { + TestClass c = new TestClass(); + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + reader.Read(); + while (reader.Read()) + { + if (reader.TokenType == JsonToken.PropertyName) + { + string propertyName = (string) reader.Value; + switch (propertyName) + { + case "strings": + reader.Read(); + while (reader.Read() && reader.TokenType != JsonToken.EndArray) + { + c.strings.Add((string) reader.Value); + } + break; + case "dictionary": + reader.Read(); + while (reader.Read() && reader.TokenType != JsonToken.EndObject) + { + string key = (string) reader.Value; + c.dictionary.Add(key, reader.ReadAsInt32().GetValueOrDefault()); + } + break; + case "Name": + c.Name = reader.ReadAsString(); + break; + case "Now": + c.Now = reader.ReadAsDateTime().GetValueOrDefault(); + break; + case "BigNumber": + c.BigNumber = reader.ReadAsDecimal().GetValueOrDefault(); + break; + case "Address1": + reader.Read(); + c.Address1 = CreateAddress(reader); + break; + case "Addresses": + reader.Read(); + while (reader.Read() && reader.TokenType != JsonToken.EndArray) + { + var address = CreateAddress(reader); + c.Addresses.Add(address); + } + break; + } + } + else + { + break; + } + } + + return c; + } + + private static Address CreateAddress(JsonTextReader reader) + { + Address a = new Address(); + while (reader.Read()) + { + if (reader.TokenType == JsonToken.PropertyName) + { + switch ((string) reader.Value) + { + case "Street": + a.Street = reader.ReadAsString(); + break; + case "Phone": + a.Phone = reader.ReadAsString(); + break; + case "Entered": + a.Entered = reader.ReadAsDateTime().GetValueOrDefault(); + break; + } + } + else + { + break; + } + } + return a; } public T DeserializeJsonNetBinary(byte[] bson) @@ -703,6 +787,11 @@ namespace Newtonsoft.Json.Tests return DeserializeJsonNet((string)json, false); case SerializeMethod.JsonNetWithIsoConverter: return DeserializeJsonNet((string)json, true); + case SerializeMethod.JsonNetManual: + if (typeof(T) == typeof(TestClass)) + return (T)(object)DeserializeJsonNetManual((string)json); + + return default(T); case SerializeMethod.JsonNetBinary: return DeserializeJsonNetBinary((byte[]) json); case SerializeMethod.BinaryFormatter: diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 72ebb846..19683bd3 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -76,5 +76,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.9.15402")] +[assembly: AssemblyFileVersion("4.5.10.15427")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs index b2455d0a..cb530ffa 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs @@ -46,6 +46,33 @@ namespace Newtonsoft.Json.Tests.Serialization [TestFixture] public class DefaultValueHandlingTests : TestFixtureBase { + [Test] + public void Include() + { + Invoice invoice = new Invoice + { + Company = "Acme Ltd.", + Amount = 50.0m, + Paid = false, + FollowUpDays = 30, + FollowUpEmailAddress = string.Empty, + PaidDate = null + }; + + string included = JsonConvert.SerializeObject(invoice, + Formatting.Indented, + new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Include }); + + Assert.AreEqual(@"{ + ""Company"": ""Acme Ltd."", + ""Amount"": 50.0, + ""Paid"": false, + ""PaidDate"": null, + ""FollowUpDays"": 30, + ""FollowUpEmailAddress"": """" +}", included); + } + [Test] public void SerializeInvoice() { diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 531f824b..2f8d1d43 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -381,6 +381,7 @@ namespace Newtonsoft.Json.Linq /// /// Gets the with the specified property name. /// + /// Name of the property. /// The with the specified property name. public JToken GetValue(string propertyName) { @@ -392,6 +393,8 @@ namespace Newtonsoft.Json.Linq /// The exact property name will be searched for first and if no matching property is found then /// the will be used to match a property. /// + /// Name of the property. + /// One of the enumeration values that specifies how the strings will be compared. /// The with the specified property name. public JToken GetValue(string propertyName, StringComparison comparison) { diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 942d9c05..715b0c5c 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -90,7 +90,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.9.15402")] +[assembly: AssemblyFileVersion("4.5.10.15427")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index 9216977a..3d1928fe 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -709,7 +709,7 @@ namespace Newtonsoft.Json.Utilities { T[] attributes = GetAttributes(attributeProvider, inherit); - return attributes.SingleOrDefault(); + return (attributes != null) ? attributes.SingleOrDefault() : null; } #if !(NETFX_CORE) diff --git a/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs b/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs index b4903e9f..acd94211 100644 --- a/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs +++ b/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs @@ -28,6 +28,7 @@ using System.Collections.Generic; #if NET20 using Newtonsoft.Json.Utilities.LinqBridge; #endif +using System.Threading; using Newtonsoft.Json.Serialization; namespace Newtonsoft.Json.Utilities @@ -44,13 +45,11 @@ namespace Newtonsoft.Json.Utilities throw new ArgumentNullException("creator"); _creator = creator; + _store = new Dictionary(); } public TValue Get(TKey key) { - if (_store == null) - return AddValue(key); - TValue value; if (!_store.TryGetValue(key, out value)) return AddValue(key); @@ -79,6 +78,7 @@ namespace Newtonsoft.Json.Utilities Dictionary newStore = new Dictionary(_store); newStore[key] = value; + Thread.MemoryBarrier(); _store = newStore; }