Json.NET
serialization
deserialization
stream
C#

Can Json.NET serialize / deserialize to / from a stream?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Yes. Json.NET can serialize directly to a Stream and deserialize directly from one. In practice you do this by wrapping the stream with StreamReader or StreamWriter, then connecting Json.NET through JsonTextReader or JsonTextWriter.

Writing JSON Directly to a Stream

The core API is JsonSerializer.Serialize. It does not write bytes by itself. It writes JSON text through a text writer, which is why you normally combine it with StreamWriter.

csharp
1using System;
2using System.IO;
3using System.Text;
4using Newtonsoft.Json;
5
6public record Product(int Id, string Name, bool Active);
7
8var product = new Product(42, "Widget", true);
9var serializer = new JsonSerializer();
10
11using var stream = new MemoryStream();
12using (var textWriter = new StreamWriter(stream, Encoding.UTF8, 1024, leaveOpen: true))
13using (var jsonWriter = new JsonTextWriter(textWriter))
14{
15    serializer.Serialize(jsonWriter, product);
16    jsonWriter.Flush();
17}
18
19stream.Position = 0;
20using var reader = new StreamReader(stream, Encoding.UTF8);
21Console.WriteLine(reader.ReadToEnd());

Two details matter here. First, the stream stays open because leaveOpen: true is set on StreamWriter. Second, the stream position is reset before reading back the bytes.

Reading JSON From a Stream

Deserialization is the mirror image of serialization. You read bytes from a stream, decode them to text, and let JsonTextReader feed tokens into JsonSerializer.Deserialize.

csharp
1using System;
2using System.IO;
3using System.Text;
4using Newtonsoft.Json;
5
6public record Product(int Id, string Name, bool Active);
7
8var json = """
9{"Id":42,"Name":"Widget","Active":true}
10""";
11
12using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
13using var textReader = new StreamReader(stream, Encoding.UTF8);
14using var jsonReader = new JsonTextReader(textReader);
15
16var serializer = new JsonSerializer();
17var product = serializer.Deserialize<Product>(jsonReader);
18
19Console.WriteLine(product);

This is the normal pattern for files, HTTP request bodies, queue payloads, and any other JSON source that naturally arrives as a stream.

Why Streams Are Better Than Temporary Strings

If the JSON already exists in a file, network socket, or request body, reading the whole payload into a giant string first adds extra memory pressure and an unnecessary copy. Stream-based processing is usually the cleaner design because:

  • the transport already gives you a stream
  • large payloads do not need a second full in-memory representation
  • the API composes naturally with file and network code

That does not mean Json.NET becomes fully incremental by magic. If you deserialize a huge array into a List<T>, the objects still have to exist in memory. The stream only removes the extra string layer.

Streaming Large Arrays Safely

For large JSON arrays, you can read one object at a time instead of materializing everything up front. This is useful in ETL jobs and import pipelines.

csharp
1using System;
2using System.IO;
3using Newtonsoft.Json;
4
5public record Product(int Id, string Name, bool Active);
6
7var json = """
8[
9  {"Id":1,"Name":"A","Active":true},
10  {"Id":2,"Name":"B","Active":false},
11  {"Id":3,"Name":"C","Active":true}
12]
13""";
14
15using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json));
16using var textReader = new StreamReader(stream);
17using var jsonReader = new JsonTextReader(textReader);
18
19var serializer = new JsonSerializer();
20
21while (jsonReader.Read())
22{
23    if (jsonReader.TokenType == JsonToken.StartObject)
24    {
25        var item = serializer.Deserialize<Product>(jsonReader);
26        Console.WriteLine(item);
27    }
28}

This pattern is not appropriate for every schema, but it is much friendlier to memory than loading a very large JArray when all you really need is sequential processing.

Files and HTTP Bodies

The same pattern works with real streams from the filesystem or the network.

csharp
1await using var fileStream = File.Create("product.json");
2await using var streamWriter = new StreamWriter(fileStream, Encoding.UTF8);
3using var jsonWriter = new JsonTextWriter(streamWriter) { Formatting = Formatting.Indented };
4
5new JsonSerializer().Serialize(jsonWriter, new Product(7, "Desk", true));
6await streamWriter.FlushAsync();

On the read side, an ASP.NET Core controller or middleware can deserialize directly from HttpRequest.Body using the same reader stack.

Common Pitfalls

One common mistake is disposing the text writer and then trying to reuse the stream without leaveOpen: true. Another is forgetting to flush buffered writers, which can make the output look truncated.

Encoding mismatches are also easy to miss. JSON is usually UTF-8, so make that explicit. Finally, if you write to a MemoryStream and then read it back, reset Position to 0 first.

Summary

  • Json.NET can serialize to and deserialize from streams without first creating a full JSON string.
  • Use StreamWriter plus JsonTextWriter for output and StreamReader plus JsonTextReader for input.
  • Stream-based handling reduces extra memory copies and fits file and network APIs naturally.
  • Large arrays can be processed incrementally by reading one object at a time.
  • Watch disposal, flushing, encoding, and stream position carefully.

Course illustration
Course illustration

All Rights Reserved.