CodeGym /Cursos /C# SELF /Configuração de serialização (

Configuração de serialização ( JsonSerializerOptions)

C# SELF
Nível 45 , Lição 3
Disponível

1. Introdução

Tudo bem quando suas necessidades batem com o comportamento padrão. Mas com frequência o cliente (ou um backend de terceiros) exige um formato rígido: em todo lugar camelCase, datas — ISO 8601, pular null, conversores customizados, e outras delícias do integrador.

É aí que entra em cena nosso novo amigo — a classe JsonSerializerOptions.

Se serialização fosse cozinha, essa classe são seus temperos, panelas e ingredientes secretos que você pode usar para dar sabor à serialização.

Como usar JsonSerializerOptions

Para customizar o processo de serialização ou desserialização, a gente só passa um objeto de opções pros métodos do serializer:

using System.Text.Json;

var options = new JsonSerializerOptions
{
    // Aqui vão suas configurações
};

string json = JsonSerializer.Serialize(myObject, options);
var obj = JsonSerializer.Deserialize<MyType>(json, options);

Esse padrão aparece por todo lado: você cria JsonSerializerOptions, configura do seu jeito (veja abaixo) e passa pros métodos do serializer Serialize/Deserialize.

Importante: O mesmo objeto JsonSerializerOptions pode (e é até recomendável) ser reaproveitado em várias serializações.

2. Configurações principais de JsonSerializerOptions

Vamos ver os "controles" mais populares e os "switches" dessa classe.

Formatar JSON — WriteIndented

As strings JSON de uma linha te incomodam? Sem problemas — liga a formatação bonita com indentação!

var options = new JsonSerializerOptions
{
    WriteIndented = true // JSON bonito, "legível por humanos"
};

var person = new Person { Name = "Anna", Age = 30 };
string json = JsonSerializer.Serialize(person, options);

Console.WriteLine(json);

Saída:

{
  "Name": "Anna",
  "Age": 30
}

Sem WriteIndented = true você teria o chato {"Name":"Anna","Age":30}. Em produção costuma-se usar JSON "minificado" (pesa menos), e pra debug/logs usa-se formatado.

Case-naming — PropertyNamingPolicy

Muitas vezes a API exige que nomes das propriedades estejam em camelCase (por exemplo, firstName em vez de FirstName). No .NET as propriedades geralmente são PascalCase, e aí entra PropertyNamingPolicy:

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

var person = new Person { Name = "Oleg", Age = 25 };
string json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json); // {"name":"Oleg","age":25}

Se precisar de um estilo próprio (por exemplo, tudo EM_MAIÚSCULAS), você pode implementar sua própria JsonNamingPolicy. Normalmente CamelCase já resolve.

3. Ignorando propriedades

Pular ou não pular valores nullDefaultIgnoreCondition

Às vezes é preciso que campos com valor vazio (null) não apareçam no JSON. Motivo — JSON mais compacto, ou porque o sistema externo não lida bem com null.

Pra isso usamos a propriedade DefaultIgnoreCondition:

using System.Text.Json.Serialization;

var options = new JsonSerializerOptions
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

var person = new Person { Name = null, Age = 22 };
string json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json); // {"Age":22}

Se quiser ignorar não só null, mas também valores default (por exemplo, int=0), use WhenWritingDefault:

options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault;

Serializar fields privados — IncludeFields e [JsonInclude]

Por padrão só são serializadas propriedades públicas com setter público. Se quiser serializar também fields, você pode ativar:

var options = new JsonSerializerOptions
{
    IncludeFields = true
};

Exemplo de classe:

public class Item
{
    public string Name;
    public int Id { get; set; }
}

var item = new Item { Name = "Book", Id = 12 };
string json = JsonSerializer.Serialize(item, options);
// {"Name":"Book","Id":12}

Pra serializar campos/propriedades privadas você pode usar o atributo [JsonInclude], mas isso é assunto pra outra palestra.

4. Conversão de valores

Formatando datas — Converters

Por padrão o System.Text.Json serializa datas no formato ISO 8601 (por exemplo, "2023-12-27T15:30:45.123Z"), o que quase sempre é conveniente. Mas às vezes o formato de data/hora tem que ser especial.

Aí a gente adiciona conversores customizados. Exemplo básico:

using System.Text.Json;
using System.Text.Json.Serialization;

public class CustomDateTimeConverter : JsonConverter<DateTime>
{
    private string _format = "yyyyMMdd";

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var date = reader.GetString();
        return DateTime.ParseExact(date, _format, null);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(_format));
    }
}

// Usando o conversor
var options = new JsonSerializerOptions();
options.Converters.Add(new CustomDateTimeConverter());

var dateObj = new { Date = new DateTime(2022, 1, 5) };
string json = JsonSerializer.Serialize(dateObj, options); // {"Date":"20220105"}

5. Dicas úteis

Ignorar case dos nomes das propriedades na desserialização — PropertyNameCaseInsensitive

var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};

var json = "{\"name\":\"Vasya\",\"age\":33}";
var person = JsonSerializer.Deserialize<Person>(json, options);
// person.Name == "Vasya"

Controlar profundidade e nível máximo de objetos — MaxDepth

var options = new JsonSerializerOptions
{
    MaxDepth = 32 // Por padrão 64
};

Mas se seus objetos podem ser infinitamente aninhados — por exemplo, uma árvore com referências a pais e filhos — é melhor repensar o design do modelo.

Comentários em JSON — ReadCommentHandling (desserialização)

JSON oficialmente não suporta comentários, mas às vezes aparecem arquivos "criativos" com comentários //.

var options = new JsonSerializerOptions
{
    ReadCommentHandling = JsonCommentHandling.Skip
};

string json = "{\n  \"Name\": \"Ivan\", // Nome do usuário\n  \"Age\": 30\n}";
var person = JsonSerializer.Deserialize<Person>(json, options);

Suporte a caracteres especiais — Encoder

Às vezes é preciso controlar como o serializer escapa caracteres (por exemplo, pra não converter cirílico em \uXXXX). Dá pra especificar um encoder:

using System.Text.Encodings.Web;

var options = new JsonSerializerOptions
{
    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};

Use com cuidado, senão seus emojis/ideogramas podem de repente "virar" sequências Unicode!

6. Erros típicos ao configurar serialização

Erro nº1: esquecer o setter público. Um campo ou propriedade não é serializado porque não tem setter público, ou é privado.

Erro nº2: ignorar incorretamente valores null. Com DefaultIgnoreCondition ativado, propriedades com null não aparecerão no JSON, mesmo que você não esperasse isso.

Erro nº3: formato de data errado. A data é serializada de forma diferente do esperado — adicione um conversor customizado (JsonConverter).

Erro nº4: esperar uma ordem fixa de propriedades. O padrão JSON não garante ordem de campos. Se uma API exige ordem estrita, você terá que mudar o modelo ou usar bibliotecas externas.

Erro nº5: configurações diferentes na serialização e desserialização. Se você não usa os mesmos JsonSerializerOptions pra escrita e leitura do JSON, podem aparecer problemas, por exemplo com case dos nomes ou formatos.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION