Para serializar una lista a JSON en C#, pásala a JsonSerializer.Serialize(). Entra un List<T>, sale un array JSON y no hay nada que instalar, porque System.Text.Json viene incluido con .NET.
Hay otras tres vías que conviene conocer. SerializeToUtf8Bytes() devuelve bytes UTF-8 en lugar de una cadena, y Newtonsoft.Json ofrece JsonConvert.SerializeObject() y la clase JsonSerializer, de más bajo nivel, para los proyectos que ya usan esa biblioteca. Si la entrada es un solo objeto y no una colección, nuestra guía sobre cómo convertir un objeto en una cadena JSON es un punto de partida mejor.
Vamos a implementar las cuatro y después mediremos su rendimiento con una lista de 10 000 objetos para ver cuál conviene usar.
Empecemos.
Preparar el entorno
Para empezar, vamos a preparar el entorno creando la lista que usaremos en este artículo. La lista va en el Program.cs de una aplicación de consola y cada clase que escribamos va en su propio archivo.
Primero, vamos a definir una clase 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; }
}
A continuación, vamos a crear un 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 esta lista como datos de entrada para todos los métodos de serialización de este artículo.
¿Cómo serializar una lista a JSON con System.Text.Json?
System.Text.Json es el serializador que viene incluido con .NET, así que para serializar una lista no hace falta ninguna referencia a paquetes.
Basta con una llamada estática. JsonSerializer.Serialize(clubList) acepta cualquier List<T> y devuelve un array JSON como cadena, con un valor por elemento.
Si no configuramos nada, escribe JSON minificado y mantiene los nombres de las propiedades exactamente como se declaran en C#. Las dos cosas se configuran mediante una instancia de JsonSerializerOptions: WriteIndented = true agrega los saltos de línea y PropertyNamingPolicy = JsonNamingPolicy.CamelCase convierte YearFounded en yearFounded, que es lo que espera la mayoría de los consumidores de JSON.
SerializeToUtf8Bytes() es la misma operación con otro tipo de valor devuelto. Devuelve un byte[] ya codificado en UTF-8, que es lo que queremos cuando el JSON va directo a un stream de red o a un archivo, porque se ahorra la cadena intermedia.
Creamos el objeto de opciones una sola vez y lo reutilizamos, porque una instancia nueva puede implicar una caché de metadatos nueva.
Vamos a definir una clase SerializeListToJsonWithSystemTextJson:
using System.Text.Json;
public class SerializeListToJsonWithSystemTextJson(List<Club> clubList)
{
}
Aquí definimos una clase que contendrá todos los métodos que usan la biblioteca System.Text.Json. Su constructor principal recibe la lista que queremos serializar, y así clubList queda disponible en todos los métodos de la clase sin necesidad de un campo aparte ni de un cuerpo de constructor.
Serializar una lista con el método Serialize()
Ahora veamos con más detalle cómo usar el método Serialize() del espacio de nombres System.Text.Json para convertir una lista en una cadena JSON en C#.
Primero, vamos a definir un método SerializeMethod():
public string SerializeMethod()
{
return JsonSerializer.Serialize(clubList);
}
En este método llamamos al método Serialize() de la clase JsonSerializer y le pasamos como parámetro el objeto List<T> que queremos serializar.
Este método devuelve una cadena minificada (sin sangría, espacios en blanco ni caracteres de nueva línea):
[{"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}]
Esta es la cadena de salida que buscamos, y podemos usarla tal cual para guardar datos en un archivo o enviar información por la red a otra aplicación. Sin embargo, no es fácil de leer y los nombres de las propiedades no siguen el formato recomendado en JSON (camelCase). Ten en cuenta que en el ejemplo de código hemos dividido la cadena en varias líneas para facilitar la lectura.
Para resolver estos problemas, vamos a crear una instancia de la clase JsonSerializerOptions:
private readonly JsonSerializerOptions _options = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
};
Aquí creamos una instancia llamada _options y establecemos dos propiedades. Primero, asignamos JsonNamingPolicy.CamelCase a la propiedad PropertyNamingPolicy. Así, al serializar la lista a JSON, los nombres de las propiedades pasan automáticamente a camelCase. La nomenclatura camelCase no se limita a esta opción, y nuestro artículo sobre la política de nomenclatura camelCase y sus opciones explica el resto.
A continuación, asignamos true a la propiedad WriteIndented. Al usar esta instancia para serializar la lista a JSON, nos aseguramos de que la cadena JSON resultante tenga sangría y se lea mejor.
Si pasamos la variable _options a la llamada a Serialize() dentro de SerializeMethod() y lo invocamos, obtenemos:
[
{
"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 vemos, con esta instancia nos aseguramos de que la cadena JSON devuelta sea legible y siga el formato recomendado para los nombres de las propiedades. La versión de nuestro repositorio pasa _options en cada llamada, que es la forma que queremos en código real.

Ebook gratis
¿Tu Web API está lista para producción?
33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.
El ebook está en inglés.
Descarga la checklist gratisPDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.
La sangría tiene su contrapartida. Los saltos de línea hacen legible un documento grande y anidado, pero también viajan por la red como bytes extra, así que aplicamos sangría al archivo que abre una persona y dejamos minificado el payload que devuelve una API.
Serializar una lista con el método SerializeToUtf8Bytes()
Como alternativa, podemos usar el método SerializeToUtf8Bytes() para serializar una lista de C# a JSON:
public string SerializeToUtf8BytesMethod()
{
var result = JsonSerializer.SerializeToUtf8Bytes(clubList, _options);
return System.Text.Encoding.UTF8.GetString(result);
}
Primero, invocamos el método JsonSerializer.SerializeToUtf8Bytes() con dos argumentos. El primero es el objeto List clubList y el segundo es _options, una instancia de la clase JsonSerializerOptions que creamos antes en este artículo. Usamos estas opciones para controlar el formato de la cadena JSON resultante, por ejemplo, para que sea legible y los nombres de las propiedades estén en camelCase.
Una vez terminada la serialización, obtenemos el array de bytes resultante y lo convertimos en una cadena con el método System.Text.Encoding.UTF8.GetString(). Aquí pasamos el array de bytes como argumento y el método decodifica esos bytes UTF-8 en una cadena.
Evitar la cadena es la razón de ser de SerializeToUtf8Bytes(), y la guía de Microsoft para serializar JSON en C# le pone cifra: “Serializar a un array de bytes UTF-8 es entre un 5 % y un 10 % más rápido que usar los métodos basados en cadenas”. El benchmark que veremos más abajo vuelve a convertir los bytes en una cadena para que los cuatro métodos devuelvan el mismo tipo, y por eso no refleja esa ganancia.
Una lista no es lo único que podemos serializar así. Las mismas llamadas funcionan con un diccionario, como explicamos en nuestro artículo sobre cómo serializar un diccionario en lugar de una lista.
¿Cómo serializar una lista a JSON con Newtonsoft.Json?
Newtonsoft.Json es un paquete NuGet y no forma parte de .NET, así que es la opción adecuada cuando un proyecto ya depende de él o necesita algo que System.Text.Json no ofrece.
JsonConvert.SerializeObject(clubList, _settings) es el equivalente en una sola línea: le pasas la lista y te devuelve un array JSON como cadena.
El formato se configura en una instancia de JsonSerializerSettings en lugar de JsonSerializerOptions. Formatting.Indented es el equivalente de WriteIndented, y los nombres en camelCase se obtienen con un DefaultContractResolver que lleva un CamelCaseNamingStrategy, lo que supone escribir más que la única propiedad que necesita System.Text.Json para el mismo resultado.
La clase JsonSerializer es la vía de más bajo nivel. La creamos con JsonSerializer.Create(_settings) y escribimos mediante un JsonTextWriter, lo que nos permite serializar en un StringBuilder, en un archivo o en cualquier otro destino en el que pueda escribir un TextWriter.
Para una lista sin más, SerializeObject() es la opción que conviene elegir. La vía del escritor solo compensa sus líneas de más cuando controlamos el destino.
Para empezar, vamos a agregar el paquete con dotnet add package Newtonsoft.Json y a definir una clase SerializeListToJsonWithNewtonsoftJson:
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Text;
public class SerializeListToJsonWithNewtonsoftJson(List<Club> clubList)
{
}
Aquí creamos una clase que contendrá todos los métodos que usan la biblioteca Newtonsoft.Json. Como antes, su constructor principal recibe la lista que queremos serializar.
A continuación, para que los métodos devuelvan la cadena JSON en un formato legible y con los nombres de las propiedades en camelCase, vamos a crear una instancia de la clase JsonSerializerSettings:
private readonly JsonSerializerSettings _settings = new()
{
Formatting = Formatting.Indented,
ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() },
};
En esta instancia inicializamos dos opciones. Primero, asignamos Formatting.Indented a la propiedad Formatting. Con esto, la cadena JSON devuelta sale con sangría para que se lea mejor.
Después, asignamos a la propiedad ContractResolver una instancia de la clase DefaultContractResolver. Así, al serializar la lista a JSON, los nombres de las propiedades pasan automáticamente a camelCase. Aquí asignamos a la propiedad NamingStrategy de DefaultContractResolver una instancia de CamelCaseNamingStrategy. Lo hacemos para que los nombres de las propiedades pasen a camelCase durante la serialización.
Serializar una lista con JsonConvert.SerializeObject()
Con esto, veamos cómo usar el método JsonConvert.SerializeObject() para serializar una lista a JSON.
Primero, vamos a definir un método SerializeObjectMethod():
public string SerializeObjectMethod()
{
return JsonConvert.SerializeObject(clubList, _settings);
}
Aquí invocamos el método JsonConvert.SerializeObject() y le pasamos dos parámetros. El primero es la lista que queremos serializar y el segundo, la instancia de la clase JsonSerializerSettings que creamos.
Serializar una lista con la clase JsonSerializer
Todavía no hemos terminado. Veamos cómo usar la clase JsonSerializer para convertir un objeto List<T> en 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();
}
En el método JsonSerializerClass() creamos una instancia de la clase JsonSerializer pasando como argumento el objeto _settings. Usaremos esta instancia para serializar la lista a JSON.
A continuación, creamos un nuevo objeto StringBuilder que almacenará la cadena JSON. Después usamos una instancia de JsonTextWriter para escribir la cadena JSON serializada en el StringBuilder.
Dentro de la instrucción using, pasamos la instancia JsonTextWriter y el objeto lista clubList como argumentos al método serializer.Serialize() y lo invocamos. Este método serializa clubList en una cadena JSON, que escribimos en el StringBuilder.
Por último, devolvemos la cadena JSON del StringBuilder llamando al método ToString().

Ebook gratis
¿Tu Web API está lista para producción?
33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.
El ebook está en inglés.
Descarga la checklist gratisPDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.
¿Cuál es la forma más rápida de serializar una lista a JSON en C#?
JsonSerializer.Serialize() de System.Text.Json es el más rápido de los cuatro métodos y el que menos memoria asigna.
Medimos los cuatro con BenchmarkDotNet sobre una lista de 10 000 objetos Club. Los dos métodos de System.Text.Json terminan por delante de los dos de Newtonsoft.Json, y Serialize() queda por delante de SerializeToUtf8Bytes().
Conviene leer la columna de memoria por separado de los tiempos. Ten en cuenta que SerializeToUtf8Bytes() se mide con una conversión de vuelta a cadena dentro del código cronometrado, para que los cuatro métodos devuelvan el mismo tipo. En código real esa conversión no suele hacerse, porque los bytes ya son lo que necesita el stream o el archivo.
La diferencia solo importa a gran escala. Con unos pocos objetos por solicitud, los cuatro quedan prácticamente empatados, y lo que decide es qué biblioteca usa ya el proyecto, no cuál gana un benchmark.
Para saber más sobre cómo funciona BenchmarkDotNet, consulta nuestra introducción a la medición del rendimiento de proyectos C#.
Para ver en detalle cómo está implementado el benchmark de estos métodos, consulta nuestro repositorio.
Tras ejecutar el benchmark sobre una lista de 10 000 objetos Club, podemos ver los resultados en la consola:
| 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 |
Según estos resultados, la forma más rápida de serializar una instancia de List<T> a una cadena JSON en C# es llamar a JsonSerializer.Serialize(). Este método es también el que menos memoria usa de todos los que hemos probado.
El método SerializeToUtf8BytesMethod() queda un poco por detrás y asigna más memoria. Recuerda que este benchmark vuelve a convertir su byte[] en una cadena dentro del código medido, para que los cuatro métodos devuelvan una cadena.
Los métodos JsonSerializerClass() y SerializeObjectMethod() son más lentos que los basados en System.Text.Json, y los dos asignan aproximadamente la misma cantidad de memoria.
Así se comparan las llamadas cuando tenemos que elegir una:
| Llamada | Biblioteca | Devuelve | Úsala cuando |
|---|---|---|---|
JsonSerializer.Serialize(list, options) | System.Text.Json | string | Es la vía predeterminada, la más rápida y la más ligera para obtener una cadena |
JsonSerializer.SerializeToUtf8Bytes(list, options) | System.Text.Json | byte[] | El JSON va a un stream, un socket o un archivo |
JsonSerializer.SerializeAsync(stream, list, options) | System.Text.Json | Task | Vas a escribir una lista grande directamente en su destino |
JsonConvert.SerializeObject(list, settings) | Newtonsoft.Json | string | El proyecto ya depende de Newtonsoft.Json |
JsonSerializer.Create(settings) + JsonTextWriter | Newtonsoft.Json | escribe en un TextWriter | Newtonsoft.Json, y controlamos el destino |
¿Cómo serializar una lista directamente en un archivo o un stream?
Serializar a una cadena y después escribir esa cadena genera dos copias de los mismos datos. System.Text.Json puede escribir directamente en un stream.
await JsonSerializer.SerializeAsync(stream, clubList, _options) recibe el destino como primer argumento, así que un FileStream obtenido de File.Create() recibe el JSON a medida que se genera. Vacía su búfer, cuyo tamaño fija DefaultBufferSize (16 KB de forma predeterminada), cada vez que se llena al 90 %, de modo que el pico de memoria es ese búfer y no el documento entero.
Existe un equivalente síncrono, Serialize(Stream, value, options), para cuando no hay un contexto asíncrono desde el que usar await.
Aquí encaja también SerializeToUtf8Bytes(). Su byte[] va directo a stream.WriteAsync() sin ningún paso de codificación intermedio, y por eso existe ese método junto al que devuelve una cadena.
Con una lista de unos cientos de elementos, nada de esto es perceptible. En una exportación de varios megabytes es la diferencia entre un búfer y tres, y esa diferencia se nota más en la memoria que en los milisegundos.
Vamos a agregar un método más a nuestra clase de System.Text.Json:
public async Task SerializeToStreamAsync(Stream stream)
{
await JsonSerializer.SerializeAsync(stream, clubList, _options);
}
El método acepta cualquier Stream en el que se pueda escribir y lo pasa a JsonSerializer.SerializeAsync() junto con la lista y las opciones. Si pasamos un FileStream devuelto por File.Create(), el array JSON se escribe en disco, y si pasamos un MemoryStream, se queda en memoria; así es como la prueba de nuestro repositorio comprueba el resultado.
Si la cadena JSON ya existe y solo hay que guardarla, nuestro artículo sobre cómo escribir una cadena JSON en un archivo explica esa otra vía.
Conclusión
En C# hay dos bibliotecas populares, System.Text.Json y Newtonsoft.Json, que podemos usar para serializar una lista a JSON, y cuatro llamadas principales entre ambas.
En este artículo hemos implementado las cuatro, hemos medido su rendimiento con una lista de 10 000 objetos y hemos escrito una lista directamente en un stream. JsonSerializer.Serialize() de System.Text.Json es la opción que conviene elegir, salvo que el proyecto ya dependa de Newtonsoft.Json. Para la conversión en sentido contrario, nuestro artículo sobre cómo volver a convertir el JSON en objetos retoma el hilo a partir del archivo.
Probado con .NET 10 y Newtonsoft.Json 13.0.4.