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 null — DefaultIgnoreCondition
À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.
GO TO FULL VERSION