User Tools

Site Tools


notes:csharp:linq_to_xml

LINQ to XML

LINQ to XML uses a document object model (DOM) that consists of classes such as XDeclaration, XDocument, XElement, XAttribute, etc. All these classes derive from the XNode class that provide the common API.

Example: Parse a string containing XML:

using System.Xml.Linq;
...
// Parse an XML document.
string xml = @"<?xml version='1.0' encoding='utf-8' standalone='yes'?>
               <books>
                   <book id='1'>
                       <title>Good Book</title>
                   </book>
                   <book id='2'>
                       <title>Another Good Book</title>
                   </book>
               </books>";
XDocument doc = XDocument.Parse(xml);
 
// Parse an XML element.
string xml = @"<book id='1'>
                   <title>Good Book</title>
               </book>";
XElement book = XElement.Parse(xml);

Example: Load an XML file:

// from the web
XDocument doc = XDocument.Load("http://mysite.com/test.xml");
 
// from a file
XElement books = XElement.Load(@"c:\temp\books.xml");

Create XML

Example: Construct an XML document. All the code snippets create the same XML structure:

<?xml version="1.0" encoding="utf-8"?>
<books>
  <book id="1">
    <title>Good Book</title>
  </book>
  <book id="2">
    <title>Another Good Book</title>
  </book>
</books>
using System.Xml.Linq;
...
 
// Snippet #1: Use the XElement.Add method:
 
XElement xml = new XElement("books");
 
XElement book1 = new XElement("book");
book1.Add(new XAttribute("id", 1));
book1.Add(new XElement("title", "Good Book"));
xml.Add(book1);
 
XElement book2 = new XElement("book");
book2.Add(new XAttribute("id", 2));
book2.Add(new XElement("title", "Another Good Book"));
xml.Add(book2);
 
// Snippet #2: Use functional construction:
 
XElement xml = new XElement(
    new XElement("books",
        new XElement("book",
            new XAttribute("id", "1"),
            new XElement("title", "Good Book")
        ),
        new XElement("book",
            new XAttribute("id", "2"),
            new XElement("title", "Another Good Book")
        )
    )
);
 
// Snippet #3: Pass an array of elements to the XElement constructor:
 
var list = new List<XElement>();
list.Add(new XElement("book",
            new XAttribute("id", "1"),
            new XElement("title", "Good Book")));
list.Add(new XElement("book",
            new XAttribute("id", "2"),
            new XElement("title", "Another Good Book")));
 
XElement xml = new XElement(
    new XElement("books", list)
);
 
// Show XML
Console.WriteLine(xml);
 
// Save XML in a file.
xml.Save("books.xml");

Example: An example of automatic deep cloning:

using System.Xml.Linq;
...
 
XElement author =
    new XElement("author",
        new XElement("firstName", "John"),
        new XElement("lastName", "Makak"));
 
// The 'author' element is cloned in both 'book' elements.
 
XElement book1 =
    new XElement("book",
        new XAttribute("id", "1"),
        new XElement("title", "Good Book"),
        author);
Console.WriteLine(book1);
 
XElement book2 =
    new XElement("book",
        new XAttribute("id", "2"),
        new XElement("title", "Another Good Book"),
        author);
book2.Element("author").Element("lastName").Value = "Gibbon"; // modify the copy of the author element
Console.WriteLine(book2);

Output:

<book id="1">
  <title>Good Book</title>
  <author>
    <firstName>John</firstName>
    <lastName>Makak</lastName>
  </author>
</book>
<book id="2">
  <title>Another Good Book</title>
  <author>
    <firstName>John</firstName>
    <lastName>Gibbon</lastName>
  </author>
</book>

Project XML

Example: Project a collection of books into XML. Use XStreamingElement rather than XElement to improve performance:

class Program
{
    static void Main(string[] args)
    {
        var books = new List<Book>();
        books.Add(new Book { Id = 1, Title = "Good Book" });
        books.Add(new Book { Id = 2, Title = "Nice Book" });
 
        var xml =
            new XStreamingElement("books",
                from b in books
                select new XStreamingElement("book", 
                    new XAttribute("id", b.Id),
                    new XElement("title", b.Title)
                )
            );
 
        // <books>
        //   <book id="1">
        //     <title>Good Book</title>
        //   </book>
        //   <book id="2">
        //     <title>Nice Book</title>
        //   </book>
        // </books>
        Console.WriteLine(xml);
    }
}
 
