Procedimiento para serializar y deserializar (calcular referencias y resolver referencias) JSON en .NET

En este artículo se muestra cómo usar el espacio de nombres System.Text.Json para serializar y deserializar a y desde la notación de objetos JavaScript (JSON). Si va a portar el código existente de Newtonsoft.Json, consulte Procedimiento para migrar a System.Text.Json.

Las instrucciones y el código de ejemplo usan la biblioteca directamente, no a través de un marco como ASP.NET Core.

La mayor parte del código de ejemplo de la serialización establece JsonSerializerOptions.WriteIndented en true para "imprimir correctamente" el JSON (con sangría y espacio en blanco para mayor legibilidad). Para su uso en producción, normalmente aceptaría el valor predeterminado de false para este valor.

Los ejemplos de código hacen referencia a la siguiente clase y sus variantes:

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
}

Espacios de nombres

El espacio de nombres System.Text.Json contiene todos los puntos de entrada y los tipos principales. El espacio de nombres System.Text.Json.Serialization contiene atributos e interfaces API para escenarios avanzados y personalización específicos de la serialización y deserialización. Los ejemplos de código que se muestran en este artículo requieren directivas using para uno o ambos espacios de nombres:

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

Importante

Los atributos del espacio de nombres System.Runtime.Serialization no se admiten en System.Text.Json.

Escritura de objetos .NET como JSON (serializar)

Para escribir JSON en una cadena o un archivo, llame al método JsonSerializer.Serialize.

En el ejemplo siguiente se crea un archivo JSON como cadena:

string jsonString = JsonSerializer.Serialize(weatherForecast);

En el ejemplo siguiente se usa código sincrónico para crear un archivo JSON:

jsonString = JsonSerializer.Serialize(weatherForecast);
File.WriteAllText(fileName, jsonString);

En el ejemplo siguiente se usa código asincrónico para crear un archivo JSON:

using FileStream createStream = File.Create(fileName);
await JsonSerializer.SerializeAsync(createStream, weatherForecast);

En los ejemplos anteriores se usa la inferencia de tipos para el tipo que se está serializando. Una sobrecarga de Serialize() toma un parámetro de tipo genérico:

jsonString = JsonSerializer.Serialize<WeatherForecastWithPOCOs>(weatherForecast);

Ejemplo de serialización

A continuación se muestra una clase de ejemplo que contiene propiedades de tipo de colección y un tipo definido por el usuario:

public class WeatherForecastWithPOCOs
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
    public string SummaryField;
    public IList<DateTimeOffset> DatesAvailable { get; set; }
    public Dictionary<string, HighLowTemps> TemperatureRanges { get; set; }
    public string[] SummaryWords { get; set; }
}

public class HighLowTemps
{
    public int High { get; set; }
    public int Low { get; set; }
}

Sugerencia

"POCO" significa objeto CRL estándar. Un POCO es un tipo de .NET que no depende de ningún tipo específico del marco, por ejemplo, a través de la herencia o atributos.

La salida JSON de la serialización de una instancia del tipo anterior es similar al ejemplo siguiente. La salida JSON es reducida (se quitan los caracteres de espacio en blanco, sangría y nueva línea) de forma predeterminada:

{"Date":"2019-08-01T00:00:00-07:00","TemperatureCelsius":25,"Summary":"Hot","DatesAvailable":["2019-08-01T00:00:00-07:00","2019-08-02T00:00:00-07:00"],"TemperatureRanges":{"Cold":{"High":20,"Low":-10},"Hot":{"High":60,"Low":20}},"SummaryWords":["Cool","Windy","Humid"]}

En el ejemplo siguiente se muestra el mismo JSON, pero con formato (es decir, impreso correctamente con espacio en blanco y sangría):

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "TemperatureRanges": {
    "Cold": {
      "High": 20,
      "Low": -10
    },
    "Hot": {
      "High": 60,
      "Low": 20
    }
  },
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}

Serialización a UTF-8

Para serializar a UTF-8, llame al método JsonSerializer.SerializeToUtf8Bytes:

byte[] jsonUtf8Bytes =JsonSerializer.SerializeToUtf8Bytes(weatherForecast);

También está disponible una sobrecarga Serialize que toma un valor Utf8JsonWriter.

La serialización a UTF-8 es aproximadamente un 5-10 % más rápida que el uso de métodos basados en cadenas. La diferencia se debe a que no hay que convertir los bytes (como UTF-8) en cadenas (UTF-16).

Comportamiento de serialización

Cuando se usa System.Text.Json indirectamente en una aplicación ASP.NET Core, algunos comportamientos predeterminados son diferentes. Para obtener más información, vea Valores predeterminados web para JsonSerializerOptions.

Los tipos no admitidos incluyen:

Puede implementar convertidores personalizados para controlar tipos adicionales o proporcionar funcionalidad que no admiten los convertidores integrados.

Lectura de JSON como objetos .NET (deserializar)

Para deserializar a partir de una cadena o un archivo, llame al método JsonSerializer.Deserialize.

En el ejemplo siguiente se lee JSON desde una cadena y se crea una instancia de la clase WeatherForecastWithPOCOs mostrada anteriormente para el ejemplo de serialización:

weatherForecast = JsonSerializer.Deserialize<WeatherForecastWithPOCOs>(jsonString);

Para deserializar a partir de un archivo mediante código sincrónico, lea el archivo en una cadena, tal y como se muestra en el ejemplo siguiente:

jsonString = File.ReadAllText(fileName);
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString);

Para deserializar a partir de un archivo mediante código asincrónico, llame al método DeserializeAsync:

using FileStream openStream = File.OpenRead(fileName);
weatherForecast = await JsonSerializer.DeserializeAsync<WeatherForecast>(openStream);

Deserialización desde UTF-8

Para deserializar desde UTF-8, llame a una sobrecarga JsonSerializer.Deserialize que tome un valor ReadOnlySpan<byte> o Utf8JsonReader, tal y como se muestra en el ejemplo siguiente. En los ejemplos se presupone que el JSON está en una matriz de bytes denominada jsonUtf8Bytes.

var readOnlySpan = new ReadOnlySpan<byte>(jsonUtf8Bytes);
WeatherForecast deserializedWeatherForecast = 
    JsonSerializer.Deserialize<WeatherForecast>(readOnlySpan);
var utf8Reader = new Utf8JsonReader(jsonUtf8Bytes);
WeatherForecast deserializedWeatherForecast = 
    JsonSerializer.Deserialize<WeatherForecast>(ref utf8Reader);

Comportamiento de la deserialización

Al deserializar JSON se aplican los comportamientos siguientes:

Cuando se usa System.Text.Json indirectamente en una aplicación ASP.NET Core, algunos comportamientos predeterminados son diferentes. Para obtener más información, vea Valores predeterminados web para JsonSerializerOptions.

Cuando se usa System.Text.Json indirectamente en una aplicación ASP.NET Core, algunos comportamientos predeterminados son diferentes. Para obtener más información, vea Valores predeterminados web para JsonSerializerOptions.

Puede implementar convertidores personalizados para proporcionar funcionalidad que no admiten los convertidores integrados.

Serialización a JSON con formato

Para imprimir correctamente la salida JSON, establezca JsonSerializerOptions.WriteIndented en true:

var options = new JsonSerializerOptions
{
    WriteIndented = true,
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);

Aquí se muestra un tipo de ejemplo que se serializa y la salida de JSON impresa correctamente:

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
}
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot"
}

Si usa JsonSerializerOptions repetidas veces con las mismas opciones, no cree una instancia de JsonSerializerOptions cada vez que lo use. Reutilice la misma instancia para cada llamada. Para obtener más información, vea Reutilización de instancias de JsonSerializerOptions.

Inclusión de campos

Use el valor global JsonSerializerOptions.IncludeFields o el atributo [JsonInclude] para incluir campos al serializar o deserializar, como se muestra en el ejemplo siguiente:

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

namespace Fields
{
    public class Forecast
    {
        public DateTime Date;
        public int TemperatureC;
        public string Summary;
    }

