Updated on

To serialize a list to JSON in C#, pass it to JsonSerializer.Serialize(). One List<T> in, one JSON array out, and nothing to install, because System.Text.Json ships with .NET.

There are three more routes worth knowing. SerializeToUtf8Bytes() returns UTF-8 bytes instead of a string, and Newtonsoft.Json offers JsonConvert.SerializeObject() and its lower-level JsonSerializer class for projects already using that library. If the input is a single object rather than a collection, our guide on turning a single object into a JSON string is the better starting point.

We will build all four, then benchmark them on a list of 10,000 objects to see which is worth reaching for.

To download the source code for this article, you can visit our GitHub repository.

Let’s dive in.

Prepare the Environment

To begin, let’s prepare our environment by creating the list object that we will use in this article.

First, let’s define a Club class:

public class Club
{
    public string Name { get; set; } = string.Empty;
    public int YearFounded { get; set; }
    public string Country { get; set; } = string.Empty;
    public int NumberOfPlayers { get; set; }
}

Next, let’s create a List<Club> object:

List<Club> _englishClubs = new()
{
    new Club
    {
        Name = "Arsenal",
        YearFounded = 1886,
        Country = "England",
        NumberOfPlayers = 26,
    },
    new Club
    {
        Name = "Manchester City",
        YearFounded = 1880,
        Country = "England",
        NumberOfPlayers = 25,
    },
    new Club
    {
        Name = "Sunderland",
        YearFounded = 1879,
        Country = "England",
        NumberOfPlayers = 30,
    }
 };

We will use this list as the input data for all the serialization methods in this article.

How Do We Serialize a List to JSON With System.Text.Json?

System.Text.Json is the serializer that ships with .NET, so serializing a list needs no package reference at all.

One static call does the work. JsonSerializer.Serialize(clubList) takes any List<T> and returns a JSON array as a string, with one object per element.

Left alone, it writes minified JSON and keeps our property names exactly as C# declares them. Both are configurable through a JsonSerializerOptions instance: WriteIndented = true adds the line breaks, and PropertyNamingPolicy = JsonNamingPolicy.CamelCase turns YearFounded into yearFounded, which is what most JSON consumers expect.

SerializeToUtf8Bytes() is the same operation with a different return type. It hands back a byte[] already encoded as UTF-8, which is what we want when the JSON is going straight onto a network stream or into a file, because it skips the intermediate string.

We create the options object once and reuse it, because each new instance builds its own metadata cache.

Let’s define a SerializeListToJsonWithSystemTextJson class:

public class SerializeListToJsonWithSystemTextJson(List<Club> clubList)
{
}

Here, we define a class that will contain all the methods that utilize the System.Text.Json library. Its primary constructor takes the list we want to serialize, and clubList is then available to every method in the class without a separate field or constructor body.

Serialize a List With the Serialize() Method

Now, let’s take a closer look at how we can use the Serialize() method from the System.Text.Json namespace to convert a list into a JSON string in C#.

First, let’s define a SerializeMethod() method:

public string SerializeMethod()
{
    return JsonSerializer.Serialize(clubList);
}

In this method, we call the Serialize() method from the JsonSerializer class, passing in the List<T> object that we want to serialize as a parameter.

This method returns a minified (no indentation, whitespaces, and newline characters) string:

[{"Name":"Arsenal","YearFounded":1886,"Country":"England","NumberOfPlayers":26},
{"Name":"Manchester City","YearFounded":1880,"Country":"England","NumberOfPlayers":25},
{"Name":"Sunderland","YearFounded":1879,"Country":"England","NumberOfPlayers":30}]

This is the output string we desire and we can use it in this form to store data in a file or send information over a network to another application. However, it is not easily readable and the property names are not in the recommended JSON format (camelcase). Please note, in the code sample, we broke the string into several lines to enhance readability.

To address these issues, let’s define an instance of the JsonSerializerOptions class:

private readonly JsonSerializerOptions _options = new()
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    WriteIndented = true,
};

Here, we create an instance named _options and set two properties. First, we set the PropertyNamingPolicy property to JsonNamingPolicy.CamelCase. With this, when we serialize our list to JSON, it automatically converts the property names to camelcase. There is more to this setting than the built-in policy, and our article on the camelCase naming policy and its options covers the rest of it.

Next, we set the WriteIndented property to true. When we utilize this instance for serializing our list to JSON, we ensure that it indents the resulting JSON string for better readability.

When we pass this _options variable to our SerializeMethod() and invoke it, we get:

[
  {
    "name": "Arsenal",
    "yearFounded": 1886,
    "country": "England",
    "numberOfPlayers": 26
  },
  {
    "name": "Manchester City",
    "yearFounded": 1880,
    "country": "England",
    "numberOfPlayers": 25
  },
  {
    "name": "Sunderland",
    "yearFounded": 1879,
    "country": "England",
    "numberOfPlayers": 30
  }
]

