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:
- Part 1: Why System.Text.Json exists, major differences with Newtonsoft, and how to find help with the new library.
- Part 2: Reading JSON documents.
- Part 3 [this post]: Writing JSON documents.
- Part 4 [coming soon]: Model Binding in ASP.NET Core.
- Part 5 [coming soon]: Considerations for using System.Text.Json in a production-grade project.
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’sJsonConvert
, 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 JsonElement
s, 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.
3 comments