public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
}

Query XML

Example: Find the first element and the last one. Then, display all the elements of the XML document:

using System.Xml.Linq;
...
string xml = 
    @"<books>
        <book id='1'>
          <title>Good Book</title>
        </book>
        <book id='2'>
          <title>Another Good Book</title>
        </book>
      </books>";
XElement books = XElement.Parse(xml);
 
// <book id="1"><title>Good Book</title></book>
Console.WriteLine(books.FirstNode);
 
// <book id="2"><title>Another Good Book</title></book>
Console.WriteLine(books.LastNode);
 
// <book id="1"><title>Good Book</title></book>
// <book id="2"><title>Another Good Book</title></book>
foreach (XNode node in books.Nodes())
    Console.WriteLine(node);
 
// Good Book
// Another Good Book
foreach (XElement element in books.Elements())
    Console.WriteLine(element.Value);

Example: Find all the books which title (actually any element) contains the word “Another”. Return a collection of Id and Title pairs:

using System.Xml.Linq;
...
string xml =
    @"<books>
        <book id='1'><title>Good Book</title></book>
        <book id='2'><title>Another Good Book</title></book>
        <book id='3'><title>Nice Book</title></book>
        <book id='4'><title>Another Nice Book</title></book>
      </books>";
XElement books = XElement.Parse(xml);
 
var query =
    from book in books.Elements() // books.Elements() returns IEnumerable<>
    where book.Elements().Any(b => b.Value.Contains("Another")) 
 
    // if you wanted to find the word "Another" only in the 'title' elements:
    // where book.Element("title").Value.Contains("Another")
 
    // when the 'title' element may not exist (for example, when it is optional):
    // where book.Element("title")?.Value.Contains("Another")
 
    select new { Id = book.Attribute("id").Value, Title = book.Element("title").Value };
 
// Id: 2, Title: Another Good Book
// Id: 4, Title: Another Nice Book
foreach (var book in query)
    Console.WriteLine("Id: {0}, Title: {1}", book.Id, book.Title);

Example: Find all the 'box' elements:

string xml =
    @"<things>
        <box>Box1</box>
        <pencil>Pencil1</pencil>
        <box>Box2</box>
        <eraser>Eraser1</eraser>
        <box>Box3</box>
      </things>";
XElement things = XElement.Parse(xml);
 
var query = things.Elements("box");
 
// <box>Box1</box>
// <box>Box2</box>
// <box>Box3</box>
foreach (var box in query)
    Console.WriteLine(box);

Example: Find all authors using chained sequences:

using System.Xml.Linq;
...
string xml =
    @"<books>
        <book>
          <title>Book1</title>
          <author>AuthorA</author>
        </book>
        <book>
          <title>Book2</title>
          <author>AuthorB</author>
        </book>
        <book>
          <title>Book3</title>
          <author>AuthorC</author>
        </book>
      </books>";
XElement books = XElement.Parse(xml);
 
var query = from author in books.Elements("book").Elements("author")
            select author;
// AuthorA
// AuthorB
// AuthorC
foreach (var author in query)
    Console.WriteLine(author.Value);

Example: List all the books using XElement.Descendants. Also, filter the books to show only those that have any reviews:

using System.Xml.Linq;
...
string xml =
    @"<books>
        <book>
          <title>Book1</title>
          <author>AuthorA</author>
          <review>...</review>
          <review>...</review>
        </book>
        <book>
          <title>Book2</title>
          <author>AuthorB</author>
        </book>
        <book>
          <title>Book3</title>
          <author>AuthorC</author>
          <review>...</review>
        </book>
      </books>";
XElement books = XElement.Parse(xml);
 
// Show all the books.
 
IEnumerable<string> titles =
    from b in books.Descendants("book")
    select (string)b.Element("title") + ", " +
           (string)b.Element("author");
 
