From 1fa5e4a22e67904abdaef3fb58cbd4fb5997b600 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 27 Oct 2012 19:25:04 +1300 Subject: [PATCH] -Added missing XML documentation -Fixed potential null reference error when getting attributes -Performance documentation --- Doc/Performance.aml | 84 +++++++ Doc/doc.content | 3 +- Doc/doc.shfbproj | 5 + Doc/performance.png | Bin 0 -> 8010 bytes .../Documentation/PerformanceTests.cs | 218 ++++++++++++++++++ .../Linq/JObjectTests.cs | 18 ++ .../Newtonsoft.Json.Tests.csproj | 1 + Src/Newtonsoft.Json.Tests/PerformanceTests.cs | 101 +++++++- .../Properties/AssemblyInfo.cs | 2 +- .../DefaultValueHandlingTests.cs | 27 +++ Src/Newtonsoft.Json/Linq/JObject.cs | 3 + .../Properties/AssemblyInfo.cs | 2 +- .../Utilities/ReflectionUtils.cs | 2 +- .../Utilities/ThreadSafeStore.cs | 6 +- 14 files changed, 459 insertions(+), 13 deletions(-) create mode 100644 Doc/Performance.aml create mode 100644 Doc/performance.png create mode 100644 Src/Newtonsoft.Json.Tests/Documentation/PerformanceTests.cs 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 0000000000000000000000000000000000000000..cd63d1bdd057cd37e907650907775c63dff70198 GIT binary patch literal 8010 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 ZT2xh5*G~pX(?5SXb#Ce3EKx^C{s;8r_AUSb literal 0 HcmV?d00001 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; }