Friday, July 10, 2009

Combining XML documents into one big one with XInclude

I needed to figure out how to include transparently some xml documents into another xml last week. We store our site navigation in one XML, but due to a decentralized management of this file, I need to cut this navigation into different file parts.

For this, the W3C XInlude standard was developed. Unfortunately, there is still no support for this in the .NET Framework. The solution is the use the Mvp.Xml library from codeplex, which includes XInclude support. A strong point of XInclude is the fallback possibilities of this standard, so when the included file isn't present, the output is predictable.

With XInclude, a XML fragment can be included from the original document:

<topic name="External Topic" path="/external-topic" id="18442">
<xi:include href="../../external-topic/navigation.xml"
xmlns:xi="http://www.w3.org/2003/XInclude"
xpointer="xpointer(/navigation/*)">
<xi:fallback>
<subject name="Fallback Subject" pad="/external-topic/FB"
id="19672" />
</xi:fallback>
</xi:include>
</topic>


Where the included XML fragment will be:

<?xml version="1.0"?>
<navigation name="External Topic" path="/external-topic"
id="18442" xmlns:xi="http://www.w3.org/2001/XInclude">
<subject name="Poll" path="/external-topic/poll" id="19670" />
<subject name="Text" path="/external-topic/text" id="19671" />
<subject name="Links" path="/external-topic/links id="19672" />
</navigation>

While XInclude describes the format of the XML document, we still need a XInclude aware XmlReader. There is one within the Mvp.Xml package. The old XML reading code:

mXmlDoc = new XmlDocument();
mXmlDoc.Load(mXmlFile.FullName);

should be changed to:

mXmlDoc = new XmlDocument();
using (XmlReader reader = new XmlIncludingReader(mXmlFile.FullName))
{
mXmlDoc.Load(reader);
}

The resulting mXmlDoc document representations are equal in both situations. Only the XInclude result includes extra xml:base attributes.

The resulting XML is:

<code style="xml">
<topic name="External Topic" path="/external-topic" id="18442">
<subject name="Poll" path="/external-topic/poll" id="19670" xml:base="../../external-topic/navigation.xml" />
<subject name="Text" path="/external-topic/text" id="19671" xml:base="../../external-topic/navigation.xml" />
<subject name="Links" path="/external-topic/links id="19672" xml:base="../../external-topic/navigation.xml" />
</topic>

These xml:base attributes can be used to add extra CacheDependencies for the storage of the resulting XML document to the application cache, so the cache will be invalidated as one of the included XML documents change.

No comments: