Just a couple of days ago, .NET Core 3 got finally released. One of the things that got us the most excited, is the brand-new System.Text.JSON namespace. Like many good .NET Citizens, we've been using the excellent Json.NET. Is it time to jump ship yet?

TLDR: Nope. Read on to find out why.

System.Text.Json Demo Image
Could Nostradamus have seen this coming?

(On an unrelated note. If you were wondering. While we at Codista work on Python projects most of the time, we also have a soft spot for .NET. Especially since the inception of the cross-platform .NET Core branch)

Using the new System.Text.Json

There is not much documentation on the new namespace on the internet yet. Except, for a helpful little introduction blog post and video on the Microsoft Devblog.

Before we dive in, I want to briefly summarize some important points from the video. They provide some good context to better understand design-decisions and trade-offs:

  • The aim of the new module is to provide a solid base for all kinds of scenarios
  • It is not a goal to have feature-parity with Json.NET
  • The code is heavily optimized for parsing and serializing, not navigating.
  • This is very much an MVP (minimal viable product). The idea was to get it out and develop further based on feedback. Everyone is encouraged to give that in the dotnet/corefx repo on Github. That's also where one can inspect the roadmap.

Let's continue with some actual coding. Sadly, the examples in the blogpost did not work for me. According to the video, the content was written and recorded for pre-release #6. They probably changed the API on the way to the final release. As time and time again, a helpful post by the almighty Hanselman saved me quite a few headaches. Honestly, I was not able to figure out what I did wrong by looking at the docs 🤷🏼‍♂️

Here's what did not work for me:

// using System.Text.Json;

class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
}

var forecast = new WeatherForecast()
{
    Date = DateTimeOffset.MaxValue,
    TemperatureC = 28,
    Summary = "Nostradamus says."
};

Console.WriteLine(JsonSerializer.ToString<WeatherForecast>(forecast));

Visual Studio would loudly complain that ToString is a non-generic method that, rightfully, cannot be used with type arguments.

The actual working code, thanks to Mr. Hanselman, looks like this:

var forecast = new WeatherForecast()
{
    Date = DateTimeOffset.MaxValue,
    TemperatureC = 28,
    Summary = "Nostradamus says."
};

Console.WriteLine(JsonSerializer.Serialize<WeatherForecast>(forecast));

Note how we are using Serialize instead of ToString now. Producing this nice output (prettified for your reading pleasure, default output is compact):

{
    "Date": "9999-12-31T23:59:59.9999999+00:00",
    "TemperatureC": 28,
    "Summary": "Nostradamus says."
}

Now, hold on. This is all fine and dandy, but I already know that any non-C# coder will complain about the PascalCasing of the property names.

Making System.Text.Json output camelCase

Thank whoever or whatever (pasta, anyone?) you believe in. This module supports naming conventions!

Alright, alright, at the moment there is only camelCasing, but the interface is there to go crazy with whatever you can dream of. Which is more than you can say of other modules/languages, where one has to invent ugly workarounds.

We are not going crazy here now, just a little snippet to showcase the camelcased output:

var forecast = new WeatherForecast()
{
    Date = DateTimeOffset.MaxValue,
    TemperatureC = 28,
    Summary = "Nostradamus says."
};

var serializerOptions = new JsonSerializerOptions()
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

Console.WriteLine(JsonSerializer.Serialize<WeatherForecast>(forecast, serializerOptions));

All the JavaScript Developers out there will surely appreciate you giving them this kind of JSON:

{
    "date": "9999-12-31T23:59:59.9999999+00:00",
    "temperatureC": 28,
    "summary": "Nostradamus says."
}

The JsonSerializerOptions Class supports a ton of other things as well. This is were one can notice that this has been written from the ground up, in the year 2019 and with non-standard scenarios in mind. Why? Glad you asked. We get to have support for:

  • Comments. So, JSONC support.
  • Trailing commas. Honestly, this is the only language I know which has support for this in a builtin module.
  • Case insensitive property name handling.
  • and some more…

Why it is not ready for prime-time yet

If you need the performance gains (memory and time-wise), go ahead and migrate. However, be aware of the following:

  • System.Text.Json does not yet have an equivalent to Json.NET's [JsonObject(MemberSerialization.OptIn)] attribute. Personally, we like things explicit. So this attribute was always a great tool to not accidentally put something into a data dump.
  • It does not yet support fields. This is mentioned in the video at around the 5:30 time mark. Note that it was also mentioned that this might come sooner or later, depending on feedback.
  • When only partially parsing JSON, there does not seem to be an equivalent to .ToObject<T>(), yet.

Most of these choices make sense when you consider the (non-)goals I briefly mentioned in the intro. This really is a MVP. An impressive one, but still an MVP.

Especially the last point however hurts my enthusiasm to migrate a bit. In some cases we rely heavily on only processing parts of API JSON responses. While you can do that with System.Text.Json, thanks to the great JsonDocument class, I was not able to find an equivalent to "hey, that bit of data you have there, please de-serialize this into an instance of type T". It is really great for convenience. To be honest, I am not quite sure that I haven't missed something there. If I did, feel free to contact us on Twitter. At least in one project this is the only reason I was not willing to move to the new namespace.

That being said, it is awesome to finally have really performant and builtin support for JSON. I am curious to see how the namespace will further develop in coming versions.

Did you enjoy this blog post? Yeah? How about you give… No just joking. Happy if you did find it useful.

Other Posts

  • How I became a member of Codista

    My “6 months anniversary” at Codista is coming up and I can’t really wrap my head around the fact that it’s already been six months since I joined Codista. My name is Angela and I’m part of the Codista team since June 2019.

  • How to build a language switcher in Wagtail / Django

    A switcher enables users to seamlessly transition between different languages. But how can we achieve this functionality in a Wagtail project? We need a way to link pages together.