1. Para que servem os atributos na serialização?
Quando você serializa objetos para JSON ou XML, acontece algo parecido com um robô aspirador vasculhando seu quarto. Tudo que está visível (propriedades/fields públicos) vai para o "saco" (arquivo ou string), e o resto é ignorado. Mas às vezes você não quer que o robô exponha o conteúdo da sua mochila, ou quer dar nomes diferentes às coisas — por exemplo, não em russo, mas em inglês.
Aí entram os atributos! Eles deixam você controlar o processo de serialização: ocultar coisas, mudar nomes, definir ordem, ignorar partes do objeto ou dizer ao serializer que existem regras especiais. É como colocar adesivos nos itens: "não mexer", "importante", "renomear no JSON".
Tarefas típicas de controle de serialização
- Mudar o nome de uma propriedade ou campo no documento de saída (por exemplo, no JSON em vez de FirstName usar "first_name")
- Ocultar alguns campos/propriedades da serialização ou desserialização (por exemplo, senhas, contadores internos)
- Controlar tratamento de valores padrão ou null
- Definir a ordem dos elementos (relevante para XML)
- Descrever parâmetros adicionais (atributos) para elementos XML
Cada plataforma de serialização — seja System.Text.Json, Newtonsoft.Json ou XmlSerializer — usa seus próprios atributos para esses fins.
2. Atributos para System.Text.Json: moderno e em alta
O serializer JSON padrão do .NET tem uma boa lista de atributos que vivem no namespace System.Text.Json.Serialization.
Os atributos mais úteis:
| Atributo | Para que serve | Exemplo de uso |
|---|---|---|
|
Converte o nome da propriedade no JSON | |
|
Exclui totalmente a propriedade da serialização | |
|
Serializa um field público (e não só propriedade) | |
|
Ignorar a propriedade sob certas condições | |
Exemplos de uso:
using System.Text.Json.Serialization;
public class Person
{
[JsonPropertyName("first_name")]
public string FirstName { get; set; } // O nome no JSON será 'first_name'
[JsonIgnore]
public string Password { get; set; } // Não vai para o JSON
[JsonPropertyName("born_year")]
public int? YearOfBirth { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Nickname { get; set; } // Se for null — não entra no JSON
}
Vamos serializar essa pessoa:
var person = new Person
{
FirstName = "Ivan",
Password = "123456",
YearOfBirth = 2000,
Nickname = null
};
string json = JsonSerializer.Serialize(person);
// json: {"first_name":"Ivan","born_year":2000}
Repare: tanto a senha quanto o apelido sumiram do JSON! (Senha porque é sempre ignorada, apelido porque é null).
Por que isso é importante? Na prática, você quase sempre não quer que o cliente (ou um atacante!) veja dados críticos como senhas, tokens, contadores ou timestamps internos. Com [JsonIgnore] e afins isso se resolve numa linha.
3. Atributos para Newtonsoft.Json: o rei da flexibilidade
Se você usa Newtonsoft.Json (documentação aqui), as possibilidades são ainda maiores (e às vezes mais fáceis se você vem de projetos antigos).
Principais atributos:
| Atributo | Finalidade |
|---|---|
|
Define o nome no JSON, além de ordem, obrigatoriedade, etc. |
|
Exclui field/propriedade da serialização |
|
Exige presença obrigatória na desserialização |
|
Pode indicar um converter customizado para casos complexos |
|
Controla serialização de valores padrão |
Exemplo prático:
using Newtonsoft.Json;
public class UserProfile
{
[JsonProperty("login")]
public string Username { get; set; }
[JsonIgnore]
public string InternalNotes { get; set; }
[JsonProperty(Required = Required.Always)]
public string Email { get; set; }
}
Recursos extras do JsonProperty — obrigatoriedade (Required), ordem (Order) etc. Por exemplo:
[JsonProperty("id", Order = 1, Required = Required.Always)]
public int Id { get; set; }
4. Atributos para XML: estilo "classic"
XmlSerializer usa um arsenal de atributos do namespace System.Xml.Serialization.
Mais comuns:
| Atributo | Para que serve |
|---|---|
|
Elemento no XML com outro nome |
|
Converte a propriedade em um atributo do elemento XML |
|
Exclui propriedade/field da serialização XML |
|
Para coleções — define o nome do array XML |
|
Para coleções — define o nome do item do array |
|
Muda o nome do tag raiz XML |
Exemplo prático:
using System.Xml.Serialization;
[XmlRoot("human")]
public class Person
{
[XmlElement("firstname")]
public string Name { get; set; }
[XmlAttribute("years")]
public int Age { get; set; }
[XmlIgnore]
public string Secret { get; set; }
}
var person = new Person { Name = "Anna", Age = 32, Secret = "42" };
Depois da serialização, o XML ficaria mais ou menos assim:
<human years="32"><firstname>Anna</firstname></human>
Note que o field Secret não entrou no XML, e Age foi serializado como atributo, não como tag filho.
5. Dicas úteis
Por baixo do capô: como os atributos funcionam
Quando o serializer encontra sua classe, ele literalmente "lê" via reflection (mágica do .NET, veja System.Reflection). O serializer consulta metadados: por exemplo, existe o atributo JsonIgnore ou XmlElement na propriedade? Dependendo disso, ele inclui ou pula dados no documento final.
É uma maneira prática de separar "esquema de dados" da lógica de negócio. Sua classe é lógica de negócio, e os atributos são, na prática, o passaporte para a serialização.
Tabela de correspondência dos atributos principais
| Função | System.Text.Json | Newtonsoft.Json | XmlSerializer |
|---|---|---|---|
| Mudar nome | |
|
|
| Ignorar | |
|
|
| Formato custom | |
|
— (via IXmlSerializable, é dolorido) |
| Raiz do objeto | — | — | |
| Coleção | — | — | |
6. Cenários avançados
Às vezes você precisa serializar um objeto de um jeito que o serializer padrão não faz. Por exemplo, guardar uma data como UNIX timestamp em vez do ISO padrão. Para isso existem atributos que permitem ligar converters personalizados.
No System.Text.Json:
public class UnixDateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()).UtcDateTime;
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
=> writer.WriteNumberValue(new DateTimeOffset(value).ToUnixTimeSeconds());
}
public class LogEntry
{
[JsonConverter(typeof(UnixDateTimeConverter))]
public DateTime EventTime { get; set; }
}
Agora, ao serializar, o campo EventTime vira um número em vez de uma string de data.
No Newtonsoft.Json:
public class BoolToYesNoConverter : JsonConverter<bool>
{
public override void WriteJson(JsonWriter writer, bool value, JsonSerializer serializer)
=> writer.WriteValue(value ? "yes" : "no");
public override bool ReadJson(JsonReader reader, Type objectType, bool existingValue, bool hasExistingValue, JsonSerializer serializer)
=> (string)reader.Value == "yes";
}
public class Answer
{
[JsonConverter(typeof(BoolToYesNoConverter))]
public bool IsCorrect { get; set; }
}
Agora um booleano é serializado como "yes" ou "no", e ao desserializar volta para true ou false.
7. Peculiaridades e armadilhas
Ao adicionar atributos, lembre-se: diferentes serializers usam atributos diferentes. Se você trabalha com JSON e XML (ou até com Newtonsoft.Json e System.Text.Json ao mesmo tempo), não esqueça de colocar ambos os atributos necessários, senão vem surpresa.
O nome padrão da propriedade será usado pelo serializer, a menos que você o override com um atributo.
Cuidado com herança: classes filhas herdam fields/propriedades públicas e, se o atributo estava no pai, ele também se aplica na criança. Isso costuma causar surpresa, mas é por design.
Erro típico: É comum desenvolvedores marcarem acidentalmente um campo que deveria entrar na serialização com JsonIgnore (ou esquecer de ignorar dados sensíveis). Ou então colocar XmlElement em um field privado — e depois estranhar por que o serializer não vê aquilo (XmlSerializer só processa membros public!).
GO TO FULL VERSION