CodeGym /Cursos /C# SELF /LINQ to XML: XDocument, XElement

LINQ to XML: XDocument, XElement

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

1. Introdução

O aparecimento do LINQ foi uma revolução no trabalho com XML no .NET. Antes, para modificar XML tinha que-se escrever construções pesadas com NodeList e XmlElement, agora podemos expressar todas as operações no estilo LINQ. É como trocar uma estrada de terra cheia de curvas por uma rodovia rápida.

LINQ to XML resolve três problemas principais:

  • Simplifica a sintaxe de construir e ler XML.
  • Torna a navegação pelo documento semelhante ao trabalho com coleções.
  • Permite integrar XML com qualquer consulta LINQ.

Classes principais: XDocument, XElement, XAttribute

Antes de mergulhar nas águas profundas do LINQ to XML, vamos conhecer três pilares:

  • XDocument — a raiz de todo o documento, tipo o lombinho do livro que contém tudo.
  • XElement — qualquer nó (elemento) XML. É como uma caixa no depósito que pode conter outras caixas ou produtos (elementos, atributos e texto).
  • XAttribute — o atributo de um elemento, por exemplo, <User id="42" />.

Esquema visual


+-----------------+
|   XDocument     |---------------------+
+-----------------+                     |
            |                      (Root XElement)
            v                           |
      +-------------+              +-----------------+
      |   XElement  |<-------------| XElement (root) |
      +-------------+              +-----------------+
            |                              |
        (XAttribute)                (nested XElements)

2. Lendo XML com LINQ to XML

Vamos supor que no nosso app educacional (por exemplo, uma agenda) queremos ler contatos a partir de um arquivo XML. Que a estrutura do arquivo seja assim:

<Contacts>
  <Person id="1">
    <Name>Ivan Ivanov</Name>
    <Email>ivan@example.com</Email>
    <Phones>
      <Phone type="mobile">+12 999 123-45-67</Phone>
      <Phone type="home">+12 812 123-45-67</Phone>
    </Phones>
  </Person>
  <Person id="2">
    <Name>Maria Petrova</Name>
    <Email>maria@example.com</Email>
    <Phones>
      <Phone type="mobile">+12 921 123-45-67</Phone>
    </Phones>
  </Person>
</Contacts>

Lendo o XML para a memória

Primeiro adicionamos a diretiva no projeto:

using System.Xml.Linq;

Agora lemos o arquivo ou a string:

// Suponha que xmlString contenha o documento XML, ou use XDocument.Load("contacts.xml")
var doc = XDocument.Parse(xmlString);

// Pegamos a lista de Person
var persons = doc.Root.Elements("Person");

foreach (var person in persons)
{
    string name = person.Element("Name")?.Value ?? "Sem nome";
    string email = person.Element("Email")?.Value ?? "Sem e-mail";
    string id = person.Attribute("id")?.Value ?? "sem id";
    
    Console.WriteLine($"Nome: {name}, Email: {email}, id: {id}");
}

- doc.Root dá o elemento raiz (Contacts).
- Elements("Person") retorna todos os <Person>.
- Depois pegamos elementos/atributos necessários via .Element("...") ou .Attribute("...").
- O operador ?. protege contra erros se o elemento/atributo não for encontrado.

Analogia

Trabalhar com XElement é como desmontar uma matrioska: você tira uma, dentro tem outra, e assim por diante. Só que agora isso é feito com uma consulta simples, sem percorridas pesadas.

3. Montando consultas LINQ sobre XML

Quer obter a lista de todos os telefones móveis de todas as pessoas? Fácil, em estilo LINQ:

var mobilePhones = doc
    .Descendants("Phone") // procura todos os Phone em qualquer nível
    .Where(p => (string)p.Attribute("type") == "mobile")
    .Select(p => p.Value);

foreach (string phone in mobilePhones)
{
    Console.WriteLine($"Móvel: {phone}");
}

- Descendants("Phone") busca todas as tags <Phone> no documento.
- Attribute("type") — obtém o atributo.
- Conversão (string) no atributo retorna automaticamente a string ou null.

Truques legais

Você pode buscar imediatamente através de vários níveis sem se preocupar com a complexidade da navegação. Isso seria bem mais chato com XmlDocument.

4. Criando e modificando XML "on the fly"

É confortável criar XML em memória usando os construtores XElement e XDocument. Exemplo de adicionar uma nova pessoa ao documento (parte do nosso "app-agenda" em evolução):

// Criamos um novo Person
var newPerson = new XElement("Person",
    new XAttribute("id", "3"),
    new XElement("Name", "Sergey Novikov"),
    new XElement("Email", "sergey@example.com"),
    new XElement("Phones",
        new XElement("Phone", new XAttribute("type", "work"), "+12 812 987-65-43")
    )
);

