Jorge Serrano
  • Home

Trabajando con documentos JSON en .NET Core 3.x (II)

  • By jorge
  • Ene-3-2020
  • .NET Core 3.0, .NET Core 3.1, C#
  • 0 Comments.

Introducción

En una entrada anterior, veíamos de forma general algunos detalles con respecto al nuevo namespace System.Text.Json incorporado en .NET Core 3.x.
En esta ocasión, vamos a ver de forma práctica algunas de sus aplicaciones generales.

Agregando la referencia al namespace

Lo primero es que dentro de nuestro código, deberemos agregar una referencia al namespace que nos permite trabajar con documentos JSON:

using System.Text.Json;

Una vez hecho esto, podremos trabajar con las clases correspondientes al namespace que nos permitirán gestionar y trabajar con documentos JSON.

Serializar

Partiremos de una entidad Person cuyos miembros o propiedades son las siguientes:

public class Person
{
  public string Name { get; set; }
  public DateTime Birthday { get; set; }
}

Para serializar la entidad, utilizaremos la clase JsonSerializer.

El código demostrativo de ejemplo de una serialización tipo quedaría de la siguiente forma:

var person = new Person() { Name = "Jorge", Birthday = new DateTime(1999, 12, 31) };
var json = JsonSerializer.Serialize(person);

Deserializar

Para desarializar los datos de un documento JSON en una entidad, utilizaremos igualmente la clase JsonSerializer.

Un ejemplo demostrativo de este uso es el que se indica a continuación:

var json = "{\"Name\":\"Jorge\",\"Birthday\":\"1999-12-31T00:00:00\"}";
var person = JsonSerializer.Deserialize(json);

Aspectos avanzados deserializando

A veces, nos podemos encontrar con algunos comportamientos inicialmente no esperables cuando tratamos de deserializar un JSON.
A continuación expondré algunos de estos casos.

Case sensitive

Por defecto, la serialización y deserialización se realiza en modo PascalCase.

Imaginemos que el campo Birthday viene con la primera letra en minúscula en lugar de en mayúscula al puro estilo camelCase.

var json = "{\"Name\":\"Jorge\",\"birthday\":\"1999-12-31T00:00:00\"}";

Al deserializar nuestro JSON, sólo nos va a deserializar correctamente la información de Name, pero la información de Birthday la va a ignorar.

Para resolver esta problemática, podemos utilizar la clase JsonSerializerOptions en combinación con JsonSerializer.
JsonSerializerOptions nos proveerá opciones que podremos utilizar al serializar o deserializar usando JsonSerializer.

Por ejemplo.
Para resolver el problema de que nos venga un JSON cuyos atributos no concuerden con exactitud en mayúsculas y minúsculas con las propiedades de la entidad, bastará con configurarlo de la siguiente manera:

var options = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true };
var json = "{\"Name\":\"Jorge\",\"birthday\":\"1999-12-31T00:00:00\"}";
var person = JsonSerializer.Deserialize(json, options);

De esta forma, la deserialización se realizará ahora de forma correcta.

Forzando una deserialización en camelCase

Cabe reiterar como indicaba antes, que por defecto la serialización y deserialización se realiza en modo PascalCase por defecto.
Pero podríamos cambiar este comportamiento por defecto haciendo que la serialización y deserialización se realice en camelCase en lugar de en PascalCase utilizando nuevamente JsonSerializerOptions.

En este caso, escribiríamos las opciones de la siguiente forma:

var options = new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
var json = "{\"Name\":\"Jorge\",\"birthday\":\"1999-12-31T00:00:00\"}";
var person = JsonSerializer.Deserialize(json, options);

Aquí, Name aparecería como null, mientras que Birthday aparecería correctamente deserializado.

Aspectos avanzados serializando

También podemos hacer uso de diferentes opciones serializando al igual que hemos visto al deserializar.
La forma de hacer esto es exactamente igual.
Veamos algunas de estas interesantes características.

Ignorar null

Cuando serializamos, es posible que nos interese ignorar aquellos datos que tienen un valor null.
Para ello, bastará con indicar en las opciones de serialización, la propiedad IgnoreNullValues a true, ya que por defecto es false.

Así que nuestro código podría quedar de la siguiente forma:

var person = new Person() { Birthday = DateTime.UtcNow };
var options = new JsonSerializerOptions() { IgnoreNullValues = true };
var json = JsonSerializer.Serialize(person, options);

