2013-08-12 14 views
15

Ho un file XML senza radice. Non posso cambiarlo. Sto cercando di analizzarlo, ma lo XDocument.Load non lo farà. Ho provato a impostare ConformanceLevel.Fragment, ma ottengo comunque un'eccezione generata. Qualcuno ha una soluzione a questo?C# XDocument Caricare con più radici

Ho provato con XmlReader, ma le cose sono incasinate e non riesco a farlo funzionare correttamente. XDocument.Load funziona alla grande, ma se ho un file con più radici, non lo è.

+1

'XDocument.Load' non funziona, perché quel tipo di file non è un documento XML valido. – MarcinJuraszek

+0

Puoi pubblicare il codice che hai provato finora? – christiandev

+0

Domanda diversa, stessa risposta: http://stackoverflow.com/a/9378442 – dtb

risposta

14

XmlReader sé lettura sostegno del frammento XML - cioè

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

Tuttavia XDocument.Load non supporta la lettura di XML frammentato.

Il modo rapido e sporco consiste nel disporre i nodi sotto una radice virtuale prima di richiamare lo XDocument.Parse. Ad esempio:

Questo approccio è limitato ai file xml di piccole dimensioni, in quanto è necessario leggere prima il file in memoria; e concatenare una stringa di grandi dimensioni significa spostare grandi oggetti nella memoria, il che è meglio evitare.

se le cose di performance si dovrebbe essere la lettura nodi in XDocument one-by-one tramite XmlReader come spiegato in ottime @ risposta s' Martin-Honnen (https://stackoverflow.com/a/18203952/2440262)

Se si utilizza API che dà per scontato che XmlReader itera su XML valido, e le questioni di prestazioni, è possibile utilizzare l'approccio-stream aderito invece:

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 - vedi ad esempio https://gist.github.com/svejdo1/b9165192d313ed0129a679c927379685

Nota: XDocument carica l'intero xml in memoria. Quindi non utilizzarlo per file di grandi dimensioni - invece utilizzare XmlReader per iterazione e carico solo i bit croccanti come XElement via XNode.ReadFrom(...)

+6

L'uso di 'XDocument.Parse()' dovrebbe eliminare la necessità di avvolgere la stringa in un 'StringReader'. – CoderDennis

0

documento XML non può avere più di un elementi radice. È richiesto un elemento radice. Puoi fare una cosa. Ottieni tutti gli elementi fragment e li racchiudili in un elemento radice e analizzarli con XDocument.

Questo sarebbe l'approccio migliore e più semplice che si possa pensare.

1

Se si desidera utilizzare XmlDocument.Load(), è necessario avvolgere il contenuto in un nodo radice.

o si potrebbe provare qualcosa di simile ...

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

L'unica in memoria rappresentazioni ad albero nel framework .NET che può trattare con i frammenti sono l'XmlDocumentFragment nella implementazione DOM di .NET modo che avrebbe bisogno creare un XmlDocument e un frammento con es

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

o è XPathDocument dove è possibile crearne uno utilizzando un XmlReader con ConformanceLevel impostato Fragment:

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

Ovviamente XPathNavigator è di sola lettura.

Se si desidera utilizzare LINQ in XML, sono d'accordo con i suggerimenti forniti che è necessario creare un XElement come wrapper. Invece di inserire una stringa con il contenuto del file, potresti comunque utilizzare XNode.ReadFrom con un XmlReader, ad es.

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

poi

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

Che potrebbe lavorare meglio e in modo più efficiente che leggere tutto in una stringa.