Json.NET 8.0 Release 1 - Allocations and bug fixes

Memory, Allocations and Performance

There is a big push across the .NET eco-system on performance. In modern .NET apps one of the biggest culprits of poor performance is allocating too many objects. The more objects and memory you allocate, the more the garbage collector needs to clean up. Best case garbage collection will slow your application; worst cause the app will halt execution until GC is finished. GC is a great way to kill pages per second on the server and frames per second on the client.

To reduce allocations and memory usage when serializing Json.NET 8.0 adds a new IArrayPool interface. Json.NET is already very lean when it comes to allocations, working with raw characters on array buffers instead of allocated strings, but those buffers can easily grow large, and a new buffer is created each time JSON is read or written. IArrayPool allows array buffers to be reused, similar to connection pooling with a database, or thread pooling in .NET.

public class JsonArrayPool : IArrayPool<char>
{
    public static readonly JsonArrayPool Instance = new JsonArrayPool();
 
    public char[] Rent(int minimumLength)
    {
        // get char array from System.Buffers shared pool
        return ArrayPool<char>.Shared.Rent(minimumLength);
    }
 
    public void Return(char[] array)
    {
        // return char array to System.Buffers shared pool
        ArrayPool<char>.Shared.Return(array);
    }
}

The example implemention above uses the upcoming System.Buffers package to manage pooling. Using the array pool just involves setting a property on JsonTextReader/JsonTextWriter.

IList<int> value;
 
JsonSerializer serializer = new JsonSerializer();
using (JsonTextReader reader = new JsonTextReader(new StringReader(@"[1,2,3,4]")))
{
    // reader will get buffer from array pool
    reader.ArrayPool = JsonArrayPool.Instance;
 
    value = serializer.Deserialize<IList<int>>(reader);
}

IArrayPool is a somewhat experimental feature. Unless you have extreme performance requirements or your own object pooling system already in-place you can wait until a default pool is automatically included with Json.NET.

JArray and Comments

Previously when loading JSON that contained comments into JArrays the comment would be added as an item in the JArray. This would be a common cause of errors: most developers would expect a commented out value in a JArray to disappear, instead it becomes a comment token, and an error would be thrown if the app looped over the array, casting its values.

In Json.NET 8.0 comments are ignored by default. If you want the old behaviour then set CommentHandling.Load on JsonLoadSettings.

Bug Fixes. Bug Fixes Everywhere.

There are two dozen minor bug fixes in Json.NET 8.0, ranging from fixes serializing DataSets and XML (brave souls still use them), to serializing F# discriminated unions in UWP applications.

Changes

Here is a complete list of what has changed since Json.NET 7.0 Release 1.

  • New feature - Added IArrayPool and a setting on JsonTextReader and JsonTextWriter
  • New feature - Added JsonReader.ReadAsDouble
  • New feature - Added JsonLoadSettings and CommentHandling to control loading comments in LINQ to JSON
  • New feature - Added JsonLoadSettings.LineInfoHandling
  • New feature - Added support for JsonConstructorAttribute on list and dictionary collections
  • New feature - Added support for deserializing string to Version
  • New feature - Added ShouldDeserialize to JsonProperty
  • New feature - Added Required.DisallowNull
  • New feature - Added support for converting JSON to XML with invalid XML name chracters
  • New feature - Improved case-insensitive deserialization performance
  • New feature - Improved date parsing performance
  • Change - Changed ReadAsDateTime, ReadAsDateTimeOffset, ReadAsBytes, ReadAsString, ReadAsInt32 on JsonReader from abstract to virtual
  • Change - Changed parsing JArrays to not include comments by default
  • Change - Changed JTokenWriter to use the last property instead of erroring when there are duplicate property names
  • Change - Changed Uri JValues to use OriginalString when written to JSON
  • Change - Changed DateTimeOffset JValues to return TypeCode.Object from IConvertible.GetTypeCode()
  • Fix - Fixed converting JSON metadata array value to XML
  • Fix - Fixed not including line information with some XML conversion errors
  • Fix - Fixed error when writing certain JSON with escaped characters
  • Fix - Fixed PopulateObject error when JSON starts with a comment
  • Fix - Fixed incorrect IJsonLineInfo line position after the first line
  • Fix - Fixed JSONPath when querying against Uri, Guid and Date values
  • Fix - Fixed JsonReader.Path when the path is escaped
  • Fix - Fixed writing JRaw twice when a TraceWriter is set
  • Fix - Fixed getting the wrong value when reading certain large integers
  • Fix - Fixed deserializing DataSet with a null DataTable
  • Fix - Fixed deserializing a null DataSet
  • Fix - Fixed error when serializing F# discriminated unions in Windows Store apps
  • Fix - Fixed error serializing some types when there are conflicting interface properties
  • Fix - Fixed error when settings ReferenceResolver is set to null
  • Fix - Fixed DateTimeZoneHandling not being used when writing DateTime dictionary keys
  • Fix - Fixed bug when converting an integer JValue to a nullable enum
  • Fix - Fixed converting null string JValue to XML
  • Fix - Fixed Mono generic class private field serialization bug
  • Fix - Fixed error when deserializing ignored property with mismatched type
  • Fix - Fixed deserializing to a type when setting JToken extension data
  • Fix - Fixed setting default values onto properties already set in constructor
  • Fix - Fixed casting dynamic JValue to JToken
  • Fix - Fixed serializing non-zero based arrays
  • Fix - Fixed reading 24 hour midnight ISO dates