// Adicionamos à raiz
doc.Root.Add(newPerson);

// Salvamos de volta no arquivo (ou em string)
doc.Save("contacts.xml");

Comentários:

- Dá para aninhar elementos usando construtores "matrioska": o primeiro argumento é a tag, os demais são o conteúdo aninhado (podem ser elementos, atributos, texto).
- Atributos normalmente vêm antes do conteúdo direto (ex.: <Phone type="work">...).

5. Interação com coleções C# — a mágica do LINQ

Frequentemente precisamos serializar uma coleção comum de objetos (por exemplo, List<Person>) para XML ou o inverso. Isso é bem simples, quase uma linha de LINQ:

var people = new List<Person>
{
    new Person { Id = 1, Name = "Ivan Ivanov", Email = "ivan@example.com" },
    // etc.
};

var doc = new XDocument(
    new XElement("Contacts",
        people.Select(p =>
            new XElement("Person",
                new XAttribute("id", p.Id),
                new XElement("Name", p.Name),
                new XElement("Email", p.Email)
            )
        )
    )
);
A classe Person é assim (para contexto):
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

Agora você tem uma conversão simples, clara e controlada de uma coleção C# para XML!

6. Métodos úteis e dicas de XElement e XDocument

Navegação

  • .Elements("TagName") — todos os elementos imediatos (filhos diretos).
  • .Descendants("TagName") — todos os elementos aninhados em qualquer lugar dentro do elemento atual.
  • .Parent — elemento pai.
  • .Ancestors() — todos os ancestrais do elemento (útil para achar o caminho).
  • .FirstNode, .LastNode, .NextNode, .PreviousNode — navegação entre nós vizinhos.

Busca com condições

var personsWithEmail = doc.Root
    .Elements("Person")
    .Where(p => !string.IsNullOrEmpty((string)p.Element("Email")));

Adicionar e remover

// Adicionar telefone a um usuário existente
var maria = doc.Root.Elements("Person")
    .FirstOrDefault(p => (string)p.Element("Name") == "Maria Petrova");

maria?.Element("Phones")?.Add(
    new XElement("Phone", new XAttribute("type", "work"), "+12 812 111-22-33")
);

// Remover usuário por id
doc.Root.Elements("Person")
    .Where(p => (string)p.Attribute("id") == "3")
    .Remove();

Converter de volta para string/arquivo

string xmlText = doc.ToString(); // com formatação
doc.Save("contacts.xml");

7. Nuances úteis

Comparando LINQ to XML com outras abordagens (.NET)

Abordagem Quando usar Vantagens Desvantagens
XmlDocument
Código legado, antigo Integração com XPath Muito código, API verbosa
XmlSerializer
Para serializar objetos Simples serializar C# ↔ XML Baixa flexibilidade
LINQ to XML
Quase tudo: parsing, edição Linear, conciso, poderoso, LINQ Usa um pouco mais de memória

Aplicação em projetos reais e em entrevistas

  • Analisar arquivos de configuração onde não existe um mapeamento claro para classes C#.
  • Importação/exportação de dados (por exemplo, exportar/importar dados de usuário).
  • Migrações, tratamento de XML "sujo" ou complexo.
  • Transformações rápidas (por exemplo, converter um formato XML em outro).

"Por que eu devo saber isso?" — em entrevistas podem pedir para transformar ou parsear um XML complexo; lembrando LINQ to XML, você fará isso de forma mais simples e elegante do que usando APIs antigas.

8. Erros típicos ao trabalhar com LINQ to XML

Erro nº1: tentar acessar um elemento ausente sem checagem.
Se o elemento pode estar ausente, use o operador ?. ou verifique null antes. Caso contrário, você vai receber NullReferenceException e um crash inesperado em tempo de execução.

Erro nº2: confundir atributos com elementos.
Atributos e elementos são coisas diferentes em XML. Às vezes os dados podem estar em um ou em outro. Por exemplo:

<User login="admin"><Status>active</Status></User>

Para obter o valor do atributo login, use .Attribute("login"), e para obter o conteúdo da tag <Status> use .Element("Status").

Erro nº3: não entender o comportamento do método .Remove().
O método .Remove() modifica imediatamente a estrutura do documento XML em memória. Ele não retorna nada, e "desfazer" a remoção não é possível. Iniciantes às vezes esperam que ele retorne os elementos removidos ou que de alguma forma sinalize — isso não acontece.

2
Tarefa
C# SELF, nível 48, lição 3
Bloqueado
Extraindo dados de um documento XML
Extraindo dados de um documento XML
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION