Em C#, para serializar uma lista em JSON, passe-a para JsonSerializer.Serialize(). Entra uma List<T>, sai um array JSON, e não é preciso instalar nada, porque o System.Text.Json já vem com o .NET.

Há mais três caminhos que vale a pena conhecer. SerializeToUtf8Bytes() retorna bytes UTF-8 em vez de uma string, e o Newtonsoft.Json oferece JsonConvert.SerializeObject() e a classe JsonSerializer, de nível mais baixo, para projetos que já usam essa biblioteca. Se a entrada for um único objeto, e não uma coleção, o nosso guia sobre como transformar um único objeto em uma string JSON é o melhor ponto de partida.

Vamos implementar os quatro e depois medir o desempenho deles em uma lista de 10.000 objetos para ver qual compensa usar.

Para baixar o código-fonte deste artigo, você pode acessar nosso repositório no GitHub.

Vamos lá.

Preparação do ambiente

Para começar, vamos preparar o ambiente criando o objeto de lista que usaremos neste artigo. A lista vai no Program.cs de uma aplicação de console, e cada classe que escrevermos fica em um arquivo próprio.

Primeiro, vamos definir uma classe Club:

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; }
}

Em seguida, vamos criar um objeto List<Club>:

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,
    }
 };

Usaremos essa lista como dados de entrada para todos os métodos de serialização deste artigo.

Como serializar uma lista em JSON com System.Text.Json?

O System.Text.Json é o serializador que vem com o .NET, então serializar uma lista não exige referência a nenhum pacote.

Uma única chamada estática faz todo o trabalho. JsonSerializer.Serialize(clubList) recebe qualquer List<T> e retorna um array JSON como string, com um valor por elemento.

Sem nenhuma configuração, ele gera JSON minificado e mantém os nomes das propriedades exatamente como foram declarados em C#. Podemos configurar as duas coisas com uma instância de JsonSerializerOptions: WriteIndented = true adiciona as quebras de linha e PropertyNamingPolicy = JsonNamingPolicy.CamelCase transforma YearFounded em yearFounded, que é o que a maioria dos consumidores de JSON espera.

SerializeToUtf8Bytes() é a mesma operação com outro tipo de retorno. Ele retorna um byte[] já codificado em UTF-8, que é o que queremos quando o JSON vai direto para um stream de rede ou para um arquivo, porque evita a string intermediária.

Criamos o objeto de opções uma única vez e o reutilizamos, porque uma nova instância pode significar um novo cache de metadados.

Vamos definir uma classe SerializeListToJsonWithSystemTextJson:

using System.Text.Json;

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

Aqui, definimos uma classe que vai conter todos os métodos que usam a biblioteca System.Text.Json. O construtor primário recebe a lista que queremos serializar, e clubList fica disponível em todos os métodos da classe, sem precisar de um campo separado nem de um corpo de construtor.

Serializar uma lista com o método Serialize()

Agora, vamos ver com mais detalhes como usar o método Serialize() do namespace System.Text.Json para converter uma lista em uma string JSON em C#.

Primeiro, vamos definir um método SerializeMethod():

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

Nesse método, chamamos o método Serialize() da classe JsonSerializer, passando como parâmetro o objeto List<T> que queremos serializar.

Esse método retorna uma string minificada (sem indentação, espaços em branco nem caracteres de nova linha):