Json sangrado

Imaginemos que tenemos el siguiente JSON:

{ "Name": "Jorge", "Birthday": "1999-12-31T00:00:00" }

Y queremos presentarlo en pantalla o en fichero como:

{
  "Name": "Jorge",
  "Birthday": "1999-12-31T00:00:00"
}

Pues bien, la forma de lograr esto es nuevamente con las opciones a la hora de serializar la información.
Nuestro código en este caso, quedará de la siguiente forma:

var person = new Person() { Name = "Jorge", Birthday = new DateTime(1999, 12, 31) };
var options = new JsonSerializerOptions() { WriteIndented = true };
var json = JsonSerializer.Serialize(person, options);

Ignorar propiedades de sólo lectura

Imaginemos que la entidad que hemos estado desde el principio, es de esta otra forma:

public class Person
{
  public string Name { get; set; }
  public DateTime Birthday { get; set; }
  public string City { get; private set; } = "FooCity";
}

Es decir, tiene un propiedad de sólo lectura con un valor fijo, y queremos que omitir esos valores fijos de sólo lectura al serializar nuestro JSON.

En este caso, podríamos escribir nuestro código de la siguiente forma:

var person = new Person() { Name = "Jorge", Birthday = new DateTime(1999, 12, 31) };
var options = new JsonSerializerOptions() { IgnoreReadOnlyProperties = true };
var json = JsonSerializer.Serialize(person, options);

 

Serializar en bytes UTF-8

Como mencionaba en otro momento de esta entrada, es posible trabajar con UTF-8 directamente.
De hecho, podemos devolver un conjunto de bytes resultado de la serialización de nuestro objeto en formato UTF-8.

Esto lo lograremos gracias a la clase SerializeToUtf8Bytes.
Serializar a UTF-8 es cerca de entre 5% y 10% más rápido debido a que los bytes (como UTF-8) no necesitan ser convertidos a cadenas (UTF-16).

Así que un ejemplo de este uso sería el siguiente:

var person = new Person() { Name = "Jorge", Birthday = new DateTime(1999, 12, 31) };
var json = JsonSerializer.SerializeToUtf8Bytes(person);

Deserializar desde bytes en formato UTF-8

El caso inverso especial es el que partiendo de un conjunto de bytes en formato UTF-8, queremos deserializarlo en su entidad de forma directa.

En este caso, y suponiendo que tenemos la variable anterior json que contiene el JSON serializado en bytes, podemos deserializar directamente éste de la forma:

var person = JsonSerializer.Deserialize(json);

JsonSerializer.Deserialize nos permite deserializar directamente el array de bytes de nuestro JSON inicial.

Otra aplicaciones

Ignorar propiedades

Es posible que estemos interesados en ignorar alguna propiedad a la hora de serializar o deserializar un JSON.

Imaginemos que partimos de la siguiente entidad de salida:

public class Person
{
  public string Name { get; set; }
  public DateTime Birthday { get; set; }
  [JsonIgnore] public string City { get; set; };
}

La salida a la hora de serializar la información será un JSON sin la propiedad ignorada.
En el caso de deserializar la información, se ignorará igualmente esta propiedad, aunque en el JSON venga la información de la misma.

Cambiar el nombre de salida de una propiedad

Otra circunstancia con la que podemos tener que lidiar es la necesidad de cambiar el nombre de salida de una propiedad.

Una vez más, partimos de nuestra entidad que tendrá el siguiente aspecto:

public class Person
{
  public string Name { get; set; }
  public DateTime Birthday { get; set; }
  [JsonPropertyName("Place")] public string City { get; set; };
}

Al serializar, obtendremos un JSON cuya propiedad City queda sustituida por Place.

Evitar perder datos al deserializar

En determinadas circunstancias, es posible que nos llegue un JSON con datos esperados y otros datos, que no contemplábamos inicialmente.
Si nuestro propósito es no perder esos datos que no contemplábamos inicialmente en el JSON de entrada de acuerdo a las propiedades de nuestra entidad, entonces podemos hacer uso del atributo JsonExtensionDataAttribute.

De esta forma y gracias a este atributo, podremos obtener los elementos del JSON en una colección de tipo Dictionary<string, object> o Dictionary<string, JsonElement>.

