Blogs

Paul Kimmel's Blog

Functional Construction with LINQ to XML

     

XML is everywhere. XHTML is XML rules applied applied to HTML. XAML (eXtensible Application Markup Language) is an implementation of XML. You can use XML for controls like our ASPxMenu. Sometimes the only challenge is to transmogrify data you have, maybe database data, into data you want instead, XML. LINQ supports a concept called function construction of XML documents. The System.Xml.Linq namespaces contains classes that support constructing an XML document. These classes like XDocument, XElement, and XAttribute each construct parts of an XML document from the arguments you provide. (Conceptually LINQ to XML and functional construction work a lot like the CodeDom namespace, which supports code generation in a similar manner.)

To use LINQ to construct an XML document you need some enumerable data. A DataTable, DataSet, Collection, or objects created from LINQ to SQL will all work as the source data. The next step is to write a LINQ query that enumerates over the data and invokes the constructors for the various XDocument, XElement, XAttribute, XDeclaration and other classes that represent elements of an XML document. Listing 1 contains some code that reads the Customers table from the database using vanilla ADO.NET and a LINQ to XML query that constructs the XML document. the output from the LINQ query is provided in Listing 2.

Listing 1: ADO.NET is used to read the Northwind Customers table and construct an XML document.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Xml.Linq;

namespace FunctionalConstructionDemo
{
  class Program
  {
    [STAThread]
    static void Main(string[] args)
    {
      string connectionString =
        @"Data Source=WYOMING\SQLEXPRESS;" +
        "Initial Catalog=Northwind;Integrated Security=True";

      string sql = "SELECT TOP 10 * FROM Customers";
      DataTable table = new DataTable();

      using (SqlConnection connection = new SqlConnection(connectionString))
      {
        connection.Open();
        SqlCommand command = new SqlCommand(sql, connection);
        SqlDataAdapter adapter = new SqlDataAdapter(command);
        adapter.Fill(table);
      }
      XDocument doc = new XDocument(
        new XDeclaration("1.0", "utf-8", "true"),
        new XElement("Customers",
        from DataRow cust in table.Rows
        orderby cust["CompanyName"]
        select new XElement("Customer",
              new XAttribute("CustomerID", cust["CustomerID"]),
              new XElement("CompanyName", cust["CompanyName"]),
              new XElement("ContactName", cust["ContactName"]),
              new XElement("ContactTitle", cust["ContactTitle"]),
              new XElement("Address", cust["Address"]),
              new XElement("City", cust["City"]),
              new XElement("Region", cust["Region"]),
              new XElement("PostalCode", cust["PostalCode"]),
              new XElement("Country", cust["Country"]),
              new XElement("Phone", cust["Phone"]),
              new XElement("Fax", cust["Fax"])
              )));

      System.Windows.Forms.Clipboard.SetData("Text", doc);
      Console.WriteLine(doc);
      Console.ReadLine();
    }
  }
}

Listing 2: The constructed XML document from the LINQ query in Listing 1.

