2012-02-21 17 views
6

Estoy escribiendo un servidor de red en C# .NET 4.0. Hay una conexión TCP/IP de red sobre la cual puedo recibir elementos XML completos. Llegan regularmente y necesito procesarlos inmediatamente. Cada elemento XML es un documento XML completo en sí mismo, por lo que tiene un elemento de apertura, varios nodos secundarios y un elemento de cierre. No hay un único elemento raíz para toda la secuencia. Así que cuando abra la conexión, lo que se ve es la siguiente:Leer elementos completos de una secuencia de red XML

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

A continuación, un poco más tarde que sigue:

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

Y así sucesivamente. Necesito una forma de leer la cadena XML completa hasta que se complete un elemento de estado. No quiero hacer eso con métodos de lectura de texto sin formato porque no sé en qué formato llegan los datos. De ninguna manera puedo esperar hasta que se termine toda la secuencia, como se describe a menudo en otra parte. He intentado usar la clase XmlReader pero su documentación es extraña, los métodos no funcionan, el primer elemento se pierde y después de enviar el segundo elemento, se produce una XmlException porque hay dos elementos raíz.

+0

dudo XmlReader es el camino a seguir, ya que se puede esperar de un elemento raíz y no se preparaban para la transmisión continuamente elementos XML. Por lo general, al manejar conexiones basadas en sockets, estaría escuchando la conexión y buscando identificar el inicio y el final de cada fragmento de datos.Sé que dijiste que no querías, pero esperaba que tendrías que usar métodos de lectura de texto sin formato para, al menos, identificar un fragmento de XML que luego puedas analizar. –

+0

necesita crear una instancia de un nuevo lector en cada mensaje, donde el mensaje es un nodo completo. –

Respuesta

7

Prueba esto:

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

Esto ya se acerca. Puede leer múltiples elementos raíz XML en la misma secuencia, pero la llamada ReadEndElement siempre se bloqueará hasta que entre el siguiente elemento. ¿Puedo eliminarlo de manera segura o tendré que esperar otro evento? – ygoe

+0

Si se trata de ReadEndElement que bloquea y no ReadSubtree, simplemente puede procesar el XDocument antes de la llamada a ReadEndElement. – dtb

+0

Gracias, está funcionando bien ahora. – ygoe

0

No estoy seguro de que haya algo incorporado que lo haga. Abría un generador de cadenas, lo llenaba hasta que veía una etiqueta </status>, y luego lo analizaba utilizando el XmlDocument ordinario.

+0

P.S. Sé que usted escribió que no quiere usar métodos String, pero estoy bastante seguro de que es su único camino. – Svarog

1

Si cambia el "nivel de conformidad" a "fragmento", que podría funcionar con el XmlReader.

Este es un ejemplo (ligeramente modificada) de MSDN:

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

usted podría utilizar XElement.Load que está destinado más para la transmisión de fragmentos Elemento XML que es nuevo en .net 3.5 y también admite lectura directamente desde una secuencia.

Tenga una mirada en System.Xml.Linq

creo que es muy posible que todavía tiene que añadir un poco de lógica de control con el fin de dividir los mensajes que está recibiendo, pero es posible que así darle una oportunidad.

0

No es sustancialmente diferente de la solución de DTB, pero 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()); 
    } 
} 

me encontré con un problema similar en PowerShell, pero la pregunta del autor de la pregunta estaba en C#, por lo que he intentado traducirlo (y verificó que se trabajos). Here es donde encontré la pista que me ayudó a superar los pequeños baches ("... La forma en que XPathDocument hace su magia es creando un nodo raíz" transparente "y manteniendo los fragmentos de él. Digo que es transparente porque su consultas XPath pueden utilizar el eje de nodo raíz y aún así obtener adecuadamente resuelto los fragmentos... ")

los fragmentos de XML que estoy trabajando resultan ser más bien pequeña. Si tiene trozos más grandes, es probable que desee consultar XStreamingElement; puede agregar mucha complejidad pero también reducir en gran medida el uso de la memoria cuando se trata de grandes volúmenes de XML.

Cuestiones relacionadas