Veamos su uso con un ejemplo partiendo de la siguiente entidad:

public class Person
{
  public string Name { get; set; }
  public DateTime Birthday { get; set; }
  public string City { get; set; }
  [JsonExtensionData] public Dictionary<string, object> ExtensionData { get; set; }
}

La deserialización de la información de entrada en la que tenemos tres datos «esperados» y otros datos «inesperados» quedará de la siguiente forma:

var json = "{\"Name\":\"Jorge\",\"Birthday\":\"1999-12-31T00:00:00\"," + 
  "\"City\":\"London\",\"Country\":\"UK\",\"Age\":\"30\"}";
var person = JsonSerializer.Deserialize(json);

De esta manera, no perderemos la información adicional no esperada que nos devuelve el JSON.

Creando nuestras propias políticas y opciones

Ahora bien, hemos visto que podemos serializar y deserializar documentos JSON gracias a System.Text.Json, y hemos visto como usar también opciones para llevar a cabo tareas adicionales con respecto a la serialización y deserialización.
Sin embargo, ¿qué ocurre si necesitamos aplicar alguna conversión u opción que no esté disponible de forma directa en la serialización y deserialización?.

Pues en este caso, tendremos que crearnos nuestra propia opción personalizada, algo que por supuesto, es posible hacer.

Para ello, deberemos crear una clase que deberá heredar a su vez de la clase JsonNamingPolicy, una clase abstracta de System.Text.Json.
Y dentro de esta clase, deberemos sobreescribir la función ConvertName.

De hecho, supongamos el siguiente sencillo ejemplo en el que vamos a convertir todas las propiedades en lowercase.

Nuestra clase especial podría quedar de la siguiente forma:

public class JsonLowerCaseNamingPolicy : JsonNamingPolicy
{
  public override string ConvertName(string name) => name.ToLower();        
}

Y la aplicación de esta política, podría quedar de la siguiente forma:

var person = new Person() { Name = "Jorge", Birthday = new DateTime(1999, 12, 31) };
var options = new JsonSerializerOptions() { PropertyNamingPolicy = new JsonLowerCaseNamingPolicy() };
var json = JsonSerializer.SerializeToUtf8Bytes(person, options);

La salida de cada propiedad en este caso será en formato lowercase.

No obstante, algo que no he comentado hasta ahora, es que es posible combinar varias opciones a la vez al serializar y deserializar, separando por comas cada una de las propiedades de opciones a utilizar.

JsonDocument y DOM

Otra de las características que tenemos dentro de System.Text.Json es la posibilidad de crear un DOM (Document Object Model) del JSON de partida en modo lectura, y acceder a sus elementos.

Veamos esto con un sencillo ejemplo.

var json = "{\"Name\":\"Jorge\",\"Birthday\":\"1999-12-31T00:00:00\"," +
  "\"City\":\"London\",\"Country\":\"UK\",\"Age\":\"30\"}";

using (JsonDocument document = JsonDocument.Parse(json))
{
  JsonElement root = document.RootElement;
  Console.WriteLine($"{root.GetProperty("City")} ({root.GetProperty("Country")})");
}

Evidentemente, éste es un ejemplo muy sencillo de uso de JsonDocument y DOM que utilizaremos en determinadas circunstancias en las que a partir de un JSON queremos acceder a un elemento o recorrerlos de forma rápida y sencilla.

 

Conclusiones finales

Como podemos observar, no es nada muy distinto a lo que otras librerías JSON permiten hacer, por lo que la migración a este namespace, tampoco debe representar un coste elevado si no hemos realizado personalizaciones muy especiales.

Cabe destacar igualmente, que muchos de los métodos indicados en estos ejemplos, contienen sus correspondientes métodos asíncronos Async.

Espero que este pequeño repaso general ayude a la hora de aplicar System.Text.Json en nuestras aplicaciones.

Happy Coding!

Comments

Deja un comentario Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

← Previous Post Next Post →

Jorge Serrano

MVP Reconnect


¡Subscríbete a mi canal!
YouTube

Donaciones
Donation

Entradas recientes

  • Go – Arrays
  • Go – Operators
  • Go – Constants
  • Go – Tipos de Datos
  • Go – Variables
  • Hello Go-rld!
  • Introducción a Go o Golang
  • JSON Patch en ASP.NET Core 5 Web API
  • Null Checking en C#
  • ¿Porqué mi página web por defecto de ASP.NET Core no se vé en mi Azure Web App y me da un 404?