As we can see, by using this instance, we can ensure that the returned JSON string is readable and follows the recommended format for property names. The version in our repository passes _options on every call, which is the form we want in real code.

It is important to note that, when we use indentation in a large JSON file, it can improve readability, especially for nested objects or arrays. However, it can also increase the file size and make transmission over a network less efficient. Therefore, we should balance readability and file size when deciding whether to use indentation in a JSON file.

Serialize a List With the SerializeToUtf8Bytes() Method

Alternatively, we can use the SerializeToUtf8Bytes() method to serialize a C# list to JSON:

public string SerializeToUtf8BytesMethod()
{
    var result = JsonSerializer.SerializeToUtf8Bytes(clubList, _options);

    return System.Text.Encoding.UTF8.GetString(result);
}

First, we invoke the JsonSerializer.SerializeToUtf8Bytes() method, passing in two arguments. The first argument is the List object clubList and the second argument is _options, an instance of the JsonSerializerOptions class created earlier in this article. We use these options to control the formatting of the resulting JSON string, such as ensuring it is readable and that property names are in camelcase.

Once the serialization is complete, we obtain the resulting byte array and convert it to a string using the System.Text.Encoding.UTF8.GetString() method. Here, we pass in the byte array as an argument and the method returns a string in UTF-8 encoding.

A list is not the only shape we serialize this way. The same calls work on a dictionary, which we cover in our article on serializing a dictionary instead of a list.

How Do We Serialize a List to JSON With Newtonsoft.Json?

Newtonsoft.Json is a NuGet package rather than part of .NET, so it is the right choice when a project already depends on it or needs something System.Text.Json does not offer.

JsonConvert.SerializeObject(clubList, _settings) is the one-line equivalent: pass the list, get a JSON array back as a string.

Formatting lives on a JsonSerializerSettings instance instead of JsonSerializerOptions. Formatting.Indented is the counterpart of WriteIndented, and camelCase names come from a DefaultContractResolver carrying a CamelCaseNamingStrategy, which is more typing than the single property System.Text.Json needs for the same result.

The JsonSerializer class is the lower-level route. We create it with JsonSerializer.Create(_settings) and write through a JsonTextWriter, which lets us serialize into a StringBuilder, a file, or any other TextWriter.

For a plain list, SerializeObject() is the one to reach for. The writer route earns its extra lines only when we control the destination.

To begin, let’s define a SerializeListToJsonWithNewtonsoftJson class:

public class SerializeListToJsonWithNewtonsoftJson(List<Club> clubList)
{
}

Here, we create a class that will contain all the methods that utilize the Newtonsoft.Json library. As before, its primary constructor takes the list we want to serialize.

Next, to ensure that our methods return the JSON string in a readable format and that the property names are in camelcase, let’s define an instance of the JsonSerializerSettings class:

private readonly JsonSerializerSettings _settings = new()
{
    Formatting = Formatting.Indented,
    ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() },
};

In this instance, we initialize two settings. First, we set the Formatting property to Formatting.Indented. Setting this causes the returned JSON string to be indented for improved readability.

Then, we set the ContractResolver property to an instance of the DefaultContractResolver class. With this, when we serialize our list to JSON, it automatically converts the property names to camelcase. Here, we set the NamingStrategy property of the DefaultContractResolver to an instance of CamelCaseNamingStrategy. We do this to convert property names to camelcase during serialization.

Serialize a List With JsonConvert.SerializeObject()

With that, let’s explore how we can use the JsonConvert.SerializeObject() method to serialize a list to JSON.

First, let’s define a SerializeObjectMethod() method:

public string SerializeObjectMethod()
{
    return JsonConvert.SerializeObject(clubList, _settings);
}

Here, we invoke the JsonConvert.SerializeObject() method, and pass in two parameters. The first parameter is the list object we want to serialize, and the second parameter is the instance of the JsonSerializerSettings class we created.

Serialize a List With the JsonSerializer Class

We’re not done yet. Let’s discuss how we can use the JsonSerializer class to convert a List<T> object to JSON:

public string JsonSerializerClass()
{
    var serializer = JsonSerializer.Create(_settings);
    var stringBuilder = new StringBuilder();
    using (var writer = new JsonTextWriter(new StringWriter(stringBuilder)))
    {
        serializer.Serialize(writer, clubList);
    }

    return stringBuilder.ToString();
}

In the JsonSerializerClass() method, we create an instance of the JsonSerializer class by passing in our _settings object as an argument. We will use this instance to serialize our list to JSON.

Next, we create a new StringBuilder object that will store our JSON string. Then we use a JsonTextWriter instance to write our serialized JSON string to the StringBuilder.

Within the using statement, we pass in the JsonTextWriter instance and our list object clubList as arguments to the serializer.Serialize() method and invoke it. This method serializes our clubList to a JSON string, which we write to the StringBuilder.

Finally, we return the JSON string from the StringBuilder by calling the ToString() method.

