C#XDocument加载多个根

Dar*_*ody 20 c# xml linq-to-xml

我有一个没有root的XML文件.我无法改变这一点.我试图解析它,但XDocument.Load不会这样做.我试图设置ConformanceLevel.Fragment,但我仍然抛出异常.有人有解决方案吗?

我尝试过XmlReader,但事情搞砸了,无法正常工作.XDocument.Load效果很好,但是如果我有一个有多个根的文件,它就没有.

Ond*_*dar 18

XmlReader 本身确实支持读取xml片段 - 即

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

但是XDocument.Load不支持读取碎片xml.

快速而肮脏的方法是在调用之前将节点包装在一个虚拟根目录下XDocument.Parse.喜欢:

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

这种方法仅限于小的xml文件 - 因为你必须先将文件读入内存; 并且连接大字符串意味着在内存中移动大对象 - 这是最好的避免.

如果性能很重要,你应该按照优秀@ Martin-Honnen的回答(/sf/answers/1274276671/)中的说明XDocument逐个读取节点.XmlReader

如果您使用理所当然的API来XmlReader迭代有效的xml,并且性能很重要,那么您可以使用join-stream方法:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

MultiStream - 请参阅https://gist.github.com/svejdo1/b9165192d313ed0129a679c927379685

注意:XDocument将整个xml加载到内存中.所以不要将它用于大文件 - 而是XmlReader用于迭代并仅加载作为XElementvia 的crispy位XNode.ReadFrom(...)

  • 使用`XDocument.Parse()`应该不需要在`StringReader`中包装字符串. (6认同)

Mar*_*nen 13

.NET框架中唯一可以处理片段的内存树表示是XmlDocumentFragment.NET的DOM实现,因此您需要创建一个XmlDocument和一个片段,例如

XmlDocument doc = new XmlDocument();
XmlDocumentFragment frag = doc.CreateDocumentFragment();
frag.InnerXml = stringWithXml; // for instance 
                               // frag.InnerXml = File.ReadAllText("fragment.xml");
Run Code Online (Sandbox Code Playgroud)

或者XPathDocument您可以使用将ConformanceLevel设置为Fragment的XmlReader创建一个:

XPathDocument doc;
using (XmlReader xr = 
                 XmlReader.Create("fragment.xml", 
                                   new XmlReaderSettings()
                                   {
                                       ConformanceLevel = ConformanceLevel.Fragment
                                    }))
{
  doc = new XPathDocument(xr);
}

// new create XPathNavigator for read out data e.g.
XPathNavigator nav = doc.CreateNavigator();
Run Code Online (Sandbox Code Playgroud)

显然XPathNavigator是只读的.

如果您想使用LINQ to XML,那么我同意您需要创建一个XElement作为包装器的建议.您可以使用XNode.ReadFromXmlReader,而不是使用文件内容拉入字符串

public static class MyExtensions
{
    public static IEnumerable<XNode> ParseFragment(XmlReader xr)
    {
        xr.MoveToContent();
        XNode node;
        while (!xr.EOF && (node = XNode.ReadFrom(xr)) != null)
        {
            yield return node;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后

XElement root = new XElement("root", 
                             MyExtensions.ParseFragment(XmlReader.Create(
                                 "fragment.xml", 
                                 new XmlReaderSettings() {
                                 ConformanceLevel = ConformanceLevel.Fragment })));
Run Code Online (Sandbox Code Playgroud)

这可能比将所有内容都读成字符串更好,更有效.