[{"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}]

Essa é a string de saída que queremos, e podemos usá-la assim para armazenar dados em um arquivo ou enviar informações pela rede para outra aplicação. No entanto, ela não é fácil de ler, e os nomes das propriedades não estão no formato recomendado para JSON (camelCase). Observe que, no exemplo de código, quebramos a string em várias linhas para facilitar a leitura.

Para resolver esses problemas, vamos criar uma instância da classe JsonSerializerOptions:

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

Aqui, criamos uma instância chamada _options e definimos duas propriedades. Primeiro, definimos a propriedade PropertyNamingPolicy como JsonNamingPolicy.CamelCase. Com isso, quando serializamos a lista em JSON, os nomes das propriedades passam automaticamente para camelCase. A nomenclatura camelCase vai além dessa configuração, e o nosso artigo sobre a política de nomenclatura camelCase e suas opções explica o restante.

Em seguida, definimos a propriedade WriteIndented como true. Quando usamos essa instância para serializar a lista em JSON, garantimos que a string JSON resultante seja indentada, o que facilita a leitura.

Quando passamos a variável _options para a chamada a Serialize() dentro de SerializeMethod() e executamos esse método, obtemos:

[
  {
    "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
  }
]

Como podemos ver, com essa instância garantimos que a string JSON retornada seja legível e siga o formato recomendado para nomes de propriedades. A versão do nosso repositório passa _options em todas as chamadas, que é a forma que queremos em código real.

The Web API Production Checklist, e-book gratuito em inglês

E-book gratuito

Sua Web API está pronta para produção?

33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.

O e-book está em inglês.

Baixe o checklist gratuito

PDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.

A indentação envolve uma contrapartida. As quebras de linha deixam legível um documento grande e aninhado, e também trafegam pela rede como bytes extras; por isso, indentamos o arquivo que uma pessoa vai abrir e deixamos minificado o payload que uma API retorna.

Serializar uma lista com o método SerializeToUtf8Bytes()

Como alternativa, podemos usar o método SerializeToUtf8Bytes() para serializar uma lista C# em JSON:

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

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

Primeiro, chamamos o método JsonSerializer.SerializeToUtf8Bytes() passando dois argumentos. O primeiro é o objeto List clubList, e o segundo é _options, a instância da classe JsonSerializerOptions criada anteriormente neste artigo. Usamos essas opções para controlar a formatação da string JSON resultante, garantindo, por exemplo, que ela seja legível e que os nomes das propriedades estejam em camelCase.

Concluída a serialização, obtemos o array de bytes resultante e o convertemos em string com o método System.Text.Encoding.UTF8.GetString(). Aqui, passamos o array de bytes como argumento, e o método decodifica esses bytes UTF-8 em uma string.

Evitar a string é justamente a razão de ser do SerializeToUtf8Bytes(), e o guia da Microsoft sobre como serializar JSON em C# quantifica o ganho: “Serializar para um array de bytes UTF-8 é de 5% a 10% mais rápido do que usar os métodos baseados em string.” O nosso benchmark, mais adiante, converte os bytes de volta em string para que os quatro métodos retornem o mesmo tipo, e é por isso que ele não mostra esse ganho.

Listas não são o único formato que serializamos dessa forma. As mesmas chamadas funcionam com um dicionário, tema do nosso artigo sobre como serializar um dicionário em vez de uma lista.

Como serializar uma lista em JSON com Newtonsoft.Json?

O Newtonsoft.Json é um pacote NuGet, e não parte do .NET, por isso é a escolha certa quando um projeto já depende dele ou precisa de algo que o System.Text.Json não oferece.

JsonConvert.SerializeObject(clubList, _settings) é o equivalente em uma linha: passe a lista e receba de volta um array JSON como string.

A formatação fica em uma instância de JsonSerializerSettings, e não em JsonSerializerOptions. Formatting.Indented corresponde a WriteIndented, e os nomes em camelCase vêm de um DefaultContractResolver com um CamelCaseNamingStrategy, o que exige mais código do que a única propriedade de que o System.Text.Json precisa para chegar ao mesmo resultado.

A classe JsonSerializer é o caminho de nível mais baixo. Criamos uma instância com JsonSerializer.Create(_settings) e escrevemos por meio de um JsonTextWriter, o que nos permite serializar para um StringBuilder, um arquivo ou qualquer outro destino em que um TextWriter consiga escrever.

Para uma lista simples, SerializeObject() é a escolha certa. A abordagem de nível mais baixo só compensa as linhas extras quando controlamos o destino.

Para começar, vamos adicionar o pacote com dotnet add package Newtonsoft.Json e definir uma classe SerializeListToJsonWithNewtonsoftJson:

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Text;

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

Aqui, criamos uma classe que vai conter todos os métodos que usam a biblioteca Newtonsoft.Json. Como antes, o construtor primário recebe a lista que queremos serializar.

Em seguida, para garantir que os métodos retornem a string JSON em um formato legível e com os nomes das propriedades em camelCase, vamos criar uma instância da classe JsonSerializerSettings:

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

Nessa instância, inicializamos duas configurações. Primeiro, definimos a propriedade Formatting como Formatting.Indented. Com isso, a string JSON retornada fica indentada, o que melhora a legibilidade.

Depois, atribuímos à propriedade ContractResolver uma instância da classe DefaultContractResolver. Com isso, quando serializamos a lista em JSON, os nomes das propriedades passam automaticamente para camelCase. Aqui, atribuímos à propriedade NamingStrategy do DefaultContractResolver uma instância de CamelCaseNamingStrategy. Fazemos isso para converter os nomes das propriedades em camelCase durante a serialização.

Serializar uma lista com JsonConvert.SerializeObject()

Com isso pronto, vamos ver como usar o método JsonConvert.SerializeObject() para serializar uma lista em JSON.

Primeiro, vamos definir um método SerializeObjectMethod():

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

Aqui, chamamos o método JsonConvert.SerializeObject() e passamos dois parâmetros. O primeiro é o objeto de lista que queremos serializar, e o segundo é a instância da classe JsonSerializerSettings que criamos.

Serializar uma lista com a classe JsonSerializer

Ainda não terminamos. Vamos ver como usar a classe JsonSerializer para converter um objeto List<T> em 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();
}

No método JsonSerializerClass(), criamos uma instância da classe JsonSerializer passando o objeto _settings como argumento. Usaremos essa instância para serializar a lista em JSON.

Em seguida, criamos um novo objeto StringBuilder, que vai armazenar a string JSON. Depois, usamos uma instância de JsonTextWriter para escrever a string JSON serializada no StringBuilder.

Dentro da instrução using, chamamos o método serializer.Serialize(), passando como argumentos a instância de JsonTextWriter e o objeto de lista clubList. Esse método serializa o clubList em uma string JSON, que escrevemos no StringBuilder.

Por fim, retornamos a string JSON do StringBuilder chamando o método ToString().

The Web API Production Checklist, e-book gratuito em inglês

E-book gratuito

Sua Web API está pronta para produção?

33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.

O e-book está em inglês.

Baixe o checklist gratuito

PDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.

Qual é a forma mais rápida de serializar uma lista para JSON em C#?

JsonSerializer.Serialize(), do System.Text.Json, é o mais rápido dos quatro métodos e o que menos aloca memória.

Medimos o desempenho dos quatro com o BenchmarkDotNet em uma lista de 10.000 objetos Club. Os dois métodos do System.Text.Json terminam à frente dos dois métodos do Newtonsoft.Json, e Serialize() termina à frente de SerializeToUtf8Bytes().

Vale a pena ler a coluna de memória separadamente dos tempos. Observe que SerializeToUtf8Bytes() é medido com uma conversão de volta para string dentro do código cronometrado, para que os quatro métodos retornem o mesmo tipo. Em código real, essa conversão geralmente não acontece, porque os bytes já são o que o stream ou o arquivo espera.

A distância entre eles só importa em grande escala. Para um punhado de objetos no caminho de uma requisição, a diferença entre os quatro é desprezível, e o fator decisivo é qual biblioteca o projeto já usa, e não qual delas vence um benchmark.

Para saber mais sobre como o BenchmarkDotNet funciona, veja a introdução à medição de desempenho de projetos C#.

Para ver em detalhes como implementamos o benchmark desses métodos, visite o nosso repositório.

Depois de executar o benchmark em uma lista de 10.000 objetos Club, podemos examinar os resultados no 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 |

Com base nesses resultados, a forma mais rápida de serializar uma instância de List<T> para uma string JSON em C# é chamar JsonSerializer.Serialize(). Esse método também é o que usa menos memória entre todos os métodos testados.

O método SerializeToUtf8BytesMethod() fica um pouco atrás e aloca mais memória. Lembre-se de que, neste benchmark, o byte[] é convertido de volta em string dentro do código medido, para que os quatro métodos retornem uma string.

Os métodos JsonSerializerClass() e SerializeObjectMethod() são mais lentos que os métodos baseados em System.Text.Json, e os dois alocam aproximadamente a mesma quantidade de memória.

Veja como as chamadas se comparam na hora de escolher uma:

ChamadaBibliotecaRetornaQuando usar
JsonSerializer.Serialize(list, options)System.Text.JsonstringA opção padrão, a mais rápida e a mais leve para obter uma string
JsonSerializer.SerializeToUtf8Bytes(list, options)System.Text.Jsonbyte[]O JSON vai para um stream, um socket ou um arquivo
JsonSerializer.SerializeAsync(stream, list, options)System.Text.JsonTaskGravar uma lista grande direto no destino
JsonConvert.SerializeObject(list, settings)Newtonsoft.JsonstringO projeto já depende do Newtonsoft.Json
JsonSerializer.Create(settings) + JsonTextWriterNewtonsoft.Jsonescreve em um TextWriterNewtonsoft.Json, e o destino está sob nosso controle

Como serializar uma lista direto para um arquivo ou stream?

Serializar para uma string e depois escrever essa string gera duas cópias dos mesmos dados. Em vez disso, o System.Text.Json pode escrever diretamente em um stream.

await JsonSerializer.SerializeAsync(stream, clubList, _options) recebe o destino como primeiro argumento, então um FileStream obtido de File.Create() recebe o JSON à medida que ele é gerado. O método esvazia o buffer no stream sempre que o buffer de tamanho DefaultBufferSize, 16 KB por padrão, fica 90% cheio, por isso o pico de memória é esse buffer, e não o documento inteiro.

Existe uma versão síncrona, Serialize(Stream, value, options), para quando não há um contexto assíncrono em que se possa usar await.

É aqui também que SerializeToUtf8Bytes() se encaixa. O byte[] que ele retorna vai direto para stream.WriteAsync(), sem nenhuma etapa de codificação no meio, e é por isso que esse método existe ao lado do que retorna string.

Em uma lista de algumas centenas de itens, nada disso é perceptível. Em uma exportação de vários megabytes, é a diferença entre um buffer e três, e essa é uma diferença que sentimos na memória, não nos milissegundos.

Vamos adicionar mais um método à classe que usa o System.Text.Json:

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

O método recebe qualquer Stream gravável e o repassa para JsonSerializer.SerializeAsync() junto com a lista e as opções. Passar um FileStream retornado por File.Create() grava o array JSON em disco, e passar um MemoryStream o mantém em memória, que é como o teste do nosso repositório verifica o resultado.

Se a string JSON já existe e só precisa ser salva, o nosso artigo sobre como gravar uma string JSON em um arquivo explica esse outro caminho.

Conclusão

O C# conta com duas bibliotecas populares, System.Text.Json e Newtonsoft.Json, que podemos usar para serializar uma lista em JSON, com quatro chamadas principais no total entre elas.

Neste artigo, implementamos as quatro chamadas, medimos o desempenho delas em uma lista de 10.000 objetos e escrevemos uma lista direto em um stream. JsonSerializer.Serialize(), do System.Text.Json, é a escolha certa, a menos que o projeto já dependa do Newtonsoft.Json. Para o sentido inverso, o nosso artigo sobre como ler o JSON de volta em objetos retoma a história a partir do arquivo.

Testado com .NET 10 e Newtonsoft.Json 13.0.4.