C# XDocument Load with multiple roots

XmlReader itself does support reading of xml fragment – i.e.

var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
using (var reader = XmlReader.Create("fragment.xml", settings))
{
  // you can work with reader just fine
}

However XDocument.Load does not support reading of fragmented xml.

Quick and dirty way is to wrap the nodes under one virtual root before you invoke the XDocument.Parse. Like:

var fragments = File.ReadAllText("fragment.xml");
var myRootedXml = "<root>" + fragments + "</root>";
var doc = XDocument.Parse(myRootedXml);

This approach is limited to small xml files – as you have to read file into memory first; and concatenating large string means moving large objects in memory – which is best avoided.

If performance matters you should be reading nodes into XDocument one-by-one via XmlReader as explained in excellent @Martin-Honnen ‘s answer (https://stackoverflow.com/a/18203952/2440262)

If you use API that takes for granted that XmlReader iterates over valid xml, and performance matters, you can use joined-stream approach instead:

using (var jointStream = new MultiStream())
using (var openTagStream = new MemoryStream(Encoding.ASCII.GetBytes("<root>"), false))
using (var fileStream = 
  File.Open(@"fragment.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
using (var closeTagStream = new MemoryStream(Encoding.ASCII.GetBytes("</root>"), false))
{
    jointStream.AddStream(openTagStream);
    jointStream.AddStream(fileStream);
    jointStream.AddStream(closeTagStream);
    using (var reader = XmlReader.Create(jointStream))
    {
        // now you can work with reader as if it is reading valid xml
    }
}

MultiStream – see for example https://gist.github.com/svejdo1/b9165192d313ed0129a679c927379685

Note: XDocument loads the whole xml into memory. So don’t use it for large files – instead use XmlReader for iteration and load just the crispy bits as XElement via XNode.ReadFrom(...)

Leave a Comment