What’s the Fastest Way to Serialize a List to JSON in C#?

JsonSerializer.Serialize() from System.Text.Json is the fastest of the four methods and allocates the least memory.

We measure all four with BenchmarkDotNet on a list of 10,000 Club objects. Both System.Text.Json methods finish ahead of both Newtonsoft.Json methods, and Serialize() finishes ahead of SerializeToUtf8Bytes().

The memory column is worth reading separately from the timings. Note that SerializeToUtf8Bytes() is measured with a conversion back to a string inside the timed code, so that all four methods return the same type. In real code that conversion usually does not happen, because the bytes are already what the stream or the file wants.

The gap only matters at scale. For a handful of objects on a request path all four are a rounding error, and the deciding factor is which library the project already uses rather than which one wins a benchmark.

To learn more about how this library works, please visit Introduction to Benchmarking C# Projects.

To see more details of the benchmark implementation for these methods, please visit our repository.

Once we run the benchmark on a list of 10,000 Club objects, we can inspect our results on the console:

| Method                                   | Mean     | Error     | StdDev    | Allocated |
|----------------------------------------- |---------:|----------:|----------:|----------:|
| SystemTextJsonSerializeMethod            | 3.297 ms | 0.0560 ms | 0.0496 ms |   2.23 MB |
| SystemTextJsonSerializeToUtf8BytesMethod | 3.404 ms | 0.0668 ms | 0.0769 ms |   3.35 MB |
| NewtonsoftJsonJsonSerializerClass        | 4.739 ms | 0.0716 ms | 0.0635 ms |   4.95 MB |
| NewtonsoftJsonSerializeObjectMethod      | 4.800 ms | 0.0545 ms | 0.0509 ms |   4.94 MB |

Based on these results, the fastest way we can serialize a List<T> instance to JSON in C# is by calling JsonSerializer.Serialize(). This method also utilizes the least amount of memory among all the tested methods.

The SerializeToUtf8BytesMethod() method finishes a little behind it and allocates more. Remember that this harness converts its byte[] back into a string inside the measured code, so that all four methods return a string.

The JsonSerializerClass() method and the SerializeObjectMethod() method are both slower than the System.Text.Json-based methods, and they allocate roughly the same amount of memory as each other.

Here is how the four calls compare when we have to choose one:

CallLibraryReturnsReach for it when
JsonSerializer.Serialize(list, options)System.Text.JsonstringThe default, fastest of the four and the lightest
JsonSerializer.SerializeToUtf8Bytes(list, options)System.Text.Jsonbyte[]The JSON is going onto a stream, a socket, or a file
JsonSerializer.SerializeAsync(stream, list, options)System.Text.JsonTaskWriting a large list straight to its destination
JsonConvert.SerializeObject(list, settings)Newtonsoft.JsonstringThe project already depends on Newtonsoft.Json
JsonSerializer.Create(settings) + JsonTextWriterNewtonsoft.Jsonwrites into a TextWriterNewtonsoft.Json, and we own the destination

How Do We Serialize a List Straight to a File or Stream?

Serializing to a string and then writing that string makes two copies of the same data. System.Text.Json can write into a stream directly instead.

await JsonSerializer.SerializeAsync(stream, clubList, _options) takes the destination as its first argument, so a FileStream from File.Create() receives the JSON as it is produced. It flushes every DefaultBufferSize bytes, 16 KB by default, so peak memory is that buffer rather than the whole document.

There is a synchronous counterpart, Serialize(Utf8JsonWriter, value, options), for when there is no async context to await from.

This is also where SerializeToUtf8Bytes() fits. Its byte[] goes straight to stream.WriteAsync() with no encoding step in between, which is why the overload exists alongside the string one.

For a list of a few hundred items none of this is measurable. For an export running to megabytes it is the difference between one buffer and three, and that is a difference we feel as memory rather than as milliseconds.

Let’s add one more method to our System.Text.Json class:

public async Task SerializeToStreamAsync(Stream stream)
{
    await JsonSerializer.SerializeAsync(stream, clubList, _options);
}

The method takes any writable Stream and hands it to JsonSerializer.SerializeAsync() along with our list and our options. Passing a FileStream returned by File.Create() writes the JSON array to disk, and passing a MemoryStream keeps it in memory, which is how the test in our repository asserts the result.

If the JSON string already exists and only needs saving, our article on writing a JSON string into a file covers that route instead.

Conclusion

C# has two popular libraries, System.Text.Json and Newtonsoft.Json, that we can use to serialize a list to JSON, and four calls between them.

In this article, we built all four, benchmarked them on a list of 10,000 objects, and wrote a list straight to a stream. JsonSerializer.Serialize() from System.Text.Json is the one to reach for unless the project already depends on Newtonsoft.Json. For the other direction, our article on reading the JSON back into objects picks the story up from the file.

Tested with .NET 10 and Newtonsoft.Json 13.0.4.