[Blog Map] This blog is inactive. New blog: EricWhite.com/blog
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:
- GetXDocument, 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 = newXDocument(
newXDeclaration("1.0", "utf-8", "yes"),
newXComment("Comment at root level"),
newXProcessingInstruction("mso-application", "progid=\"Word.Document\""),
newXElement("Document",
newXAttribute("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;
publicstaticclassMyExtensions
{
publicstaticXElement GetXElement(thisXmlNode node)
{
XDocument xDoc = newXDocument();
using (XmlWriter xmlWriter = xDoc.CreateWriter())
node.WriteTo(xmlWriter);
return xDoc.Root;
}
publicstaticXmlNode GetXmlNode(thisXElement element)
{
using (XmlReader xmlReader = element.CreateReader())
{
XmlDocument xmlDoc = newXmlDocument();
xmlDoc.Load(xmlReader);
return xmlDoc;
}
}
publicstaticXDocument GetXDocument(thisXmlDocument document)
{
XDocument xDoc = newXDocument();
using (XmlWriter xmlWriter = xDoc.CreateWriter())
document.WriteTo(xmlWriter);
XmlDeclaration decl =
document.ChildNodes.OfType<XmlDeclaration>().FirstOrDefault();
if (decl != null)
xDoc.Declaration = newXDeclaration(decl.Version, decl.Encoding,
decl.Standalone);
return xDoc;
}
publicstaticXmlDocument GetXmlDocument(thisXDocument document)
{
using (XmlReader xmlReader = document.CreateReader())
{
XmlDocument xmlDoc = newXmlDocument();
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;
}
}
}
classProgram
{
staticvoid Main(string[] args)
{
XElement element = newXElement("Element",
newXElement("Child",
newXAttribute("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 = newXDocument(
newXDeclaration("1.0", "utf-8", "yes"),
newXComment("Comment at root level"),
newXProcessingInstruction("mso-application", "progid=\"Word.Document\""),
newXElement("Document",
newXAttribute("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();
}
}
Clik here to view.