Categorías

  • .NET 5
  • .NET Core
  • .NET Core 3.0
  • .NET Core 3.1
  • .NET Framework 2.0
  • .NET Framework 3.0
  • .NET Framework 3.5
  • .NET Framework 4.0
  • .NET Framework 4.5
  • .NET Framework 4.6
  • .NET Framework 4.7
  • .NET Framework 4.8
  • .NET Standard 2.0
  • .NET Standard 2.1
  • AMQP
  • Android
  • Angular
  • API REST
  • Apple
  • Apple iOS
  • Apple macOs
  • Arquitectura
  • ASP.NET
  • ASP.NET Core
  • ASP.NET Core 3
  • ASP.NET Core 5
  • AWS
  • Azure App Service
  • Azure Application Insights
  • Azure Cosmos DB
  • Azure Database Migration Service
  • Azure Databricks
  • Azure DevOps
  • Azure Event Grid
  • Azure Functions
  • Azure IoT
  • Azure Portal
  • Azure PowerShell
  • Azure Queue Storage
  • Azure SQL
  • Azure Storage
  • Azure Virtual Datacenter
  • Azure WebApps
  • Big Data
  • Bing
  • Blazor
  • Blog
  • Bots
  • C#
  • C# 7.0
  • C# 7.1
  • C# 7.2
  • C# 7.3
  • C# 8.0
  • C# 9.0
  • Channel 9
  • Codeplex
  • Codespaces
  • Containers
  • Debugging
  • DevOps
  • Docker
  • Electron
  • Entity Framework
  • Entity Framework Core
  • Entity Framework Core 3.0
  • Entity Framework Core 5
  • Eventos
  • F#
  • FaaS
  • FeatureFlags
  • FeatureToggles
  • Feeds
  • Fluent Assertions
  • General
  • GIMP
  • Git
  • GitHub
  • Go
  • Google
  • Google Analytics
  • Gradle
  • gRPC
  • GSA
  • Historia de la Informática
  • HoloLens
  • HtmlAgilityPack
  • IdentityServer4
  • Inkscape
  • Ionic
  • iOS
  • IoT
  • Java
  • JavaScript
  • JDBC
  • JSON
  • Kubernetes
  • Lenguajes de Programación
  • Libros y Cursos
  • LINQ
  • Linux
  • LiteDB
  • Machine Learning
  • macOS
  • Microservices
  • Microsoft
  • Microsoft .NET Framework 4.5
  • Microsoft 365
  • Microsoft Azure
  • Microsoft Build
  • Microsoft Ignite
  • Microsoft Learn
  • Microsoft Orleans
  • Microsoft Surface Go
  • Microsoft Teams
  • ML.NET
  • MQTT
  • MRO
  • MS-DOS
  • MsCoders Madrid
  • MVP
  • NancyFx
  • Node.js
  • NoSQL
  • NuGet
  • NUnit
  • OData
  • ODP.NET Core
  • Office 2007
  • Office 2010
  • Office 2013
  • Office 2016
  • Office 2019
  • Office 365
  • Open Source
  • Open XML SDK
  • Opinión
  • Orchard CMS
  • OT
  • PaaS
  • Patterns
  • PdfSharpCore
  • Performance
  • PHP
  • Postman
  • Power BI
  • PowerShell
  • PowerShell Core
  • Productividad
  • Project Server 2019
  • R
  • Rendimiento
  • Scala
  • Scraper
  • Security
  • Serverless
  • Service Fabric
  • SharePoint Server 2019
  • SignalR
  • Sin categoría
  • Sistemas Distribuidos
  • Skype
  • Skype for Business Server 2019
  • Small Basic Online
  • SQL Server 2005
  • SQL Server 2008
  • SQL Server 2012
  • SQL Server 2014
  • SQL Server 2016
  • SQL Server 2017
  • SQL Server 2019
  • STOMP
  • Swagger
  • Testing
  • TFS 2017
  • TFS 2018
  • Tools
  • TypeScript
  • Unity
  • UWP
  • UX
  • Visio
  • Visual Basic
  • Visual Studio 2010
  • Visual Studio 2012
  • Visual Studio 2013
  • Visual Studio 2015
  • Visual Studio 2017
  • Visual Studio 2017 for Mac
  • Visual Studio 2019
  • Visual Studio 2019 for Mac
  • Visual Studio App Center
  • Visual Studio Code
  • Visual Studio IntelliCode
  • Visual Studio Live Share
  • Visual Studio Live Share Audio
  • Visual Studio Online
  • VS Anywhere
  • Vue.js
  • Web API
  • WebAssembly
  • WinDbg
  • Windows
  • Windows 10
  • Windows Compatibility Pack
  • Windows Phone 10
  • Windows Phone 7
  • Windows Phone 8
  • Windows Server 2008
  • Windows Server 2012
  • Windows Server 2016
  • Windows Server 2019
  • Windows Service
  • WinForms
  • WinUI
  • WPF
  • Xamarin
  • Xbox
  • Xcode
  • Xiaomi Mi Band 2
  • xUnit
  • YAML

