100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C#

LINQ to XML

Learn to read, query, create, and modify XML documents in C# using the XDocument and XElement API combined with LINQ query syntax.

Performance and DebuggingIntermediate9 min readJul 10, 2026
Analogies

What LINQ to XML Replaces

Before LINQ to XML, working with XML in .NET meant using the verbose DOM-style XmlDocument API, navigating nodes with XPath strings, and manually casting XmlNode objects, all with weak compile-time checking. LINQ to XML introduces XDocument and XElement, an in-memory tree representation designed to be constructed and queried with ordinary LINQ operators and object-initializer syntax, so the code that builds or reads XML looks like idiomatic C# rather than string-heavy XPath navigation.

🏏

Cricket analogy: Reading an old-fashioned paper scorebook with hand-written tally marks compared to a modern digital scoring app that lets you tap a player's name to instantly filter their stats mirrors the shift from XmlDocument's XPath strings to XElement's LINQ queries.

XDocument and XElement Basics

XDocument.Load(path) or XDocument.Parse(xmlString) reads an entire XML document into an in-memory tree of XElement and XAttribute objects, where each XElement exposes Elements() and Attributes() to navigate children and Element("Name") to get the first matching child directly. Building new XML is equally direct with functional construction: nesting new XElement("name", content) calls inside each other mirrors the shape of the resulting document, which reads far more naturally than the older API's sequence of CreateElement and AppendChild calls.

🏏

Cricket analogy: A team's full playing squad list with each player's role nested underneath the team name mirrors how XElement nests child elements under a parent, letting you navigate from team to players directly.

csharp
// Building XML with functional construction
var doc = new XDocument(
    new XElement("catalog",
        new XElement("book",
            new XAttribute("id", "bk101"),
            new XElement("title", "LINQ in Action"),
            new XElement("author", "Jane Doe"),
            new XElement("price", 39.99)
        ),
        new XElement("book",
            new XAttribute("id", "bk102"),
            new XElement("title", "C# Deep Dive"),
            new XElement("author", "John Smith"),
            new XElement("price", 44.99)
        )
    )
);

// Reading it back and navigating
var firstTitle = doc.Root.Element("book").Element("title").Value;
doc.Save("catalog.xml");

Querying XML with LINQ

Because XElement.Elements() returns IEnumerable<XElement>, the full LINQ operator set applies directly: doc.Descendants("book").Where(b => (decimal)b.Element("price") > 40).Select(b => (string)b.Element("title")) filters and projects XML content using the same query syntax you'd use on any other collection. XElement and XAttribute also support explicit conversion operators to primitive types like decimal, int, and DateTime, which is why casting (decimal)b.Element("price") reads the element's text content and parses it directly, avoiding manual string parsing.

🏏

Cricket analogy: Filtering a full ball-by-ball database down to just deliveries bowled by spinners that went for a boundary uses the same query pattern whether the data comes from a database or an XML scorecard feed, just like Descendants().Where() on XML.

Modifying and Creating XML

Existing XElement objects can be modified in place with Add() to append a new child, SetElementValue() or SetAttributeValue() to update or create a value in one call, and Remove() to delete an element or attribute from its parent, all of which mutate the live in-memory tree directly. Because these methods mutate the tree, and because navigation methods like Descendants() are themselves lazily-evaluated LINQ queries, modifying the tree while iterating over a live Descendants() query (for example calling Remove() inside a foreach over doc.Descendants("book")) throws an InvalidOperationException from a collection-modified-during-enumeration error, so such operations should materialize the query with .ToList() first.

🏏

Cricket analogy: Editing a live scorecard app by adding a new batter's entry while someone else is simultaneously scrolling through that same live-updating list causes the display to jump unpredictably, similar to modifying an XElement tree while a live Descendants() query enumerates it.

Calling .Remove() or .Add() on elements while foreach-ing over a live Descendants() or Elements() query throws an InvalidOperationException, because that query is itself a lazily-evaluated LINQ enumerable over the live tree. Always call .ToList() on the query first to snapshot the elements you intend to modify, then mutate the tree freely.

csharp
// Safe mutation: materialize before modifying
var cheapBooks = doc.Descendants("book")
    .Where(b => (decimal)b.Element("price") < 40)
    .ToList(); // snapshot first

foreach (var book in cheapBooks)
{
    book.SetElementValue("onSale", true);
    book.SetAttributeValue("discount", "10%");
}

// Removing elements safely
doc.Descendants("book")
    .Where(b => (string)b.Element("author") == "Unknown")
    .ToList()
    .ForEach(b => b.Remove());
  • XDocument and XElement replace the older XmlDocument DOM API with an in-memory tree built and queried using ordinary LINQ.
  • XDocument.Load() and XDocument.Parse() read XML into memory; functional construction with nested new XElement() calls builds it.
  • XElement.Elements() and Descendants() return IEnumerable<XElement>, so the full LINQ operator set (Where, Select, OrderBy) applies directly.
  • Explicit conversion operators let you cast an XElement or XAttribute directly to decimal, int, DateTime, and other primitive types.
  • SetElementValue(), SetAttributeValue(), Add(), and Remove() mutate the live tree in place.
  • Modifying the tree while enumerating a live Descendants() or Elements() query throws an InvalidOperationException.
  • Always materialize the target elements with .ToList() before calling Remove() or Add() inside a loop over a query.

Practice what you learned

Was this page helpful?

Topics covered

#LINQStudyNotes#MicrosoftTechnologies#LINQToXML#LINQ#XML#Replaces#XDocument#StudyNotes#SkillVeris#ExamPrep