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 brand new 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 deploying 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 meg JSON file:

image

And the after timeline:

image

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

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

Json.NET 6.0 Release 4 - JSON Merge, Dependency Injection

JSON Merge

The most visible new feature in this release is the ability to quickly merge JSON using the Merge method added to JObject and JArray.

JObject o1 = JObject.Parse(@"{
  'FirstName': 'John',
  'LastName': 'Smith',
  'Enabled': false,
  'Roles': [ 'User' ]
}");
JObject o2 = JObject.Parse(@"{
  'Enabled': true,
  'Roles': [ 'User', 'Admin' ]
}");
 
o1.Merge(o2, new JsonMergeSettings
{
    // union array values together to avoid duplicates
    MergeArrayHandling = MergeArrayHandling.Union
});
 
string json = o1.ToString();
// {
//   "FirstName": "John",
//   "LastName": "Smith",
//   "Enabled": true,
//   "Roles": [
//     "User",
//     "Admin"
//   ]
// }

The logic for combining JSON objects together is fairly simple: name/values are copied across, skipping nulls if the existing property already has a value. Arrays are a bit more tricky in how they can be merged so there is a setting for you to specify whether arrays should be concatenated together, unioned, merged by position or completely replaced.

Dependency Injection

The low-level ConstructorInfo properties on JsonObjectContract, used when creating objects during deserialization, are now obsolete and have been replaced with functions. Also Json.NET no longer immediately throws an exception if it tries to deserialize an interface or abstract type. If you have specified a way for that type to be created, such as resolving it from a dependency inject framework, then Json.NET will happily continue deserializing using that instance.

These changes combined make using Json.NET with dependency inject frameworks like Autofac, Ninject and Unity much simpler.

public class AutofacContractResolver : DefaultContractResolver
{
    private readonly IContainer _container;
 
    public AutofacContractResolver(IContainer container)
    {
        _container = container;
    }
 
    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        JsonObjectContract contract = base.CreateObjectContract(objectType);
 
        // use Autofac to create types that have been registered with it
        if (_container.IsRegistered(objectType))
            contract.DefaultCreator = () => _container.Resolve(objectType);
 
        return contract;
    }
}

Performance Improvements

There have been a lot of small performance improvements across Json.NET. All reflection is now cached or compiled into dynamic IL methods, large XML documents are converted to JSON much faster and JObject memory usage has been reduced.

Changes

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

  • New feature - Added Merge to LINQ to JSON
  • New feature - Added JValue.CreateNull and JValue.CreateUndefined
  • New feature - Added Windows Phone 8.1 support to .NET 4.0 portable assembly
  • New feature - Added OverrideCreator to JsonObjectContract
  • New feature - Added support for overriding the creation of interfaces and abstract types
  • New feature - Added support for reading UUID BSON binary values as a Guid
  • New feature - Added MetadataPropertyHandling.Ignore
  • New feature - Improved performance of KeyValuePairConverter
  • New feature - Improved performance when serializing large XML documents
  • Change - Limited integer parsing size to JavaScript integer size
  • Change - Validation that numbers don't end with an invalid character
  • Fix - Fixed JToken.ReadFrom creating a string value for a comment
  • Fix - Fixed relying on Dictionary order when calling parameterized constructors
  • Fix - Fixed writing new lines to use TextWriter.WriteLine
  • Fix - Fixed deserializing non-generic IReadOnlyCollection<T> implementations
  • Fix - Fixed sending HTTP requests when resolving DTD urls in XmlNodeConverter
  • Fix - Fixed populating ignored properties with DefaultValueHandling.IgnoreAndPopulate
  • Fix - Fixed not throwing JsonReaderException when parsing some invalid numbers
  • Fix - Fixed JsonConvert.PopulateObject not setting JsonReader settings
  • Fix - Fixed deserializing JObjects starting with a comment
  • Fix - Fixed using DateParseHandling and FloatParseHandling with DataTable/DataSet
  • Fix - Fixed serializing static fields
  • Fix - Fixed selecting a property after an empty array with a JSON Path query
  • Fix - Fixed error handling when last value in array or object fails
  • Fix - Fixed directly serializing XmlElements
  • Fix - Fixed incorrect NuGet targets on .NET 4.5 portable assembly
  • Fix - Fixed using JTokenEqualityComparer with JProperties that have no value
  • Fix - Fixed MetadataPropertyHandling.ReadAhead bugs

