Convert XDocument to XmlDocument (and Convert XmlDocument to XDocument)

Some time ago, I blogged about an approach for converting an XElement object to an XmlNode object, and vice versa.  This is useful when you want to use a programming interface that takes and returns objects of type XmlNode, but you want to use the expressiveness and power of LINQ to XML for your code that modifies the XML tree.  There are a few occasions where this isn’t good enough – you need to convert from an XDocument to XmlDocument and back.  If you need to control encoding, round-trip the XML declaration, or round-trip processing instructions, then you need to use documents, not elements.  This post builds on the Convert XElement to XmlNode (and Convert XmlNode to XElement) blog post.  It presents two more extension methods:

  • This blog is inactive.
    New blog: EricWhite.com/blog

    Blog TOCGetXDocument, which you can call on an XmlDocument object.

  • GetXmlDocument, which you can call on an XDocument object.

Here is a quick summary of some characteristics of XML documents.

  • Documents can have an XML declaration at the beginning of the document.  The XML declaration defines three values – the XML version (normally 1.0), the encoding (often utf-8 or utf-16), and the standalone value.  A standalone document declaration indicates that there are no external markup declarations that affect the information passed from the XML processor to the application.
  • Documents can contain one root element.
  • Documents can contain comments as siblings to the root element.
  • Documents can contain processing instructions as siblings to the root element.

When converting between XmlDocument and XDocument, we can take the same approach as when converting between XmlNode and XElement – we can get an XmlWriter from the source object, and create a new destination object using that XmlWriter.  This will appropriately convert the children nodes of the document.  The only special case that we have to handle is for the XML declaration.  In System.Xml, an XmlDeclaration object is a child node of an XmlDocument.  In contrast, in LINQ to XML, an XDeclaration is a property of an XDocument.  When converting from XmlDocument to XDocument, and from XDocument to XmlDocument, the extension methods determine if there is an XML declaration, and then creates the same declaration in the destination object. 

Note that per the XML specification, documents can also contain children text nodes so long as they contain only white space.  These text nodes never have any impact on the semantic meaning of the document.  The default behavior of LINQ to XML is to not preserve insignificant white space when de-serializing, and then to format or indent the XML when serializing.  The extension methods I present in this post have this same behavior with regards to white space in sibling text nodes to the root element of the document.

The following code shows how to convert from an XDocument object to an XmlDocument object:

XDocument document = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("Comment at root level"),
new XProcessingInstruction("mso-application", "progid="Word.Document""),
new XElement("Document",
new XAttribute("Att", "1")));
XmlDocument xmlDocument = document.GetXmlDocument();

Converting the other way is just as easy:

XDocument newDocument = xmlDocument.GetXDocument();

Following is the complete listing of an example that includes the four extension methods – two to convert to and from XmlNode, and two to convert to and from XmlDocument.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

public static class MyExtensions
{
public static XElement GetXElement(this XmlNode node)
{
XDocument xDoc = new XDocument();
using (XmlWriter xmlWriter = xDoc.CreateWriter())
node.WriteTo(xmlWriter);
return xDoc.Root;
}

public static XmlNode GetXmlNode(this XElement element)
{
using (XmlReader xmlReader = element.CreateReader())
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlReader);
return xmlDoc;
}
}

public static XDocument GetXDocument(this XmlDocument document)
{
XDocument xDoc = new XDocument();
using (XmlWriter xmlWriter = xDoc.CreateWriter())
document.WriteTo(xmlWriter);
XmlDeclaration decl =
document.ChildNodes.OfType<XmlDeclaration>().FirstOrDefault();
if (decl != null)
xDoc.Declaration = new XDeclaration(decl.Version, decl.Encoding,
decl.Standalone);
return xDoc;
}

public static XmlDocument GetXmlDocument(this XDocument document)
{
using (XmlReader xmlReader = document.CreateReader())
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlReader);
if (document.Declaration != null)
{
XmlDeclaration dec = xmlDoc.CreateXmlDeclaration(document.Declaration.Version,
document.Declaration.Encoding, document.Declaration.Standalone);
xmlDoc.InsertBefore(dec, xmlDoc.FirstChild);
}
return xmlDoc;
}
}
}

class Program
{
static void Main(string[] args)
{
XElement element = new XElement("Element",
new XElement("Child",
new XAttribute("Att", "1")
)
);

XmlNode xmlNode = element.GetXmlNode();
Console.WriteLine("XmlNode (converted from XElement)");
Console.WriteLine("=================================");
Console.WriteLine(xmlNode.OuterXml);
Console.WriteLine();

XElement newElement = xmlNode.GetXElement();
Console.WriteLine("XElement (converted from XmlNode)");
Console.WriteLine("=================================");
Console.WriteLine(newElement);
Console.WriteLine();

XDocument document = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("Comment at root level"),
new XProcessingInstruction("mso-application", "progid="Word.Document""),
new XElement("Document",
new XAttribute("Att", "1")));
XmlDocument xmlDocument = document.GetXmlDocument();
Console.WriteLine("XmlDocument (converted from XDocument)");
Console.WriteLine("======================================");
Console.WriteLine(xmlDocument.OuterXml);
Console.WriteLine();

XDocument newDocument = xmlDocument.GetXDocument();
Console.WriteLine("XDocument (converted from XmlDocument)");
Console.WriteLine("======================================");
newDocument.Save("doc.xml", SaveOptions.None);
Console.WriteLine(File.ReadAllText("doc.xml"));
Console.WriteLine();
}
}