Serialization with System.Text.Json

Posted by

System.Text.Json’s serialization APIs aren’t all that different from Json.NET, but there are a few differences that make it more practical than its predecessor. This post explores how those options can help you be more effective when working with JSON.

This is the third post in a series on System.Text.Json. The other posts are:

Overview

There are three different ways to serialize data with System.Text.Json. The one you use depends on the type of data that you’re working with.

  • The JsonSerializer class is how you serialize POCOs to formatted JSON. It’s very similar to Newtonsoft’s JsonConvert, with different defaults and a few specialized implementations.
  • JsonDocument‘s API is primarily for reading documents for which you don’t know the structure, but you can still write all or parts of that JSON document back out, as need be.
  • The Utf8JsonWriter is the class pulling all the strings behind the scenes. You can also make use of its API directly to control every aspect of how to write a JSON document.

Let’s look at each of these approaches in more detail.

JsonSerializer

The JsonSerializer has three methods for serializing an object to JSON, giving you the flexibility to use the one that best fits your scenario.

The most commonly used method, Serialize, does what you’d expect. It takes a POCO and creates a string representation of that object in JSON. For example, I most frequently use Serialize to create a JSON body for HTTP requests:

var content = JsonSerializer.Serialize<FlightPlan>(flightPlan);
await httpClient.PostAsync(requestUri, new StringContent(content, Encoding.UTF8, ApplicationJsonContentType));

In Json.NET, I would have used SerializeObject to create JSON meant for a file on disk or messaging platform like Azure Service Bus. Luckily, there is a more efficient way to do that with System.Text.Json. The SerializeAsync method exists to write JSON asynchronously to a stream. It avoids a string allocation and writes the serialized object to the stream without blocking. You should use it anytime you’re working with something that supports a stream:

using Stream writer = new FileStream("flightplan.json", FileMode.OpenOrCreate);
await JsonSerializer.SerializeAsync(writer, flightPlan);

SerializeToUtf8Bytes creates a byte array that represents the JSON string. You won’t need this method too often, but it can come in handy for specialized handling. As an example, I’ve written the JSON out as a base64 string:

var result = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes<FlightPlan>(flightPlan);
var base64Result = Convert.ToBase64String(result);

You should use JsonSerializer ‘s Serialize methods when

  • You have a POCO that you need to transform to JSON.
  • You need to write the JSON to a string, stream, or raw bytes for further manipulation.

JsonDocument (via Utf8JsonWriter)

The JsonDocument API is useful for reading JSON documents that are either in an unknown format or are too big to deserialize into a POCO. It might seem like an API that’s meant for reading JSON wouldn’t need to write JSON, but there are a few scenarios where it proves useful.

A JsonDocument is composed of any number of JsonElements, starting with the root element. Every JsonElement has a GetRawText and WriteTo method. GetRawText return the original JSON string representation of the JsonElement‘s value. It can be used for simple cases where you need part of a JSON object in a string:

var waypointsJson = flightPlans.RootElement.GetProperty("Revised").GetProperty("Waypoints").GetRawText();
// Do something with waypointsJson

The more useful method is WriteTo, which takes a Utf8JsonWriter as a parameter. It opens up a few options, most notably to create a new JSON from existing elements of the JsonDocument. The example below is a bit contrived, but shows the power of the WriteTo method to combine element into a new JSON:

// Read a large JSON string into a JsonDocument
var flightPlans = JsonDocument.Parse(JsonFlightPlans());

// Prepare the destination for the new JSON document we're going to create
using Stream waypointsStreamWriter = new FileStream("allwaypoints.json", FileMode.OpenOrCreate);
var utf8MemoryWriter = new Utf8JsonWriter(waypointsStreamWriter);

// Create the new object
utf8MemoryWriter.WriteStartObject();

// Add the initial waypoints of the flight plan by accessing the property and writing it to the writer
utf8MemoryWriter.WritePropertyName("InitialWaypoints");
flightPlans.RootElement.GetProperty("Initial").GetProperty("Waypoints").WriteTo(utf8MemoryWriter);

// Add the revised waypoints in the same way as the initial waypoints
utf8MemoryWriter.WritePropertyName("RevisedWaypoints");
flightPlans.RootElement.GetProperty("Revised").GetProperty("Waypoints").WriteTo(utf8MemoryWriter);
utf8MemoryWriter.WriteEndObject();

utf8MemoryWriter.Flush();

The JsonDocument and Utf8JsonWriter APIs can be a powerful tool when combined to create JSON. It’s not something you’ll need every day, but it’s nice to know the basic building blocks are there when you need them.

You should use JsonDocument and JsonElement serialization methods when

  • You’re already working with a JsonDocument.
  • You want to build a JSON document based on the content of an existing JSON document.

Utf8JsonWriter

There is a Utf8JsonWriter at work under the covers of both JsonSerializer and JsonDocument‘s serialization methods. You can be more productive working with the higher level abstractions of JsonSerializer and JsonDocument, while still taking advantage of the performance of Utf8JsonWriter.

Utf8JsonWriter also opens up the ability to handle your own custom serialization needs, such as creating a standardized JSON format for use within your applications. The options are endless, and the best part is that you don’t need to build everything from scratch, since you can leave the writing of valid JSON to Utf8JsonWriter.

You should use Utf8JsonWriter when

  • You want to create JSON from scratch.
  • You want to build a custom JSON serializer but not worry about every single edge case of writing valid JSON.

Summary

There are a few different ways to serialize JSON data with System.Text.Json. The method you chose depends on what you have, and what you’re doing with the serialized data. In most cases, JsonSerializer is what you’re looking for, but there are times where you’ll need to use JsonDocument or Utf8JsonWriter to handle more funky scenarios.

With the basics of serialization and deserialization covered, it’s now time to learn how ASP.NET Core interacts with System.Text.Json. That’ll be the topic of the next post, arriving in early December.

One comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s