CodeGym /Cursos /C# SELF /Serialización con System.T...

Serialización con System.Text.Json

C# SELF
Nivel 44 , Lección 3
Disponible

1. Introducción

Durante mucho tiempo en el ecosistema .NET la herramienta principal para trabajar con JSON fue el estupendo paquete externo Newtonsoft.Json (también conocido como Json.NET). Es potente, flexible y sigue usándose mucho. Pero con la llegada de nuevas versiones .NET 9 y C# 14, Microsoft decidió que ya tocaba tener su propio serializador JSON integrado y de alto rendimiento. Así apareció System.Text.Json.

¿Por qué un enfoque nuevo? System.Text.Json está diseñado pensando en las realidades modernas y soluciona problemas acumulados tras años de uso de bibliotecas externas. Está optimizado para máxima velocidad y seguridad, encaja genial en escenarios async y Web API, y —lo mejor— no requiere instalación vía NuGet: ya viene con la plataforma.

Claro, Newtonsoft.Json no ha desaparecido, y lo veremos más adelante. Pero para la mayoría de proyectos nuevos System.Text.Json es la elección por defecto. Prepárate: ahora vamos a enseñar a nuestros objetos a hablar JSON.

2. Fundamentos de System.Text.Json

Serialización simple de un objeto

Bueno, empecemos con la magia básica. Serialicemos nuestro objeto a una cadena JSON.

using System;
using System.Text.Json; // ¡No olvides añadirlo!

public class Player
{
    public string Name { get; set; }
    public int Health { get; set; }
    public bool IsAlive { get; set; }
}

// En alguna parte de tu programa:
Player player = new Player { Name = "Aragorn", Health = 100, IsAlive = true };

// Serialización:
string json = JsonSerializer.Serialize(player);
Console.WriteLine(json); // Mostrará: {"Name":"Aragorn","Health":100,"IsAlive":true}

Comentario: Si recién empiezas con la serialización, este ejemplo muestra lo sencillo que es: JsonSerializer.Serialize — y listo.

Deserialización de un objeto

Resucitemos un objeto desde una cadena JSON:

string incomingJson = "{\"Name\":\"Legolas\",\"Health\":88,\"IsAlive\":true}";

Player player2 = JsonSerializer.Deserialize<Player>(incomingJson);
Console.WriteLine(player2.Name);   // Legolas
Console.WriteLine(player2.Health); // 88
Console.WriteLine(player2.IsAlive); // true

Comentario: Si la estructura del JSON coincide con tu clase —todo funciona como un reloj. Si no —pueden aparecer excepciones o valores por defecto.

3. Principios y mecánica de la serialización

Cómo ocurre el mapeo

System.Text.Json por defecto usa los mismos nombres de propiedad que en la clase. ¡El case importa! Si en el JSON está health en lugar de Health, la deserialización no funcionará —la propiedad quedará con el valor por defecto (0, false o null).

Por ejemplo:

// JSON con claves en minúsculas:
string badJson = "{\"name\":\"Gimli\",\"health\":120,\"isAlive\":true}";
Player player3 = JsonSerializer.Deserialize<Player>(badJson);
Console.WriteLine(player3.Name);    // vacío
Console.WriteLine(player3.Health);  // 0
Console.WriteLine(player3.IsAlive); // false

Dato curioso: Muchos API usan claves en camelCase (health), mientras que en C# es habitual PascalCase (Health). Esto se soluciona con configuraciones (más abajo).

4. Controlando la serialización — opciones y configuraciones

Formateo JSON: salida "legible"

A veces queremos JSON bonito, no compacto —para configs o logs.

var options = new JsonSerializerOptions
{
    WriteIndented = true // Añadir indentación
};

string prettyJson = JsonSerializer.Serialize(player, options);
Console.WriteLine(prettyJson);
/*
{
  "Name": "Frodo",
  "Health": 50,
  "IsAlive": true,
  "Inventory": [
    "Ring",
    "Bread",
    "Torch"
  ],
  "Position": {
    "X": 5,
    "Y": 15
  }
}
*/

Se usa la propiedad WriteIndented, y el serializador añade saltos de línea y espacios.

Control del estilo de nombres (CamelCase vs PascalCase)

Si trabajas con un Web API donde todas las claves están en camelCase, activa la política de nombres:

var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

string camelCaseJson = JsonSerializer.Serialize(player, options);
// {"name":"Frodo","health":50,"isAlive":true,"inventory":["Ring","Bread","Torch"],"position":{"x":5,"y":15}}

Así, en la deserialización esas claves se mapearán correctamente:

string apiJson = "{\"name\":\"Bilbo\",\"health\":40,\"isAlive\":true,\"inventory\":[\"Mug\"],\"position\":{\"x\":10,\"y\":5}}";
Player bilbo = JsonSerializer.Deserialize<Player>(apiJson, options);
Console.WriteLine(bilbo.Name); // Bilbo

5. Matices útiles

Usando el atributo [JsonIgnore]

A veces no quieres serializar todas las propiedades —por ejemplo datos privados o valores calculados temporales.

using System.Text.Json.Serialization;

public class Player
{
    public string Name { get; set; }
    public int Health { get; set; }

    [JsonIgnore] // Esta propiedad no irá al JSON
    public bool IsSecretCharacter { get; set; }
}

Ahora al serializar:

var player = new Player { Name = "Boromir", Health = 80, IsSecretCharacter = true };
string json = JsonSerializer.Serialize(player);
Console.WriteLine(json); // {"Name":"Boromir","Health":80}

Al deserializar, IsSecretCharacter obtendrá el valor por defecto (false).

Usando [JsonPropertyName("...")]

Imagina que en tu código la propiedad se llama IsAlive, pero en el JSON debe ser "status":

using System.Text.Json.Serialization;

public class Player
{
    public string Name { get; set; }
    public int Health { get; set; }

    [JsonPropertyName("status")]
    public bool IsAlive { get; set; }
}

La serialización quedará así:

var player = new Player { Name = "Pippin", Health = 60, IsAlive = false };
string json = JsonSerializer.Serialize(player);
Console.WriteLine(json); // {"Name":"Pippin","Health":60,"status":false}

Y al deserializar, la clave "status" rellenará correctamente la propiedad IsAlive.

Limitaciones y consideraciones de seguridad integradas

  • Por defecto se serializan solo las propiedades públicas con getters/setters; campos/propiedades privadas se ignoran.
  • En enlaces cíclicos se lanza excepción: "A possible object cycle was detected".
  • A diferencia de Newtonsoft.Json, el serializador estándar depende menos de trucos "mágicos" con tipos —lo que lo hace más seguro y rápido para escenarios comunes.

6. Errores comunes y trampas

Accidentalmente cambias el nombre de una propiedad en el JSON y olvidas actualizar el código —como resultado la propiedad obtiene el valor por defecto (null, 0, false).

Falta un campo necesario en el JSON —la propiedad correspondiente del objeto quedará por defecto (ver la documentación).

La propiedad no tiene setter público —no se llenará en la deserialización.

Cambiaste la estructura de clases anidadas o colecciones —la deserialización puede romperse o producir resultados inesperados.

Nombres iguales en distintos niveles de anidamiento (en el padre y en el hijo) confunden y dificultan el debug.

A veces la causa es la versión de la plataforma: versiones antiguas de System.Text.Json tenían peores resultados con ciertos tipos (por ejemplo, Dictionary, DateTime, enum), pero en .NET 7/8/9 mucho está corregido.

2
Tarea
C# SELF, nivel 44, lección 3
Bloqueada
Deserialización usando JsonPropertyName
Deserialización usando JsonPropertyName
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION