C#XDocument加载多个根

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

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

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

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

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

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

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

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

如果性能很重要,你应该通过XmlReader逐个读取节点到XDocument ,正如优秀@ Martin-Honnen的回答( https://stackoverflow.com/a/18203952/2440262 )所述

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

 using (var jointStream = new MultiStream()) using (var openTagStream = new MemoryStream(Encoding.ASCII.GetBytes(""), false)) using (var fileStream = File.Open(@"fragment.xml", FileMode.Open, FileAccess.Read, FileShare.Read)) using (var closeTagStream = new MemoryStream(Encoding.ASCII.GetBytes(""), 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 – 请参阅https://gist.github.com/svejdo1/b9165192d313ed0129a679c927379685

注意: XDocument将整个xml加载到内存中。 所以不要将它用于大文件 – 而是使用XmlReader进行迭代,并通过XNode.ReadFrom(...)将松脆的位加载为XElement

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

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

或者是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 eg XPathNavigator nav = doc.CreateNavigator(); 

显然XPathNavigator是只读的。

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

 public static class MyExtensions { public static IEnumerable ParseFragment(XmlReader xr) { xr.MoveToContent(); XNode node; while (!xr.EOF && (node = XNode.ReadFrom(xr)) != null) { yield return node; } } } 

然后

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

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

如果您想使用XmlDocument.Load(),则需要将内容包装在根节点中。

或者你可以试试这样的……

 while (xmlReader.Read()) { if (xmlReader.NodeType == XmlNodeType.Element) { XmlDocument d = new XmlDocument(); d.CreateElement().InnerText = xmlReader.ReadOuterXml(); } } 

XML文档不能有多个根元素。 需要一个根元素。 你可以做一件事。 获取所有fragment元素并将它们包装到根元素中并使用XDocument对其进行解析。

这将是人们可以想到的最好和最简单的方法。