Links

Json.NET GitHub Project

Json.NET 8.0 Release 1 Download - Json.NET source code and assemblies

Json.NET 7.0 Release 1 - Documentation, bug fixes, performance

Documentation

The biggest improvements in Json.NET 7.0 have been to user documentation. The old documentation design with its 2003 era HTML (iframes + inline JavaScript) has been replaced with a lightweight, fast to load design.

Old and Busted (left) vs New Hotness (right):

It takes two to lie: one to lie and one to listen   It's Patty who chose a life of celibacy. Selma had celibacy thrust upon her

It has been a couple of years since the docs were properly updated. New features added since then like extension data, annotations and JSONPath now have documentation and code samples. The new code samples brings the total up to 116!

Finally the documentation has been professionally proofread. My most embarrassing grammatical errors have been fixed.

NuGet Logo

Json.NET has a NuGet logo. Check it:

Kill my boss? Do I dare live out the American dream?

DiscriminatedUnionConverter performance improvements

Json.NET’s F# discriminated union support has been rewritten. Serializing very large collections of large discriminated unions was noticeably slow. The new implementation caches reflection and type data, and significantly improves performance.

What’s the point of going out? We’re just gonna wind up back home anyway.

That’s a 3200% improvement. If you’re using F# then you don’t need to do anything other than update Json.NET.

And everything else

Json.NET 7.0 includes 30 changes from 6 months of user feature requests and bug reports.

Changes

Here is a complete list of what has changed since Json.NET 6.0 Release 8.

  • New feature - DiscriminatedUnionConverter performance improvements
  • New feature - Added JsonRequiredAttribute
  • New feature - Added JsonSerializerSettings.ReferenceResolverProvider property
  • New feature - Added DefaultContractResolver.ResolveDictionaryKey
  • New feature - Added JsonDictionaryContract.DictionaryKeyResolver
  • New feature - Added support for reading GUID strings as bytes in JsonTextReader
  • New feature - Added EqualityComparer to JsonSerializer
  • Change - Changed reading GUIDs as bytes to only support 00000000-0000-0000-0000-000000000000 format
  • Change - Renamed aspnetcore50 target to dnxcore50
  • Change - Marked JsonSchema as obsolete
  • Change - Marked DefaultContractResolver(bool) as obsolete
  • Change - Marked JsonSerializerSettings.ReferenceResolver as obsolete
  • Change - Marked JsonDictionaryContract.PropertyNameResolver as obsolete
  • Fix - Fixed deserializing empty strings in Hashtables
  • Fix - Fixed incorrect JTokenReader.Path in certain situations
  • Fix - Fixed error when serializing certain objects in medium trust
  • Fix - Fixed deserializing large nullable UInt64 values
  • Fix - Fixed writing large UInt64 JValues
  • Fix - Fixed converting unmatched namespace prefixes in JSON to XML
  • Fix - Fixed IsoDateTimeConverter on DateTime properties with DateTimeFormatHandling.DateTimeOffset
  • Fix - Fixed preserving object references with read only properties
  • Fix - Fixed error when deserializing large JSON integers to XML
  • Fix - Fixed serializing extension data properties with no setter
  • Fix - Fixed serializing discriminated unions with type name or reference tracking enabled
  • Fix - Fixed DataTableConverter not using JsonSerializer settings
  • Fix - Fixed resolving properties from nested interfaces
  • Fix - Fixed deserializing classes derived from ConcurrentDictionary
  • Fix - Fixed passing default values to constructors
  • Fix - Fixed serializing root references from JsonConverters
  • Fix - Fixed empty strings coerced to null not erroring with Required.Always
  • Fix - Fixed invalid Required.Always error with constructor property name casing
  • Fix - Fixed empty string coerce check with Required.Always and constructor

Links

Json.NET GitHub Project

Json.NET 7.0 Release 1 Download - Json.NET source code and assemblies

Json.NET 6.0 Release 7 - LINQ to JSON Annotations

Annotations

This release of Json.NET adds annotations to LINQ to JSON. Annotations allow you to associate arbitrary objects with LINQ to JSON JObjects, JArrays and JValues.

Annotations aren’t part of the JSON specification; they aren’t read from JSON or written to JSON. Annotations are for use within an application.

JObject o = JObject.Parse(@"{
    'name': 'Bill G',
    'age': 58,
    'country': 'United States',
    'employer': 'Microsoft'
}");
 
