2012-02-21 14 views
6

Sto scrivendo un server di rete in C# .NET 4.0. Esiste una connessione TCP/IP di rete tramite la quale posso ricevere elementi XML completi. Arrivano regolarmente e ho bisogno di elaborarli immediatamente. Ogni elemento XML è un documento XML completo di per sé, quindi ha un elemento di apertura, diversi sottonodi e un elemento di chiusura. Non esiste un singolo elemento radice per l'intero stream. Così, quando ho aperto il collegamento, quello che ottengo è come questo:Lettura di interi elementi da un flusso di rete XML

<status> 
    <x>123</x> 
    <y>456</y> 
</status> 

Poi qualche tempo dopo continua:

<status> 
    <x>234</x> 
    <y>567</y> 
</status> 

E così via. Ho bisogno di un modo per leggere la stringa XML completa fino al completamento di un elemento di stato. Non voglio farlo con metodi di lettura di testo normale perché non so in che tipo di dati arrivano i dati. Non posso in alcun modo aspettare che l'intero flusso sia finito, come spesso descritto altrove. Ho provato a usare la classe XmlReader ma la sua documentazione è strana, i metodi non funzionano, il primo elemento è perso e dopo l'invio del secondo elemento, si verifica un'eccezione Xml perché ci sono due elementi radice.

+0

Dubito che XmlReader sia la strada da percorrere, come ci si aspetterebbe da un elemento radice e non è orientato per gli elementi XML in streaming continuo. Di solito quando gestisci connessioni basate su socket, ascolterai la connessione e cercheresti di identificare l'inizio e la fine di ogni blocco di dati.So che hai detto che non vuoi, ma mi aspetterei che dovresti usare metodi di lettura in testo normale per identificare almeno una porzione di XML che puoi quindi analizzare. –

+0

è necessario istanziare un nuovo lettore ogni messaggio, in cui il messaggio è un nodo completo. –

risposta

7

Prova questo:

var settings = new XmlReaderSettings 
{ 
    ConformanceLevel = ConformanceLevel.Fragment 
}; 

using (var reader = XmlReader.Create(stream, settings)) 
{ 
    while (!reader.EOF) 
    { 
     reader.MoveToContent(); 

     var doc = XDocument.Load(reader.ReadSubtree()); 

     Console.WriteLine("X={0}, Y={1}", 
      (int)doc.Root.Element("x"), 
      (int)doc.Root.Element("y")); 

     reader.ReadEndElement(); 
    } 
} 
+0

Questo si avvicina già ad esso. Può leggere più elementi radice XML nello stesso flusso, ma la chiamata ReadEndElement bloccherà sempre fino a quando arriva l'elemento successivo. Posso rimuoverlo in sicurezza o dovrò aspettare un altro evento? – ygoe

+0

Se è ReadEndElement che blocca e non ReadSubtree, è possibile elaborare semplicemente XDocument prima della chiamata ReadEndElement. – dtb

+0

Grazie, sta funzionando bene ora. – ygoe

0

Non sono sicuro che ci sia qualcosa di built-in che funzioni. Aprirei un generatore di stringhe, lo compilo fino a quando vedo un tag </status> e poi lo analizzo utilizzando l'ordinario XmlDocument.

+0

P.S. So che hai scritto che non vuoi usare i metodi String, ma sono abbastanza sicuro che sia il tuo unico modo. – Svarog

1

Se si modifica il "livello di conformità" in "frammento", potrebbe funzionare con XmlReader.

Questa è una (leggermente modificato) esempio dalla MSDN:

XmlReaderSettings settings = new XmlReaderSettings(); 
settings.ConformanceLevel = ConformanceLevel.Fragment; 
XmlReader reader = XmlReader.Create(streamOfXmlFragments, settings); 
1

Si potrebbe utilizzare XElement.Load che è significato di più per lo streaming di frammenti XML elemento che è nuovo in .net 3.5 e supporta anche la lettura direttamente da un flusso.

Dai un'occhiata alla System.Xml.Linq

penso che si potrebbe ancora aggiungere qualche logica di controllo in modo da suddividere i messaggi che si ricevono, ma si può anche dare un andare.

0

non sostanzialmente diverso dalla soluzione di DTB, ma linqier

static IEnumerable<XDocument> GetDocs(Stream xmlStream) 
{ 
    var xmlSettings = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment }; 
    using (var xmlReader = XmlReader.Create(xmlStream, xmlSettings)) 
    { 
     var xmlPathNav = new XPathDocument(xmlReader).CreateNavigator(); 
     foreach (var selectee in xmlPathNav.Select("/*").OfType<XPathNavigator>()) 
      yield return XDocument.Load(selectee.ReadSubtree()); 
    } 
} 

mi sono imbattuto in un problema simile in PowerShell, ma la domanda del richiedente ero in C#, così ho cercato di tradurlo (e verificato che essa lavori). Here è dove ho trovato l'indizio che mi ha fatto superare gli ultimi piccoli urti ("... Il modo in cui XPathDocument fa la sua magia è creare un nodo radice" trasparente "e trattenere i frammenti da esso. Le query XPath possono utilizzare l'asse del nodo radice e vengono ancora correttamente risolte nei frammenti. ")

I frammenti di XML con cui sto lavorando risultano essere piccoli. Se avessi pezzi più grandi, probabilmente dovresti esaminare XStreamingElement - può aggiungere molta complessità ma anche ridurre notevolmente l'utilizzo della memoria quando si gestiscono grandi volumi di XML.

Problemi correlati