<Customers>
  <Customer CustomerID="ALFKI">
    <CompanyName>Alfreds Futterkiste</CompanyName>
    <ContactName>Maria Anders</ContactName>
    <ContactTitle>Sales Representative</ContactTitle>
    <Address>Obere Str. 57</Address>
    <City>Berlin</City>
    <Region></Region>
    <PostalCode>12209</PostalCode>
    <Country>Germany</Country>
    <Phone>030-0074321</Phone>
    <Fax>030-0076545</Fax>
  </Customer>
  <Customer CustomerID="ANATR">
    <CompanyName>Ana Trujillo Emparedados y helados</CompanyName>
    <ContactName>Ana Trujillo</ContactName>
    <ContactTitle>Owner</ContactTitle>
    <Address>Avda. de la Constitución 2222</Address>
    <City>México D.F.</City>
    <Region></Region>
    <PostalCode>05021</PostalCode>
    <Country>Mexico</Country>
    <Phone>(5) 555-4729</Phone>
    <Fax>(5) 555-3745</Fax>
  </Customer>
  <Customer CustomerID="ANTON">
    <CompanyName>Antonio Moreno Taquería</CompanyName>
    <ContactName>Antonio Moreno</ContactName>
    <ContactTitle>Owner</ContactTitle>
    <Address>Mataderos  2312</Address>
    <City>México D.F.</City>
    <Region></Region>
    <PostalCode>05023</PostalCode>
    <Country>Mexico</Country>
    <Phone>(5) 555-3932</Phone>
    <Fax></Fax>
  </Customer>
  <Customer CustomerID="AROUT">
    <CompanyName>Around the Horn</CompanyName>
    <ContactName>Thomas Hardy</ContactName>
    <ContactTitle>Sales Representative</ContactTitle>
    <Address>120 Hanover Sq.</Address>
    <City>London</City>
    <Region></Region>
    <PostalCode>WA1 1DP</PostalCode>
    <Country>UK</Country>
    <Phone>(171) 555-7788</Phone>
    <Fax>(171) 555-6750</Fax>
  </Customer>
  <Customer CustomerID="BERGS">
    <CompanyName>Berglunds snabbköp</CompanyName>
    <ContactName>Christina Berglund</ContactName>
    <ContactTitle>Order Administrator</ContactTitle>
    <Address>Berguvsvägen  8</Address>
    <City>Luleå</City>
    <Region></Region>
    <PostalCode>S-958 22</PostalCode>
    <Country>Sweden</Country>
    <Phone>0921-12 34 65</Phone>
    <Fax>0921-12 34 67</Fax>
  </Customer>
  <Customer CustomerID="BLAUS">
    <CompanyName>Blauer See Delikatessen</CompanyName>
    <ContactName>Hanna Moos</ContactName>
    <ContactTitle>Sales Representative</ContactTitle>
    <Address>Forsterstr. 57</Address>
    <City>Mannheim</City>
    <Region></Region>
    <PostalCode>68306</PostalCode>
    <Country>Germany</Country>
    <Phone>0621-08460</Phone>
    <Fax>0621-08924</Fax>
  </Customer>
  <Customer CustomerID="BLONP">
    <CompanyName>Blondesddsl père et fils</CompanyName>
    <ContactName>Frédérique Citeaux</ContactName>
    <ContactTitle>Marketing Manager</ContactTitle>
    <Address>24, place Kléber</Address>
    <City>Strasbourg</City>
    <Region></Region>
    <PostalCode>67000</PostalCode>
    <Country>France</Country>
    <Phone>88.60.15.31</Phone>
    <Fax>88.60.15.32</Fax>
  </Customer>
  <Customer CustomerID="BOLID">
    <CompanyName>Bólido Comidas preparadas</CompanyName>
    <ContactName>Martín Sommer</ContactName>
    <ContactTitle>Owner</ContactTitle>
    <Address>C/ Araquil, 67</Address>
    <City>Madrid</City>
    <Region></Region>
    <PostalCode>28023</PostalCode>
    <Country>Spain</Country>
    <Phone>(91) 555 22 82</Phone>
    <Fax>(91) 555 91 99</Fax>
  </Customer>
  <Customer CustomerID="BONAP">
    <CompanyName>Bon app'</CompanyName>
    <ContactName>Laurence Lebihan</ContactName>
    <ContactTitle>Owner</ContactTitle>
    <Address>12, rue des Bouchers</Address>
    <City>Marseille</City>
    <Region></Region>
    <PostalCode>13008</PostalCode>
    <Country>France</Country>
    <Phone>91.24.45.40</Phone>
    <Fax>91.24.45.41</Fax>
  </Customer>
  <Customer CustomerID="BOTTM">
    <CompanyName>Bottom-Dollar Markets</CompanyName>
    <ContactName>Elizabeth Lincoln</ContactName>
    <ContactTitle>Accounting Manager</ContactTitle>
    <Address>23 Tsawassen Blvd.</Address>
    <City>Tsawassen</City>
    <Region>BC</Region>
    <PostalCode>T2F 8M4</PostalCode>
    <Country>Canada</Country>
    <Phone>(604) 555-4729</Phone>
    <Fax>(604) 555-3745</Fax>
  </Customer>
</Customers>

I added the code to copy the output to the clipboard to make it easy to get the XML output into this blog post, but you could just as easily use a FileStream to write the XML output to a file. TOP 10 was used to keep the size of the XML output manageable.

You can decompose the LINQ to XML query that constructs the document in the following way:

  • The actual type of the output object is an XDocument. XDocument is defined to accept an indeterminate number params object[] arguments, which is consistent with an unknown number of XML nodes
  • The first argument, an XDeclaration, represents the <xml> element node
  • The second argument is an XElement and it represents the root node, in this case <Customers>, only nodes that are repeated need to be in the LINQ query
  • The LINQ query is produces the results for the first and subsequent XML child elements of the Customers root beginning with the from DataRow cust in table.Rows part of the statement
  • The orderby clause will sort the results by CompanyName
  • and, the select uses projection to construct each node for each custom, filling in the details of a customer record

In the sample XDocument has two arguments: XDeclaration and XElement. The first XElement defines the Customers root node. The Customers element will contain a single child for each Customer, a Customer element. Each Customer element will contain an XAttribute representing containing the CustomerID, and a child element for every column in a Customer row.

Checkout the System.Linq.Xml namespace for more classes for constructing XML documents and examples that show you how to add CDATA.

 

Published Sep 15 2009, 08:52 PM by Paul Kimmel (DevExpress)
Bookmark and Share

Comments

No Comments
More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.