Al migrar o trabajar con .NET 6, es común encontrarse con la biblioteca integrada System.Text.Json. Aunque potente, presenta algunos desafíos que difieren de la biblioteca de terceros Newtonsoft.Json (Json.NET). Este artículo detalla cómo abordar algunos de estos problemas frecuentes.
Problemas de Codificación de Caracteres
Por defecto, System.Text.Json tiende a escapar caracteres no ASCII, representándolos como secuencias \uxxxx. Esto puede ser indeseable en muchas aplicaciones. Para solucionar esto, podemos configurar las opciones de serialización para incluir un rango más amplio de caracteres Unicode.
Considermeos el siguiente objeto:
var datoEjemplo = new {
Nombre = "Prueba",
Valor = 456
};
var jsonSinConfig = JsonSerializer.Serialize(datoEjemplo);
Console.WriteLine(jsonSinConfig);
La salida predeterminada muestra los caracteres escapados:
{"Nombre":"\u0050\u0072\u0075\u0065\u0062\u0061","Valor":456}
Para mantener los caracteres legibles, podemos especificar un codificador que soporte un rango Unicode más amplio durante la serialización:
var opcionesSerializacion = new JsonSerializerOptions()
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
var jsonConConfig = JsonSerializer.Serialize(datoEjemplo, opcionesSerializacion);
Console.WriteLine(jsonConConfig);
La salida ahora muestra los caracteres correctamente:
{"Nombre":"Prueba","Valor":456}
Problemas de Escapado de Caracteres HTML
Incluso con la configuración anterior, las cadenas que contienen etiquetas HTML pueden ser escapadas por seguridad. Si se desea desactivar este comportamiento de escapado para HTML, se puede usar una configuración específica en el codificador.
Observemos este ejemplo con una cadena HTML:
var datoConHtml = new {
Nombre = "Ejemplo",
Valor = 789,
Etiqueta = "<div>Contenido</div>"
};
var opcionesEncoderAmplio = new JsonSerializerOptions {
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
var jsonHtmlEscapado = JsonSerializer.Serialize(datoConHtml, opcionesEncoderAmplio);
Console.WriteLine(jsonHtmlEscapado);
La salida muestra las etiquetas HTML escapadas:
{"Nombre":"Ejemplo","Valor":789,"Etiqueta":"\u003cdiv\u003eContenido\u003c/div\u003e"}
Para evitar este escapado, se utiliza JavaScriptEncoder.UnsafeRelaxedJsonEscaping:
var opcionesSinEscapadoHtml = new JsonSerializerOptions {
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var jsonSinEscapadoHtml = JsonSerializer.Serialize(datoConHtml, opcionesSinEscapadoHtml);
Console.WriteLine(jsonSinEscapadoHtml);
La salida correcta, sin escapar HTML:
{"Nombre":"Ejemplo","Valor":789,"Etiqueta":"<div>Contenido</div>"}
Problemas de Referencias Cíclicas (Objetos Anidados)
Al serializar objetos con relaciones circulares (por ejemplo, un objeto Usuario que tiene una lista de Publicaciones, y cada Publicacion tiene un autor Usuario), System.Text.Json puede lanzar una excepción JsonException indicando un ciclo de objeto o una profundidad de recursión excesiva.
Definamos las clases de ejemplo:
internal class ObjetoBase {
public string Id { get; set; }
}
internal class TareaCientifica : ObjetoBase {
public string Descripcion { get; set; }
public Investigador Investigador { get; set; }
public string InvestigadorId { get; set; }
}
internal class Investigador : ObjetoBase {
public string Nombre { get; set; }
public List<TareaCientifica> Tareas { get; set; }
}
Intentemos serializar un escenario con referencia circular:
// Datos de ejemplo
var tarea = new TareaCientifica { Descripcion = "Análisis de datos", InvestigadorId = "inv-101" };
var investigador = new Investigador { Nombre = "Dr. Smith", Tareas = new List<TareaCientifica> { tarea } };
tarea.Investigador = investigador;
tarea.Id = "tarea-505";
investigador.Id = "inv-101";
// Serialización que provocará error
try
{
var jsonCircular = JsonSerializer.Serialize(tarea);
Console.WriteLine(jsonCircular);
}
catch (JsonException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
Esto resultará en una excepción JsonException. La solución en .NET 6 es utilizar el manejador de referencias ReferenceHandler.IgnoreCycles en las opciones de serialización.
var opcionesCiclos = new JsonSerializerOptions {
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true, // Para una salida legible
ReferenceHandler = ReferenceHandler.IgnoreCycles
};
var jsonConManejoCiclos = JsonSerializer.Serialize(tarea, opcionesCiclos);
Console.WriteLine(jsonConManejoCiclos);
La salida mostrará la referencia circular reemplazada por null, evitando la excepción:
{
"Descripcion": "Análisis de datos",
"Investigador": {
"Nombre": "Dr. Smith",
"Tareas": [
null
],
"Id": "inv-101"
},
"InvestigadorId": "inv-101",
"Id": "tarea-505"
}
Es importante notar que el comportamiento de ReferenceHandler.IgnoreCycles difiere ligeramente de ReferenceLoopHandling.Ignore de Newtonsoft.Json. Mientras que System.Text.Json puede representar la propiedad circular como un array con un elemento nulo (como se ve en Tareas: [null]), Newtonsoft.Json tiende a serializar toda la propiedad como null.