Procesar JSON con System.Text.Json
Sin lugar a dudas el formato JSON se ha convertido en un uno de los formatos preferidos al momento de intercambiar datos entre sistemas. En el entorno de .NET la librería de facto para serializar y deserializar JSON es Json.NET superando en facilidad de uso y en velocidad incluso (50% más rápida) a las API nativas del mismo .NET Framework es decir DataContractJsonSerializer.
Esta biblioteca ha permeado tanto el entorno de .NET que fue incluida como dependencia en el shared framework Microsoft.AspNetCore.App de ASP.NET Core desde un inicio y es el paquete más descargado de Nuget.org.
El reinado de Json.NET puede estar en riesgo la liberación de la versión 3.1 .NET Core el pasado 3 de diciembre del 2019 y la inclusión API nativa de alto desempeño System.Text.Json
. Basicamente te permite serializar y deserialziar JSON sin la necesidad de instalar un paquete adicional. Ya se anuncio que no sera incluida como dependencia en ASP.NET Core 3.1.
Nota: este articulo ha sido actualizado para funcionar con la versión más actual de .NET 6 y que ha sido marcada como una versión con soporte a largo plazo, es decir, 3 años a partir de la fecha de liberación.
Si tienes un proyecto con .NET 6 solo es necesario importar los espacios de nombres con la instrucción using
) correspondientes para hacer uso de esta API:
using System.Text.Json;
using System.Text.Json.Serialization;
Si deseas usar e .NET Framework 4.6.1 o superior necesitas instalar el paquete de Nuget : System.Text.Json .Para más detalles de como controlar los paquetes de Nuget puede ver Controlando los paquetes de Nuget
Para nuestro ejemplo asumimos que tenemos la clase Producto
definida por la siguientes propiedades:
using System.Collections.Generic;
namespace ProcesarJson
{
public class Producto
{
public int Id { get; set; }
public string Nombre { get; set; }
public List<string> Categorias { get; set; }
public decimal Precio { get; set; }
}
}
Adicionalmente creamos 2 métodos estáticos, el primero regresa una instancia de la clase Producto
producto y el segundo regresa una cadena JSON que representa un objecto producto.
private static Producto ObtenerProducto()
{
Producto p = new Producto();
p.Id = 1;
p.Nombre = "Fundamentos de ASP.NET Core";
p.Categorias = new List<string>() { "Libros", "Programación" };
p.Precio = 198.50m;
return p;
}
private static string ObtenerProductoJson()
{
return @"{
""Id"": 100,
""Nombre"": ""ASP.NET Core Fundamentals"",
""Precio"": 10.50,
""Categorias"": [
""Libros"",
""Programación"",+
""Ingles""
]
}";
}
Aunque en la vida real estos objetos se pueden obtener de una base de datos, un servicio web o un archivo de texto.
Serialización con System.Text.Json
El proceso de convertir un objeto de C# en una cadena JSON se conoce como serialización. Esta tarea se realiza utilizando la clase JsonSerializer
el método estatico de Serialize
o alguna de sus sobrecargas. Existen versiones sincronas y asincronas
En su forma genérica requiere especificar el tipo a convertir y el una instancia del objeto a convertir a JSON. Es importante notar que el método Serialize
usa un parámetro opcional del tipo JSerializerOptions
que si no lo especificas lo será una referencia null
.
Producto producto = ObtenerProducto();
var cadenaJson = JsonSerializer.Serialize<Producto>(producto);
Console.WriteLine(cadenaJson);
El resultado de código anterior es una cadena JSON minificada de forma predeterminada.
{"Id":1,"Nombre":"Fundamentos de ASP.NET Core","Categorias":["Libros","Programaci\u00F3n"],"Precio":198.50}
Adicionalmete puedes agregar un objeto del tipo JsonSerializerOptions
que controla el comportamiento de la serialización por ejemplo.
JsonSerializerOptions options = new JsonSerializerOptions();
options.WriteIndented = true;
options.IgnoreNullValues = true;
options.PropertyNameCaseInsensitive = true;
options.AllowTrailingCommas = true;
var json2 = JsonSerializer.Serialize<Producto>(producto,options);
Console.WriteLine(json2);
Nota La propiedad IgnoreNullValues ha sido marcada como obsoleta.
Deserializar un objecto con System.Text.Json
Para deserializar se usa el método Deserialize
y sus sobrecargas. Igualmente este método acepta un objeto JsonSerializerOptions
para cambiar el comportamiento de la serialización.
string cadena = ObtenerProductoJson();
Producto producto2 = JsonSerializer.Deserialize<Producto>(cadena);
Console.WriteLine(producto2.Nombre);
Usando System.Text.Json con en la vida real
Los ejemplos anteriores aunque son ilustrativos y tienen como proposito mostrar como funciona el API estan muy alejados de la la vida real por lo que he decidido agregar un ejemplo para un poco más real. Obtendremos los datos de un perfil de de la API de Github que regresa JSON y lo convertiremos a un objeto C#. Para ello necesitamos invocar a la url https://api.github.com/users/jahbenjah
donde jahbenjah es mi nombre de usuario.
Lo primero que necesitamos hacer es crearu una clase de C# donde almacenaremos las propiedades que requerimos del objeto json que devuelve el API como proposito de ejemplo solo copiamos el JSON del API y usamos la característica Editar>Pegado Especial>Pegar JSON como clases de Visual Studio para crear la clase GithubUser
public class GithubUser
{
public string login { get; set; }
public int id { get; set; }
public string node_id { get; set; }
public string avatar_url { get; set; }
public string gravatar_id { get; set; }
public string url { get; set; }
public string html_url { get; set; }
public string followers_url { get; set; }
public string following_url { get; set; }
public string gists_url { get; set; }
public string starred_url { get; set; }
public string subscriptions_url { get; set; }
public string organizations_url { get; set; }
public string repos_url { get; set; }
public string events_url { get; set; }
public string received_events_url { get; set; }
public string type { get; set; }
public bool site_admin { get; set; }
public string name { get; set; }
public object company { get; set; }
public string blog { get; set; }
public string location { get; set; }
public object email { get; set; }
public bool hireable { get; set; }
public object bio { get; set; }
public int public_repos { get; set; }
public int public_gists { get; set; }
public int followers { get; set; }
public int following { get; set; }
public DateTime created_at { get; set; }
public DateTime updated_at { get; set; }
}
Finalmente configuramos un cliente HTTP para poder comunicarnos con la API de Github. Observa que requiere especificar un agente de usuario ya que si no lo usas te regresa el error 403.
static async Task Main(string[] args)
{
HttpClient cliente = new HttpClient();
cliente.DefaultRequestHeaders.Add("User-Agent","aspnetcoremaster.com");
cliente.BaseAddress = new Uri("https://api.github.com");
var jsonUser = await cliente.GetStringAsync("/users/jahbenjah");
GithubUser jahbenjah = JsonSerializer.Deserialize<GithubUser>(jsonUser);
Console.WriteLine("**************Datos deusuario**********************");
Console.WriteLine($"Nombre: {jahbenjah.name}");
Console.WriteLine($"Blog: {jahbenjah.blog}");
Console.WriteLine($"País: {jahbenjah.location}");
}
El atributo JsonPropertyName
El atributo JsonPropertyName se utiliza para cambiar el nombre de las propiedades en el proceso de serialización.
Para llevar
Es interesante ver la discusión entorno a al futuro de JSON en el entorno .NET y como puede cambiar la API de System.Text.Json
de acuerdo a los estudios de usabilidad realizados.