Archivos

  • enero 2021
  • diciembre 2020
  • noviembre 2020
  • octubre 2020
  • septiembre 2020
  • agosto 2020
  • julio 2020
  • junio 2020
  • mayo 2020
  • abril 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • diciembre 2019
  • noviembre 2019
  • octubre 2019
  • septiembre 2019
  • agosto 2019
  • julio 2019
  • junio 2019
  • mayo 2019
  • abril 2019
  • marzo 2019
  • febrero 2019
  • enero 2019
  • diciembre 2018
  • noviembre 2018
  • octubre 2018
  • septiembre 2018
  • agosto 2018
  • julio 2018
  • junio 2018
  • mayo 2018
  • abril 2018
  • marzo 2018
  • febrero 2018
  • enero 2018
  • diciembre 2017
  • noviembre 2017
  • octubre 2017
  • septiembre 2017
  • agosto 2017
  • julio 2017
  • junio 2017
  • febrero 2015
  • octubre 2014
  • junio 2014
  • marzo 2014
  • febrero 2014
  • enero 2014
  • diciembre 2013
  • septiembre 2013
  • agosto 2013
  • julio 2013
  • junio 2013
  • abril 2013
  • febrero 2013
  • enero 2013
  • diciembre 2012
  • noviembre 2012
  • septiembre 2012
  • agosto 2012
  • junio 2012
  • mayo 2012
  • abril 2012
  • marzo 2012
  • febrero 2012
  • enero 2012
  • diciembre 2011
  • noviembre 2011
  • octubre 2011
  • septiembre 2011
  • agosto 2011
  • julio 2011
  • junio 2011
  • mayo 2011
  • abril 2011
  • marzo 2011
  • enero 2011
  • diciembre 2010
  • noviembre 2010
  • octubre 2010
  • septiembre 2010
  • agosto 2010
  • julio 2010
  • junio 2010
  • mayo 2010
  • abril 2010
  • marzo 2010
  • febrero 2010
  • enero 2010
  • diciembre 2009
  • noviembre 2009
  • octubre 2009
  • septiembre 2009
  • agosto 2009
  • julio 2009
  • junio 2009
  • mayo 2009
  • abril 2009
  • marzo 2009
  • febrero 2009
  • enero 2009
  • diciembre 2008
  • noviembre 2008
  • octubre 2008
  • septiembre 2008
  • agosto 2008
  • julio 2008
  • junio 2008
  • mayo 2008
  • abril 2008
  • marzo 2008
  • febrero 2008
  • enero 2008
  • diciembre 2007
  • noviembre 2007
  • octubre 2007
  • septiembre 2007
  • agosto 2007
  • julio 2007
  • junio 2007
  • mayo 2007
  • abril 2007
  • marzo 2007
  • febrero 2007
  • enero 2007
  • diciembre 2006
  • noviembre 2006
  • octubre 2006
  • septiembre 2006
  • agosto 2006
  • julio 2006
  • junio 2006
  • mayo 2006
About This Site

A cras tincidunt, ut tellus et. Gravida scel ipsum sed iaculis, nunc non nam. Placerat sed phase llus, purus purus elit.

Archives Widget
  • January 2010
  • December 2009
  • November 2009
  • October 2009
Categories
  • Entertainment
  • Technology
  • Sports & Recreation
  • Jobs & Lifestyle
Search
  • twitter

Powered by WordPress  |  Business Directory by InkThemes.

This site uses cookies: Find out more.