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