Manejo de Caracteres Especiales en Claves JSON con C# y DataContract

Al trabajar con datos JSON en aplicaciones C#, la deserialización de objetos es una tarea común. Sin embargo, surge un desafío particular cuando las claves dentro de la estructura JSON contienen caracteres especiales, como @ o $, que no son válidos para nombres de propiedades en C#. Este artículo explora cómo abordar esta situación utilizando los atributos DataContract y DataMember del framework .NET.

El Problema con Claves Especiales

Considere el siguiente fragmento JSON:

{
   "codigo" : "PRD001",
   "@nombreProducto" : "Laptop Gaming X",
   "$precioUnitario" : 1299.99
}

Un intento directo de mapear esto a una clase C# de la forma habitual (por ejemplo, public string codigo { get; set; }) funcionaría para la clave "codigo". Pero, ¿qué pasa con "@nombreProducto" o "$precioUnitario"? No podemos declarar propiedades como public string @nombreProducto { get; set; }.

La Solución: Atributos DataContract y DataMember

La biblioteca System.Runtime.Serialization.Json, en combinación con los atributos DataContract y DataMember, proporciona una solución robusta para este escenario. Estos atributos permiten definir un contrato de datos explícito, donde podemos epsecificar el nombre de la clave JSON asociada a cada propiedad de la clase C#.

A continuación, se muestra cómo definir una clase C# que pueda deserializar el JSON de ejemplo:

using System.Runtime.Serialization;

[DataContract]
public class InfoProducto
{
    [DataMember(Order = 0, Name = "codigo")]
    public string Identificador { get; set; }

    [DataMember(Order = 1, Name = "@nombreProducto")]
    public string NombreCompleto { get; set; }

    [DataMember(Order = 2, Name = "$precioUnitario")]
    public decimal CostoPorUnidad { get; set; }
}

En este ejemplo:

  • El atributo [DataContract] marca la clase como un tipo que participa en un contrato de datos.
  • El atributo [DataMember] se aplica a cada propiedad que se desea serializar o deserializar.
  • La propiedad Name dentro de [DataMember(Name = "...") es crucial, ya que permite especificar la clave exacta del JSON, incluidos los caracteres especiales.
  • Order es opcional, pero puede usarse para especificar el orden de los miembros en el JSON serializado.

Ejemplo Completo de Serialización y Deserialización

Para demostrar el proceso completo, consideremos el siguiente código que deserializa el JSON de ejemplo, modifica un valor y luego lo serializa de nuevo a una cadena JSON.

using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

namespace JsonSpecialKeys
{
    class Program
    {
        static void Main(string[] args)
        {
            // JSON de entrada con claves especiales
            string jsonEntrada = "{\"codigo\":\"PRD001\",\"@nombreProducto\":\"Laptop Gaming X\",\"$precioUnitario\":1299.99}";

            // Instanciar el serializador para la clase InfoProducto
            DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(InfoProducto));

            Console.WriteLine("--- Deserialización ---");
            // Deserializar el JSON a un objeto InfoProducto
            InfoProducto miProducto;
            using (MemoryStream msInput = new MemoryStream(Encoding.UTF8.GetBytes(jsonEntrada)))
            {
                miProducto = jsonSerializer.ReadObject(msInput) as InfoProducto;
            }

            // Mostrar valores deserializados
            Console.WriteLine($"Identificador: {miProducto.Identificador}");
            Console.WriteLine($"Nombre: {miProducto.NombreCompleto}");
            Console.WriteLine($"Costo: {miProducto.CostoPorUnidad:C}");

            // Modificar un valor antes de la serialización
            miProducto.CostoPorUnidad = 1199.50m;
            miProducto.NombreCompleto += " (Oferta)";

            Console.WriteLine("\n--- Serialización ---");
            // Serializar el objeto InfoProducto de nuevo a una cadena JSON
            string jsonSalida;
            using (MemoryStream msOutput = new MemoryStream())
            {
                jsonSerializer.WriteObject(msOutput, miProducto);
                msOutput.Position = 0; // Resetear la posición del stream para la lectura
                using (StreamReader reader = new StreamReader(msOutput, Encoding.UTF8))
                {
                    jsonSalida = reader.ReadToEnd();
                }
            }
            
            Console.WriteLine($"JSON Resultante:\n{jsonSalida}");

            Console.ReadKey();
        }
    }

    [DataContract]
    public class InfoProducto
    {
        [DataMember(Order = 0, Name = "codigo")]
        public string Identificador { get; set; }

        [DataMember(Order = 1, Name = "@nombreProducto")]
        public string NombreCompleto { get; set; }

        [DataMember(Order = 2, Name = "$precioUnitario")]
        public decimal CostoPorUnidad { get; set; }
    }
}

Este código demuestra cómo la deserialización traduce correctamente las claves JSON con caracteres especiales a las propeidades de C# definidas con DataMember(Name = "..."). Posteriormente, el objeto modificado se serializa de nuevo, manteniendo la estructura original de las claves JSON.

Etiquetas: C# json deserialización Serialización DataContract

Publicado el 9-8 11:06