visual valid typeparam returns name example comment c# xml linq-to-xml

valid - C#XDocumento de carga con múltiples raíces



typeparam c# (4)

Tengo un archivo XML sin root. No puedo cambiar esto. Estoy intentando analizarlo, pero XDocument.Load no lo hará. He intentado establecer ConformanceLevel.Fragment , pero todavía se produce una excepción. ¿Alguien tiene una solución para esto?

Lo intenté con XmlReader , pero las cosas están mal y no funcionan bien. XDocument.Load funciona muy bien, pero si tengo un archivo con varias raíces, no lo hace.


El documento XML no puede tener más de un elemento raíz. Se requiere un elemento raíz. Usted puede hacer una cosa. Obtenga todos los elementos del fragment y envuélvalos en un elemento raíz y XDocument con XDocument .

Este sería el mejor y más fácil enfoque que uno podría pensar.


Las únicas representaciones de árbol en memoria en el marco .NET que pueden tratar con fragmentos son XmlDocumentFragment en la implementación de DOM de .NET, por lo que deberá crear un XmlDocument y un fragmento con, por ejemplo,

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

o es XPathDocument donde puede crear uno usando un XmlReader con ConformanceLevel establecido en Fragmento:

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();

Obviamente, XPathNavigator es de solo lectura.

Si desea usar LINQ to XML, entonces estoy de acuerdo con las sugerencias hechas de que necesita crear un XElement como un contenedor. Sin embargo, en lugar de tirar una cadena con el contenido del archivo, podría utilizar XNode.ReadFrom con un XmlReader, por ejemplo

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; } } }

entonces

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

Eso podría funcionar mejor y más eficientemente que leer todo en una cadena.


Si quisiera usar XmlDocument.Load (), entonces necesitaría envolver el contenido en un nodo raíz.

o podrías intentar algo como esto ...

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


XmlReader sí admite la lectura de fragmentos xml, es decir,

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

Sin embargo, XDocument.Load no admite la lectura de xml fragmentado.

La forma rápida y sucia es envolver los nodos en una raíz virtual antes de invocar XDocument.Parse . Me gusta:

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

Este enfoque se limita a pequeños archivos xml, ya que primero tiene que leer el archivo en la memoria; y concatenar una cadena grande significa mover objetos grandes en la memoria, lo que es mejor evitar.

Si el rendimiento es importante, debería leer los nodos en XDocument uno por uno a través de XmlReader como se explica en la excelente respuesta de @ Martin-Honnen ( https://.com/a/18203952/2440262 )

Si usa una API que da por sentado que XmlReader itera sobre un xml válido, y que el rendimiento es importante, puede usar el enfoque de flujo unido en su lugar:

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: vea, por ejemplo, https://gist.github.com/svejdo1/b9165192d313ed0129a679c927379685

Nota: XDocument carga todo el xml en la memoria. Así que no lo use para archivos grandes; en su lugar, use XmlReader para la iteración y cargue solo los bits crujientes como XElement través de XNode.ReadFrom(...)