// Book1, AuthorA
// Book2, AuthorB
// Book3, AuthorC
foreach (string title in titles)
    Console.WriteLine(title);
 
 
// Show the books that have any reviews.
 
IEnumerable<string> filtered =
    from b in books.Descendants("book")
    where b.Descendants("review").Any()
    let title = (string)b.Element("title") + ", " +
                (string)b.Element("author")
    orderby title
    select title;
 
// Book1, AuthorA
// Book3, AuthorC
foreach (string title in filtered)
    Console.WriteLine(title);

Update XML

Methods to update XML:

  • XElement methods: RemoveAttributes, RemoveAll, ReplaceAttributes, ReplaceAll, SetElementValue, SetAttributeValue.
  • XContainer methods: Add, AddFirst, RemoveNodes, ReplaceNodes.
  • XNode methods: AddBeforeSelf, AddAfterSelf, Remove, ReplaceWith

Example: Update an XML element using the SetValue method and the Value property:

string xml =
    @"<books>
        <book id='1'><title>Good Book</title></book>
        <book id='2'><title>Another Good Book</title></book>
      </books>";
XElement books = XElement.Parse(xml);
 
// books.Element("book") selects the first book element.
 
// Method #1
books.Element("book").SetValue("Discountinued");
 
// Method #2
books.Element("book").Value = "Discountinued";
 
// <books>
//   <book id="1">Discountinued</book>
//   <book id="2"><title>Another Good Book</title></book>
// </books>
Console.WriteLine(books);

Example: Add or update elements and attributes using SetElementValue and SetAttributeValue:

XElement things = new XElement("things");
things.SetAttributeValue("count", 1);      // add an attribute on <things>
things.SetElementValue("pencil", "aaa");   // add a child node
things.SetElementValue("mug", "bbb");      // add a child node
things.SetElementValue("notebook", "ccc"); // add a child node
things.SetElementValue("mug", "zzz");      // update <mug>
things.SetAttributeValue("count", 3);      // update the attribute on <things>
 
// <things count="3">
//   <pencil>aaa</pencil>
//   <mug>zzz</mug>
//   <notebook>ccc</notebook>
// </things>
Console.WriteLine(things);

Transform XML

Example: Add an 'isKindle' attribute to all book elements:

string xml =
    @"<books>
        <book id='1'><title>Good Book</title></book>
        <book id='2'><title>Another Good Book</title></book>
      </books>";
XElement books = XElement.Parse(xml);
 
XElement updated = 
    new XElement("books",
        from b in books.Descendants("book")
        let title = b.Element("title")
        select new XElement("books",
            new XAttribute("isKindle", false),
            b.Attributes(), // include existing attributes i.e., the 'id' attribute
            title // 'title' represents the entire element i.e., <title>Good Book</title>
        )
    );
 
Console.WriteLine(updated);

Output:

<books>
  <books isKindle="false" id="1">
    <title>Good Book</title>
  </books>
  <books isKindle="false" id="2">
    <title>Another Good Book</title>
  </books>
</books>

XML Namespaces

// Create a namespace.
XNamespace ns = "http://something.com/stuff/2016";
 
// Create elements within a default namespace ns. Note that the + operator is overloaded.
var xml = new XElement(ns + "books", 
    from b in books // books is a collection of books
    select new XElement(ns + "Book",
        new XAttribute("title", b.Title))
    );
 
// Create elements within a different namespace (without a prefix).
XNamespace ext = "http://something.com/stuff/2016/extended";
var xml = new XElement(ns + "books", 
    from b in books 
    select new XElement(ext + "Book",
        new XAttribute("title", b.Title))
    );
 
// Create elements within a different namespace (with a prefix).
XNamespace ext = "http://something.com/stuff/2016/extended";
var xml = new XElement(ns + "books", 
    from b in books 
    select new XElement(ext + "Book",
        new XAttribute("title", b.Title))
    );
xml.Add(new XAttribute(XNamespace.Xmlns + "ext", ext));
notes/csharp/linq_to_xml.txt · Last modified: 2017/06/09 by leszek