Links

Json.NET GitHub Project

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

Json.NET 6.0 Release 3 - Serialize All The F#

MOAR F#

Json.NET 6.0 added support for F# discriminated unions - this release adds support for F# collections. F# lists, sequences, sets and maps now serialize and deserialize automatically.

type Movie = {
    Name : string
    Year: int
}
 
[<EntryPoint>]
let main argv = 
 
    let movies = [
        { Name = "Bad Boys"; Year = 1995 };
        { Name = "Bad Boys 2"; Year = 2003 }
    ]
 
    let json = JsonConvert.SerializeObject(movies)
 
    let deserializedMovies = JsonConvert.DeserializeObject<Movie list>(json)
 
    deserializedMovies |> List.iter (fun x -> printfn "Name: %s, Year: %d" x.Name x.Year)
    // Name: Bad Boys, Year: 1995
    // Name: Bad Boys 2, Year: 2003
 
    Console.ReadKey() |> ignore
    0

To all future creators of immutable .NET collections: If your collection of T has a constructor that takes IEnumerable<T> then Json.NET will automatically work when deserializing to your collection, otherwise you're all out of luck.

Metadata Property Handling

Some Json.NET serializer features like preserving types or references require Json.NET to read and write metadata properties, e.g. $type, $id and $ref. Because of the way Json.NET deserialization works these metadata properties have had to be ordered first in a JSON object. This can cause problems because JSON object properties can't be ordered in JavaScript and some other JSON frameworks.

This release adds a new setting to allow metadata properties to be located anywhere in an object: MetadataPropertyHandling.ReadAhead

string json = @"{
    'Name': 'James',
    'Password': 'Password1',
    '$type': 'MyNamespace.User, MyAssembly'
}";
 
object o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All,
    // $type no longer needs to be first
    MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
});
 
User u = (User)o;
 
Console.WriteLine(u.Name);
// James

Internally this setting will instruct the serializer to load the entire JSON object into memory. Metadata properties will then be read out of the object, and then deserialization will continue as normal. There is a slight cost in memory usage and speed but if you require a feature that uses metadata properties and can't guarantee JSON object property order then you will find this useful.

And The Rest

DateFormatString is now used as a fallback when parsing dates during deserialization, lots of bug fixes, and a couple of small but significate performance improvements.

Changes

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

  • New feature - Added MetadataPropertyHandling
  • New feature - Added support for reading MS format JSON dates to ReadAsDateTime
  • New feature - Added support for serializing F# lists, sets and maps
  • New feature - Added support for XML document type
  • Change - Blank XML elements will be written as an empty string instead of null
  • Change - JValue with a null value will be written as null instead of empty string
  • Change - DateFormatString is now used when reading JSON
  • Fix - Fixed deserializing null values with extension data
  • Fix - Fixed converting certain XML namespaces to JSON
  • Fix - Fixed error with whitespace only JSONPath
  • Fix - Fixed property query path that starts with $
  • Fix - Fixed array query path with open brace after $
  • Fix - Fixed parsing certain JSON with comments into a JObject
  • Fix - Fixed bug where matching JSONPath incorrectly raises an error
  • Fix - Fixed non-public base class properties being used instead of child class properties
  • Fix - Fixed hiding generic properties sometimes not being detected
  • Fix - Fixed potential race condition serializing F# objects
  • Fix - Fixed schema divisible sometimes incorrectly validating to false
  • Fix - Fixed not calling virtual ShouldSerialize methods
  • Fix - Fixed invalid cast with DateParseHandling.DateTimeOffset and IsoDateTimeConverter
  • Fix - Fixed StringEnumConverter thread safety
  • Fix - Fixed using FloatParseHandling.Decimal with XmlNodeConverter
  • Fix - Fixed using DateParseHandling.DateTimeOffset with XmlNodeConverter
  • Fix - Fixed type name handling when a property already has a base type assigned

Links

Json.NET GitHub Project

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

Json.NET 6.0 Release 1 - JSONPath and F# Support

JSONPath

Json.NET has supported basic path queries with SelectToken since forever. Json.NET 6.0 supes up SelectToken with full support for JSONPath, an XPath like querying language for JSON.

JObject o = JObject.Parse(@"{
  ""Manufacturers"": [
    {
      ""Name"": ""Acme Co"",
      ""Products"": [
        {
          ""Name"": ""Anvil"",
          ""Price"": 50
        }
      ]
    },
    {
      ""Name"": ""Contoso"",
      ""Products"": [
        {
          ""Name"": ""Elbow Grease"",
          ""Price"": 99.95
        },
        {
          ""Name"": ""Headlight Fluid"",
          ""Price"": 4
        }
      ]
    }
  ]
}");
 
// manufacturer with the name 'Acme Co'
var acme = o.SelectToken("$.Manufacturers[?(@.Name == 'Acme Co')]");
 
Console.WriteLine(acme);
// { "Name": "Acme Co", Products: [{ "Name": "Anvil", "Price": 50 }] }

A SelectTokens (plural) method has been added for returning a range of results from a JSONPath query.

// name of all products priced 50 and above
var pricyProducts = o.SelectTokens("$..Products[?(@.Price >= 50)].Name");
 
Console.WriteLine(pricyProducts);
// Anvil
// Elbow Grease

While LINQ to JSON offers more features and flexibility, JSONPath being string based makes it a good choice for persisting a queries or constructing dynamic queries.

F# Support

Json.NET 6.0 adds support for serializing and deserializing F# discriminated unions. There is nothing you need to do, F# discriminated unions will now Just Work.

type Shape =
    | Rectangle of width : float * length : float
    | Circle of radius : float
    | Empty
 
[<EntryPoint>]
let main argv = 
 
    let shape1 = Rectangle(1.3, 10.0)
 
    let json = JsonConvert.SerializeObject(shape1)
    // {
    //   "Case": "Rectangle",
    //   "Fields": [
    //     1.3,
    //     10.0
    //   ]
    // }
 
    let shape2 = JsonConvert.DeserializeObject<Shape>(json)
 
    Console.ReadKey() |> ignore
    0

Assembly Version Happenings

Json.NET has had a static assembly version since 4.5 to avoid binding redirects. The problem with having a static assembly version is if a strongly named assembly with the same version number is found in the GAC, the GAC version will be used ahead for the /bin version. Some people have been encountering the problem that their applications break when someone else GACs an old Json.NET 4.5 on their server. I’m looking at you .NET CMSes.

The plan going forward is to increase the assembly version with major Json.NET releases. 6.0 Release 1 –> 6.0.0.0, 6.0 Release 2 –> 6.0.0.0, 7.0 Release 1 –> 7.0.0.0. Hopefully this will provide a balance between binding redirects and having the GAC ruin your day.

And The Rest

Tons of smaller features like parsing single line comments in JSON, reading multiple pieces of JSON content from a stream with one JsonReader, obsoleting of bad methods and lots of bug fixes.

Changes

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

  • New feature - Added support for JSONPath
  • New feature - Added support for serializing F# discriminated unions
  • New feature - Added support for deserializing nested DataTables and arrays in DataTables
  • New feature - Added support for reading multiple pieces of JSON with a JsonReader
  • New feature - Added AllowIntegerValues setting to StringEnumConverter
  • New feature - Added Decimal and DateTimeOffset constructors to JValue
  • New feature - Added support for reading JSON single line comments
  • New feature - Improved number parsing error messages
  • Change - Changed assembly version to 6.0.0.0
  • Change - .NET 4 Portable build targets MonoTouch and MonoDroid in NuGet package
  • Change - .NET 4 Portable build targets WP8 and SL5 instead of WP7 and SL4
  • Removed - DefaultMemberSearchFlags on DefaultContractResolver is obsolete
  • Removed - SerializeObjectAsync, DeserializeObjectAsync, PopulateObjectAsync on JsonConvert are obsolete
  • Fix - Fixed JObject ICustomTypeDescriptor properties returning incorrect value
  • Fix - Fixed error when casting dynamic base64 string to byte array
  • Fix - Fixed EntityKeyMemberConverter not using property name resolve
  • Fix - Fixed serializing JValues with readonly JsonConverters
  • Fix - Fixed formatting override on SerializeObject methods
  • Fix - Fixed error when wrapping an exception in a JsonConverter
  • Fix - Fixed using extension data with a non-default constructor
  • Fix - Fixed Uri serialization roundtripping with Uri.OriginalString
  • Fix - Fixed TypeNameHandling.Auto with JsonSerializer.Serialize inside a JsonConverter

Links

Json.NET GitHub Project

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