👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
Working with Unknown JSON Using JsonDocument in .NET

Working with Unknown JSON Using JsonDocument in .NET

Author - Abdul Rahman (Bhai)

JSON

5 Articles

Improve

Table of Contents

  1. What we gonna do?
  2. Why we gonna do?
  3. How we gonna do?
  4. Summary

What we gonna do?

Ever receive a JSON payload so big that deserializing it crashes your app? Or maybe you don't even have a class to deserialize into? JsonDocument solves both problems. In this article, we'll explore how to work with unknown or dynamic JSON structures in .NET without creating a single POCO class.

Why we gonna do?

JsonSerializer is great—until it isn't. When you have a massive API response (think 50MB of nested objects) and you only need a few specific fields, deserializing the entire payload into memory is wasteful and slow. Worse yet, what if the JSON schema keeps changing, or you're inspecting third-party data where no model exists?

That's where JsonDocument comes in. It creates an immutable, read-only Document Object Model (DOM) that lets you navigate JSON like a tree structure. You get random access to any property without loading the whole object into .NET types. The performance gains are massive: shared memory buffers, direct UTF-8 reading, and values allocated only when requested.

Think of it this way: instead of unpacking an entire shipping container to find one box, you open the container, locate the box you need, and leave everything else untouched. That's JsonDocument.

How we gonna do?

Here's how to leverage JsonDocument for high-performance JSON parsing in .NET:

Step 1: Understanding the JsonDocument Structure

The JsonDocument represents JSON as a tree. The top-level entry point is the RootElement, which is of type JsonElement . From there, every property, object, and array is also a JsonElement. Here's the hierarchy:

  • JsonDocument: The container that parses and holds the entire JSON structure
  • RootElement: The first JsonElement, representing the top-level object or array
  • JsonElement: Represents any value—object, array, string, number, boolean, or null
  • JsonProperty: A name-value pair within an object

Step 2: Parsing JSON with JsonDocument

Start by parsing your JSON string, stream, or UTF-8 bytes. Always use the using statement—JsonDocument implements IDisposable and must be properly disposed to free memory.


string jsonString = """
{
  "user": "Abdul",
  "temperature": 22,
  "readings": [15, 18, 22, 19, 21]
}
""";

using JsonDocument document = JsonDocument.Parse(jsonString);
JsonElement root = document.RootElement;

Console.WriteLine(root.GetProperty("user").GetString()); 
// Output: Abdul

The Parse method has multiple overloads—you can parse from a string, ReadOnlySpan<byte>, or Stream. For async scenarios, use ParseAsync.

Step 3: Accessing Properties Safely

Use GetProperty() when you're certain a property exists. If uncertain, use TryGetProperty() to avoid exceptions.


// Safe access with TryGetProperty
if (root.TryGetProperty("temperature", out JsonElement tempElement))
{
    int temp = tempElement.GetInt32();
    Console.WriteLine($"Temperature: {temp}°C");
}
else
{
    Console.WriteLine("Temperature property not found");
}

// Direct access (throws if property doesn't exist)
string user = root.GetProperty("user").GetString();

Step 4: Working with Arrays

JSON arrays are enumerated using EnumerateArray(). This is far more efficient than manual indexing.


JsonElement readingsArray = root.GetProperty("readings");

int total = 0;
int count = 0;

foreach (JsonElement reading in readingsArray.EnumerateArray())
{
    total += reading.GetInt32();
    count++;
}

double average = (double)total / count;
Console.WriteLine($"Average reading: {average}"); 
// Output: Average reading: 19

Step 5: Deserializing Specific Elements

One powerful feature: you can selectively deserialize only the JsonElement you need into a strongly-typed object.


public class UserProfile
{
    public string User { get; set; }
    public int Temperature { get; set; }
}

// Deserialize only the root element (or any sub-element)
UserProfile profile = JsonSerializer.Deserialize<UserProfile>(
    root.GetRawText()
);

Console.WriteLine($"User: {profile.User}, Temp: {profile.Temperature}°C");

Step 6: Customizing Parse Behavior

Use JsonDocumentOptions to control parsing. For example, allow comments or trailing commas, set max depth, or handle duplicate properties.


var options = new JsonDocumentOptions
{
    CommentHandling = JsonCommentHandling.Skip,
    AllowTrailingCommas = true,
    MaxDepth = 64
};

using JsonDocument document = JsonDocument.Parse(jsonString, options);
// Now JSON with comments and trailing commas won't throw exceptions

Step 7: Performance Best Practices

  • Always dispose: Use using statements to free memory promptly
  • Avoid unnecessary allocations: Access values only when needed
  • Use EnumerateArray(): Don't manually index arrays
  • Know your structure: Searching is sequential, so direct property access is faster
  • Prefer UTF-8: JsonDocument reads UTF-8 directly; converting from UTF-16 adds overhead
  • Use ParseAsync for I/O: When reading from streams, async prevents blocking

// Good: Async parsing from a stream
await using FileStream stream = File.OpenRead("large-data.json");
using JsonDocument document = await JsonDocument.ParseAsync(stream);

// Extract only what you need
if (document.RootElement.TryGetProperty("userId", out JsonElement userId))
{
    Console.WriteLine($"User ID: {userId.GetString()}");
}
// Everything else remains untouched in memory

Summary

JsonDocument is your go-to tool when dealing with large, unknown, or dynamic JSON in .NET. By creating an immutable DOM, it gives you fast, memory-efficient random access without deserializing entire payloads. Remember: always dispose, use TryGetProperty for safety, and leverage EnumerateArray for collections. When you need speed and flexibility, JsonDocument delivers both.

👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
  • JSON
  • JsonDocument
  • JSON
  • DOM
  • Immutable
  • Performance
  • System.Text.Json
  • JsonElement
  • RootElement