Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 21 de junio de 2022
.NET

De casualidad me he topado con un interesante cambio que .NET 5 introdujo en los componentes de serialización y deserialización System.Text.Json y que, al menos para mí, pasó totalmente desapercibido en su momento. Por eso, he pensado que quizás sea buena idea dejarlo por aquí, por si hay algún despistado más al que pueda resultar útil.

Como seguro sabéis, al usar los componentes de System.Text.Json para serializar o deserializar una clase, utilizamos el atributo [JsonIgnore] para marcar las propiedades que queremos que sean ignoradas al convertir desde y hacia JSON.

Por ejemplo, dada la siguiente clase:

class User
{
    public int Id { get; set; }
    public string Email { get; set; }
    [JsonIgnore]
    public string Token { get; set; }
}

En ella estamos indicando expresamente que la propiedad Token debe ser ignorada, por lo que ésta no aparecerá si serializamos un objeto a JSON:

var user = new User { Id = 42, Email = "john@server.com", Token = "ABCDEF"};
Console.WriteLine(JsonSerializer.Serialize(user));

// Result:
{"Id":42,"Email":"john@server.com"}

Y lo mismo ocurre en sentido contrario:

var jsonStr = "{ \"Id\": 42, \"Email\": \"john@server.com\", \"Token\": \"ABCDEF\"}";
var user = JsonSerializer.Deserialize<User>(jsonStr);
// ¡user.Token es nulo aquí!

La novedad introducida en .NET 5 es que JsonIgnore ahora soporta un parámetro de tipo JsonIgnoreCondition que permite especificar ciertas condiciones para que la propiedad afectada sea ignorada, por ejemplo:

class User
{
    public int Id { get; set; }
    public string Email { get; set; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string Token { get; set; }
}

JsonIgnoreCondition es un enumerado con los siguientes miembros, cuya utilidad puede intuirse simplemente al verlos:

public enum JsonIgnoreCondition
{
    Never,
    Always,
    WhenWritingDefault,
    WhenWritingNull,
}

El valor Never indica que la propiedad no debe ignorarse nunca, por lo que será equivalente a no decorarla con [JsonIgnore].

Always indica que la propiedad debe ser ignorada siempre, así que es el equivalente a decorar con [JsonIgnore] a secas.

Con WhenWritingDefault indicamos que la propiedad debe ser ignorada cuando se trate de escribir (serializar) el valor por defecto del tipo de datos de la misma. Por ejemplo, en el siguiente código, la serialización omitirá la propiedad id cuando ésta valga cero (el valor por defecto del tipo int):

class User
{
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public int Id { get; set; }
    public string Email { get; set; }
}

var user = new User { Email = "john@server.com" };
Console.WriteLine(JsonSerializer.Serialize(user));

var user42 = new User { Id=42, Email = "peter@server.com" };
Console.WriteLine(JsonSerializer.Serialize(user42));

// Result:
{"Email":"john@server.com"}
{"Id":42, "Email":"peter@server.com"}

De forma muy similar, WhenWritingNull omitirá la serialización de la propiedad cuando su valor sea nulo.

class User
{
    public string Email { get; set; }
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string Token { get; set; }
}

var userWithToken = new User { Email = "john@server.com", Token="ABCDEF" };
Console.WriteLine(JsonSerializer.Serialize(userWithToken));

var userWithoutToken = new User { Email = "peter@server.com" };
Console.WriteLine(JsonSerializer.Serialize(userWithoutToken));

// Result:
{"Email":"john@server.com","Token":"ABCDEF"}
{"Email":"peter@server.com"}

Fijaos que en tipos referencia como un string, WhenWritingNull y WhenWritingDefault son equivalentes.

En definitiva, no es algo que no pudiéramos hacer antes con un pequeño conversor personalizado, pero ciertamente así lo tenemos más a mano :)

Publicado en Variable not found.

2 Comentarios:

Jato dijo...

Hola!
y si quisiéramos que JsonIgnore fuera condicional?
haciendo pruebas, y asignando "[JsonIgnore(Condition = LlamadaAUnaFuncion())]" da un error en compilación, ya que únicamente permite valores constantes.

Hay forma de de condicionarlo?
Un abrazo.

José María Aguilar dijo...

Hola!

El atributo no soporta esa posibilidad, y tampoco podemos heredar de él para hacer una implementación personalizada.

Pero quizás podrías conseguir lo que pretendes usando un conversor personalizado. Puedes leer algo sobre ellos aquí: Deserialización y serialización personalizada en .NET con System.Text.Json usando custom converters.

La forma de hacerlo depende de la lógica que quieras aplicar para decidir la forma de serialización. Podrías crear un custom serializador para la clase completa, o hacerlo para aplicarlo en campos individuales con [JsonConverter].

Saludos!