o.AddAnnotation(new HashSet<string>());
o.PropertyChanged += (sender, args) => o.Annotation<HashSet<string>>().Add(args.PropertyName);
 
o["age"] = 59;
o["employer"] = "Bill & Melinda Gates Foundation";
 
 
HashSet<string> changedProperties = o.Annotation<HashSet<string>>();
// age
// employer

In this example we use annotations to track changes to a JObject. First a set of strings for is associated with a JObject using annotations. The PropertyChanged event is then used to add a property name to the set whenever its value is changed. The JObject’s changed properties are now easily accessible anywhere in your application from the JObject.

Changes

Here is a complete list of what has changed since Json.NET 6.0 Release 6.

  • New feature - Added Annotations to LINQ to JSON
  • New feature - Added DescendantsAndSelf method to JObject and JArray
  • New feature - Added AncestorsAndSelf method to JToken
  • New feature - Added support for tracking references in ISerializable objects
  • New feature - Added CurrentToken to JTokenReader
  • New feature - Added CurrentToken to JTokenWriter
  • New feature - Added WriteToken(JsonToken, object) to JsonWriter
  • Fix - Fixed deserializing null values onto JObject and JArray properties
  • Fix - Fixed error when extension data bag doesn't inherit from Dictionary<TKey, TValue>
  • Fix - Fixed deserializing complex values inside multi-dimensional arrays
  • Fix - Fixed serialization settings not being used when deserializing F# unions
  • Fix - Fixed MetadataTypeAttribute not being found on some platforms
  • Fix - Fixed line breaks in exception messages to use Environment.NewLine
  • Fix - Fixed deserializing certain XElements with comments
  • Fix - Fixed casting JValues with a negative value to a nullable SByte

Links

Json.NET GitHub Project

Json.NET 6.0 Release 7 Download - Json.NET source code and assemblies

Json.NET 6.0 Release 6 - ASP.NET CoreCLR Support, Memory Usage Optimizations

ASP.NET CoreCLR

Json.NET now supports running on the ASP.NET CoreCLR, a coming soon server optimized CLR for running applications in the cloud.

Today, you run ASP.NET using the same CLR that desktop apps use. We’re adding a cloud-optimized (my cloud, your cloud, their cloud - server stuff) version optimized for server scenarios like low-memory and high-throughput.

ASP.NET vNext will let you deploy your own version of the .NET Framework on an app-by-app-basis. One app with new libraries can’t break an app next door with a different version. Different apps can even have their own cloud-optimized CLR of their own version. The CLR and cloud-optimized libraries are NuGet packages!

Bin deploy ASP.NET to a Mac or Linux server? Sign. Me. Up. Find out more about the ASP.NET CoreCLR and ASP.NET vNext here.

Memory Usage Optimizations

This release of Json.NET optimizes memory usage, in particular heap allocations when reading and writing JSON.

Json.NET has always been memory efficient, streaming the reading and writing large documents rather than loading them entirely into memory, but I was able to find a couple of key places where object allocations could be reduced. Deserialization saw the biggest improvement with about a 35% decrease in allocations. Less allocations, less garbage collection. Less garbage collection, more requests per second.

The before memory timeline when deserializing a 5 megabyte JSON file:

I have asked you nicely not to mangle my merchandise. You leave me no choice but to ask you nicely again.

And the after timeline:

I've gotten word that a child is using his imagination... and I've come to put a stop to it

Grey is unmanaged memory, blue is the Gen0 heap, red is Gen1, green is Gen2 and the profiler used is dotMemory.

For comparison, here is what JavaScriptSerializer looks like doing the same work:

All my life I've been an obese man trapped inside a fat man's body.

JavaScriptSerializer only works with strings so the purple here is a 5 megabyte string being loaded into the large object heap. After the latest optimizations Json.NET allocates 8 times less memory than JavaScriptSerializer.

Changes

Here is a complete list of what has changed since Json.NET 6.0 Release 5.

  • New feature - Added support for ASP.NET CoreCLR
  • New feature - Reduced memory allocations when reading and writing JSON
  • New feature - Added support for passing arguments to JsonConverters with JsonConvertAttribute
  • New feature - Added JsonConvert.ToString overload that takes a StringEscapeHandling parameter
  • Change - Omit fields array for F# discriminated union serialization when there are no fields
  • Change - Escape property names in path on readers/writers/tokens when a name contains special characters
  • Change - Provide line numbers for end tokens on JTokenReader
  • Fix - Fixed parsing in SelectToken when the path has an escaped property followed by an unescaped property
  • Fix - Fixed error when deserializing a GUID from certain BSON
  • Fix - Fixed null reference error when using a JEnumerable created with its default constructor
  • Fix - Fixed line breaks in exception messages to use Environment.NewLine
  • Fix - Fixed MetadataTypeAttribute reflection error on ASP.NET CoreCLR
  • Fix - Fixed immutable collections reflection error on ASP.NET CoreCLR

Links

Json.NET GitHub Project

Json.NET 6.0 Release 6 Download - Json.NET source code and assemblies