    public class Forecast2
    {
        [JsonInclude]
        public DateTime Date;
        [JsonInclude]
        public int TemperatureC;
        [JsonInclude]
        public string Summary;
    }

    public class Program
    {
        public static void Main()
        {
            var json =
                @"{""Date"":""2020-09-06T11:31:01.923395"",""TemperatureC"":-1,""Summary"":""Cold""} ";
            Console.WriteLine($"Input JSON: {json}");

            var options = new JsonSerializerOptions
            {
                IncludeFields = true,
            };
            var forecast = JsonSerializer.Deserialize<Forecast>(json, options);

            Console.WriteLine($"forecast.Date: {forecast.Date}");
            Console.WriteLine($"forecast.TemperatureC: {forecast.TemperatureC}");
            Console.WriteLine($"forecast.Summary: {forecast.Summary}");

            var roundTrippedJson =
                JsonSerializer.Serialize<Forecast>(forecast, options);

            Console.WriteLine($"Output JSON: {roundTrippedJson}");

            var forecast2 = JsonSerializer.Deserialize<Forecast2>(json);

            Console.WriteLine($"forecast2.Date: {forecast2.Date}");
            Console.WriteLine($"forecast2.TemperatureC: {forecast2.TemperatureC}");
            Console.WriteLine($"forecast2.Summary: {forecast2.Summary}");

            roundTrippedJson = JsonSerializer.Serialize<Forecast2>(forecast2);
            
            Console.WriteLine($"Output JSON: {roundTrippedJson}");
        }
    }
}

// Produces output like the following example:
//
//Input JSON: { "Date":"2020-09-06T11:31:01.923395","TemperatureC":-1,"Summary":"Cold"}
//forecast.Date: 9/6/2020 11:31:01 AM
//forecast.TemperatureC: -1
//forecast.Summary: Cold
//Output JSON: { "Date":"2020-09-06T11:31:01.923395","TemperatureC":-1,"Summary":"Cold"}
//forecast2.Date: 9/6/2020 11:31:01 AM
//forecast2.TemperatureC: -1
//forecast2.Summary: Cold
//Output JSON: { "Date":"2020-09-06T11:31:01.923395","TemperatureC":-1,"Summary":"Cold"}

Para omitir los campos de solo lectura, use el parámetro global JsonSerializerOptions.IgnoreReadOnlyFields.

Los campos no se admiten en System.Text.Json en .NET Core 3.1. Los convertidores personalizados pueden proporcionar esta funcionalidad.

Métodos de extensión HttpClient y HttpContent

La serialización y deserialización de cargas JSON de la red son operaciones comunes. Los métodos de extensión de HttpClient y HttpContent permiten realizar estas operaciones en una sola línea de código. Estos métodos de extensión usan valores predeterminados web para JsonSerializerOptions.

En el siguiente ejemplo se muestra el uso de HttpClientJsonExtensions.GetFromJsonAsync y HttpClientJsonExtensions.PostAsJsonAsync:

using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;

namespace HttpClientExtensionMethods
{
    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Username { get; set; }
        public string Email { get; set; }
    }

    public class Program
    {
        public static async Task Main()
        {
            using HttpClient client = new()
            {
                BaseAddress = new Uri("https://jsonplaceholder.typicode.com")
            };

            // Get the user information.
            User user = await client.GetFromJsonAsync<User>("users/1");
            Console.WriteLine($"Id: {user.Id}");
            Console.WriteLine($"Name: {user.Name}");
            Console.WriteLine($"Username: {user.Username}");
            Console.WriteLine($"Email: {user.Email}");

            // Post a new user.
            HttpResponseMessage response = await client.PostAsJsonAsync("users", user);
            Console.WriteLine(
                $"{(response.IsSuccessStatusCode ? "Success" : "Error")} - {response.StatusCode}");
        }
    }
}

// Produces output like the following example but with different names:
//
//Id: 1
//Name: Tyler King
//Username: Tyler
//Email: Tyler @contoso.com
//Success - Created

También hay métodos de extensión para System.Text.Json en HttpContent.

Los métodos de extensión de HttpClient y HttpContent no están disponibles en System.Text.Json en .NET Core 3.1.

Vea también