1. Introdução
Por mais que a gente queira acreditar que coleções se serializam e desserializam perfeitamente “out of the box”, em projetos reais nem sempre é assim. Às vezes é preciso esconder certas coleções da serialização — por exemplo, dados de cache internos. Às vezes é necessário renomear propriedades de coleções para atender ao contrato da API. Em alguns casos é importante controlar quais elementos são preservados ou ignorados, ou até transformar a coleção de um jeito que o JSON final fique legível e “amigável” para outros serviços.
Felizmente, o System.Text.Json oferece uma forma simples e transparente de controlar a serialização usando atributos que podem ser aplicados tanto a coleções quanto a elementos individuais. Nesta seção vamos evoluir nosso modelo de biblioteca para entender como isso funciona na prática.
2. Excluir propriedades de coleção: [JsonIgnore]
Começamos pelo básico. Às vezes sua classe tem uma coleção que não deve ser serializada — por exemplo, dados temporários, cache ou informações sensíveis. O que fazer? Claro: [JsonIgnore]!
Suponha que temos a classe Library com a propriedade List<Book> Cache, usada só para acesso rápido:
using System.Text.Json.Serialization;
public class Library
{
public string Name { get; set; }
public List<Book> Books { get; set; }
[JsonIgnore]
public List<Book> Cache { get; set; } // Não é serializado!
}
// Exemplo de uso:
var library = new Library
{
Name = "Biblioteca Principal",
Books = new List<Book>
{
new Book { Title = "Vale Encantado", Author = new Author { Name = "Tove Jansson", BirthYear = 1914 } }
},
Cache = new List<Book>
{
new Book { Title = "O Senhor das Moscas", Author = new Author { Name = "William Golding", BirthYear = 1911 } }
}
};
string json = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json); // No JSON não existe a propriedade Cache!
O resultado da serialização ficará mais ou menos assim:
{
"Name": "Biblioteca Principal",
"Books": [
{
"Title": "Vale Encantado",
"Author": {
"Name": "Tove Jansson",
"BirthYear": 1914
}
}
]
}
Viu? Nada de “caches” no mundo externo. Tudo que está sob [JsonIgnore] — fica escondido e seguro, como a senha do Wi-Fi na sua cabeça.
3. Renomear coleções com [JsonPropertyName]
Com frequência você encontra APIs que esperam, por exemplo, "items" em vez de "Books". Ou você não quer renomear o campo em C# (pra não se confundir), mas no JSON ele precisa “soar” diferente.
Aqui está como fazer:
using System.Text.Json.Serialization;
public class Library
{
public string Name { get; set; }
[JsonPropertyName("items")]
public List<Book> Books { get; set; }
[JsonIgnore]
public List<Book> Cache { get; set; }
}
// Serialização:
var library = new Library { Name = "Filial #1", Books = new List<Book>() };
string json = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);
Saída:
{
"Name": "Filial #1",
"items": []
}
Repare que a desserialização também vai mapear corretamente items do JSON para Books no C# — a mágica funciona nos dois sentidos.
4. Controlando a serialização de coleções e seus elementos
Para isso você vai usar JsonIgnoreCondition.WhenWritingNull e/ou tipos nullable.
Pode acontecer de uma coleção ser um campo opcional. Por exemplo, uma biblioteca criada agora pode ainda não ter livros. Se você não quer que apareça no JSON a propriedade books: null, dá pra controlar isso com opções:
var library = new Library { Name = "Biblioteca Vazia" };
// Books não inicializado = null
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
WriteIndented = true
};
string json = JsonSerializer.Serialize(library, options);
Console.WriteLine(json);
Resultado:
{
"Name": "Biblioteca Vazia"
}
Se você tiver uma lista vazia (mas não null), o serializador vai emitir "books": []. Essa diferença é importante: às vezes você quer ocultar o campo quando ele é null, mas não quando é uma lista vazia.
5. O atributo [JsonIgnore] em propriedades de elementos
Atributos de serialização também funcionam dentro dos elementos da coleção. Dá pra esconder propriedades individuais de cada objeto na lista.
public class Book
{
public string Title { get; set; }
public Author Author { get; set; }
[JsonIgnore]
public string InternalCode { get; set; }
}
Agora, ao serializar um Book dentro de Books, a propriedade InternalCode não vai aparecer no JSON.
6. Endereçamento de coleções via “índices” ou estruturas aninhadas
Às vezes precisamos serializar coleções não apenas como arrays, mas como “maps” (dictionary) — por exemplo, quando cada livro tem um identificador único. Nesse caso — sem atributos mirabolantes para elementos, mas usando recursos padrão — você declara uma propriedade de dicionário:
public class Library
{
[JsonPropertyName("catalog")]
public Dictionary<string, Book> BookCatalog { get; set; }
}
Ao serializar, o dicionário vira um objeto com pares chave-valor:
var library = new Library
{
BookCatalog = new Dictionary<string, Book>
{
["978-5-699-12345-6"] = new Book { Title = "Dicionário", Author = new Author { Name = "Desconhecido", BirthYear = 2000 } }
}
};
JSON:
{
"catalog": {
"978-5-699-12345-6": {
"Title": "Dicionário",
"Author": {
"Name": "Desconhecido",
"BirthYear": 2000
}
}
}
}
Essa representação é conveniente para APIs onde é importante manter a associação entre chave e objeto.
7. Erros e dificuldades ao gerenciar a serialização de coleções
Se você tentar serializar uma coleção que tem elementos não inicializados (por exemplo, na lista existem null), por padrão o System.Text.Json vai escrever esses elementos como null no array.
Mesmo que você tenha definido DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, elementos-null dentro do array vão permanecer — essa regra se aplica a propriedades do objeto, não ao conteúdo das coleções. Para evitar isso, limpe a coleção antes: RemoveAll(b => b == null).
Uma confusão comum na desserialização é o mismatch de nomes. Se esquecer de aplicar [JsonPropertyName], a classe vai esperar a propriedade Books, mas você enviou JSON com items: como resultado a coleção não será preenchida e vai ficar vazia. Sempre verifique se os nomes batem!
8. Tabela: onde os principais atributos se aplicam
| Atributo | Pode ser aplicado a coleções? | Pode ser aplicado a elementos de coleções? | Exemplos de uso |
|---|---|---|---|
|
sim | sim | Esconder a lista ou um campo dentro de Book |
|
sim | sim | Renomear Books → items ou Title → name |
|
sim | sim | Incluir propriedades privadas na serialização |
|
sim | sim | Atribuir um converter especial para a lista |
9. Esquema de serialização de coleções com atributos
+-------------+
| Library |
+-------------+
| Name -- serializa como "Name"
| Books -- [JsonPropertyName("items")], serializa como "items": [...]
| Cache -- [JsonIgnore], não serializa
| BookCatalog -- [JsonPropertyName("catalog")], serializa como "catalog": {...}
Resultado JSON mais ou menos assim:
{
"Name": "Biblioteca Municipal",
"items": [
{
"Title": "1984",
"Author": {
"Name": "George Orwell",
"BirthYear": 1903
}
},
{
"Title": "Grandes Esperanças",
"Author": {
"Name": "Charles Dickens",
"BirthYear": 1812
}
}
],
"catalog": {
"978-1234567890": {
"Title": "O Chamado de Cthulhu",
"Author": {
"Name": "Howard Phillips Lovecraft",
"BirthYear": 1890
}
}
}
}
10. Relevância prática e pontos em entrevistas e projetos reais
Em produção você precisa sempre considerar o contrato da API externa e os requisitos de serialização. É preciso saber “esconder” coleções internas, corresponder o casing e o estilo de nomes, e às vezes até mudar dinamicamente o esquema de serialização dependendo da versão do cliente.
Perguntas típicas em entrevistas:
- Como serializar apenas uma parte dos dados?
- Como fazer para uma propriedade de coleção não aparecer no JSON?
- Como mapear nomes de propriedades entre C# e JSON quando são diferentes?
- É possível esconder elementos individuais dentro de uma coleção da serialização (por exemplo, informações confidenciais)?
As respostas giram em torno do uso correto de atributos e opções de serialização: [JsonIgnore], [JsonPropertyName], as opções de JsonSerializerOptions e um trabalho cuidadoso com o conteúdo das coleções.
GO TO FULL VERSION