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
对其进行解析。
这将是人们可以想到的最好和最简单的方法。