Mastering the use of System.Text.Json
Serhii Kokhan
Microsoft MVP??CTO & .NET/Azure Architect??Stripe Certified Professional Developer??Offering MVP Development, Legacy Migration, & Product Engineering??Expert in scalable solutions, high-load systems, and API integrations
Introduction
Handling JSON data is a daily task for many developers, given its widespread use in modern applications. With the rise of .NET's System.Text.Json library, working with JSON has never been more efficient or straightforward. In this guide, we'll walk through the essentials of this library, ensuring you have the tools and knowledge to manage JSON data with ease. Whether you're new to JSON or just looking to sharpen your skills, let's dive in and see what .NET brings to the table!
Primitives
The System.Text.Json namespace in .NET equips developers with powerful classes tailored for various JSON operations, be it creation, update, deletion, or traversal. With the understanding of JsonValue, JsonObject, JsonArray, JsonDocument, and JsonNode, manipulating JSON in .NET has never been more intuitive and efficient.
JsonValue
JsonValue representing the atomic elements in the JSON, caters to the fundamental data types: numbers, strings, booleans, and null. With simple instantiation, you can encapsulate a string within the JSON paradigm. But the real beauty lies in its type safety. Implicit conversions are a cornerstone, allowing retrieval of values in their native .NET formats. While JsonValue shines for individual values, remember it's not crafted for complex structures - that's where our next primitive comes into play.
JsonValue number = JsonValue.Create(123);
JsonValue text = JsonValue.Create("Hello World");
JsonValue flag = JsonValue.Create(false);
Definition
Represents simple JSON values: numbers, strings, booleans, or null.
Best Practices
Pitfalls
Performance Benchmarks
JsonObject
Imagine needing a collection of key-value pairs in JSON. That’s precisely the domain of JsonObject. It's not just a static representation; you can dynamically add, remove, or alter these pairs even after the object's creation.
JsonObject employee = new JsonObject
{
["firstName"] = JsonValue.Create("John"),
["lastName"] = JsonValue.Create("Doe"),
["isManager"] = JsonValue.Create(false)
};
To update, simply reassign a value:
employee["isManager"] = true;
Deleting a property can be achieved using the Remove method:
employee.Remove("isManager");
Definition
Encapsulates JSON objects – key-value pairs. The methods ContainsKey and TryGetValue further enhance safety, ensuring the key's existence before any retrieval attempts. However, for pure read-only tasks, JsonObject might be overkill.
Best Practices
Pitfalls
Performance Benchmarks
JsonArray
Lists or sequences in JSON are encapsulated by JsonArray. Think of it as a dynamic list dedicated to JSON, capable of versatile operations.
JsonArray colors = new JsonArray { "red", "green", "blue" };
Addition and removal are simple operations:
colors.Add("black");
colors.Remove("red");
Definition
Represents sequential JSON arrays. It’s crucial, however, to ensure type consistency within these arrays. While they're adept for list-like structures, for more demanding performance scenarios, native .NET collections might be a better choice.
Best Practices
Pitfalls
Performance Benchmarks
JsonDocument
When it's about capturing a JSON snapshot without the intent of alteration, JsonDocument stands out. It’s a parsed, read-only rendition of the JSON content. The memory efficiency is notable since it operates within a rented buffer, reducing allocations.
using var doc = JsonDocument.Parse("{\"country\":\"Ukraine\",\"capital\":\"Kyiv\"}");
var capital = doc.RootElement.GetProperty("capital").GetString();
Definition
A read-only representation of a parsed JSON document. While its read-only nature ensures peak performance for certain tasks, it inherently means the structure is immutable. If there’s a need for modification, you might want to explore other primitives.
Best Practices
Pitfalls
Performance Benchmarks
JsonNode
At the abstract heart of System.Text.Json is JsonNode, underlying the likes of JsonValue, JsonObject, and JsonArray. It offers a unified representation of any node within a JSON structure. While developers rarely interact with it directly, understanding its foundational role can shed light on the behavior of its derivatives. Each derivative, from value to object to array, brings a tailored functionality set, ensuring developers have the right tool for the right job.
JsonNode node = new JsonObject();
node["name"] = "Doe";
For locating a particular node or value within complex JSON structures:
if(node is JsonObject obj && obj.ContainsKey("name"))
{
Console.WriteLine($"Found name: {obj["name"]}");
}
Definition
The abstract base class for JsonValue, JsonObject, and JsonArray.
领英推荐
Best Practices
Pitfalls
Performance Benchmarks
Node-to-String
ToString()
By default, each .NET object offers a ToString() method. However, in the context of JSON, it serves a specialized function.
ToString() method on JSON nodes provides a string representation of the current value. For primitive values like strings and numbers, the output is as one would expect. However, for more complex objects, the output will be formatted for readability, with proper indentation and line breaks.
var simpleValue = JsonValue.Create("Alice");
Console.WriteLine(simpleValue.ToString());
// Output: Alice
var complexValue = new JsonObject
{
["id"] = 1,
["name"] = "Bob",
["tags"] = new JsonArray { "friend", "developer" }
};
Console.WriteLine(complexValue.ToString());
/* Output:
{
"id": 1,
"name": "Bob",
"tags": [
"friend",
"developer"
]
}
*/
As seen, ToString() method ensures that more intricate objects are indented, improving human readability.
ToJsonString()
While ToString() targets readability, ToJsonString() aims for a compact and valid JSON output. This is the method to employ when you're looking for a proper JSON representation that can be transmitted over a network or stored in a database.
var simpleValue = JsonValue.Create("Alice");
Console.WriteLine(simpleValue.ToJsonString());
// Output: "Alice"
var complexValue = new JsonObject
{
["id"] = 1,
["name"] = "Bob",
["tags"] = new JsonArray { "friend", "developer" }
};
Console.WriteLine(complexValue.ToJsonString());
// Output: {"id":1,"name":"Bob","tags":["friend","developer"]}
Note that in ToJsonString(), string values are enclosed in double quotes, consistent with the JSON standard. For complex objects, spaces and line breaks are eliminated to produce a compact representation.
Customization
Beyond the basic conversion, ToJsonString() also allows for customization using JsonSerializerOptions. This is handy when you need to modify the serialized output to match certain requirements, like camel casing properties.
var data = new JsonObject
{
["FirstName"] = "Charlie",
["LastName"] = "Brown"
};
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
Console.WriteLine(data.ToJsonString(options));
// Output: {"firstName":"Charlie","lastName":"Brown"}
In the example above, the JsonSerializerOptions was used to change property names into camel case.
Tips & Tricks
System.Text.Json has established itself as a high-performance and lightweight library for JSON operations in .NET. Whether you're new to it or have been using it for a while, some insights can optimize your usage and address common challenges.
Using Property Naming Policies
Handling Null Values
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
Custom Converters for Complex Types
Opt for ReadOnlySpan<byte> for Performance
Using Utf8JsonReader for Streaming Scenarios
JsonSerializerOptions for Global Settings
Case Sensitivity
Always Dispose JsonDocument
System.Text.Json vs Newtonsoft.Json
System.Text.Json and Newtonsoft.Json are two popular libraries for handling JSON in .NET. Both have their strengths and weaknesses, which make them more or less suitable for certain scenarios.
Origin & Popularity
Performance
API & Features
Flexibility
Integration
Migration
Target Framework
Conclusion
Choosing between System.Text.Json and Newtonsoft.Json should be based on the specific needs of a project. If performance is a primary concern and the feature set of System.Text.Json meets the project's requirements, then it might be the better choice. On the other hand, projects that require a wide range of features, extensive customization, or have existing dependencies on Newtonsoft.Json might be more appropriate.
Summary
System.Text.Json offers a comprehensive suite of tools for JSON handling in .NET. By understanding the nuances and best-fit scenarios for each class, developers can write efficient, clean, and high-performing code for all their JSON needs.