Pregunta

¿Cuál es la mejor manera para obtener el contenido de la mezcla de body elemento en el código de abajo?El elemento puede contener XHTML o de texto, pero sólo quiero su contenido en forma de cadena.El XmlElement el tipo tiene la InnerXml la propiedad, que es exactamente lo que yo busco.

El código escrito casi hace lo que quiero, sino que incluye a los alrededores <body>...</body> elemento, que no quiero.

XDocument doc = XDocument.Load(new StreamReader(s));
var templates = from t in doc.Descendants("template")
                where t.Attribute("name").Value == templateName
                select new
                {
                   Subject = t.Element("subject").Value,
                   Body = t.Element("body").ToString()
                };
¿Fue útil?

Solución

Yo quería ver cuál de estas soluciones se realiza mejor, así que me hicieron algunas pruebas comparativas.De interés, yo también en comparación con los métodos de LINQ a la llanura de edad System.Xml método sugerido por Greg.La variación fue muy interesante, y no lo que yo esperaba, con el más lento de los métodos que se más de 3 veces más lento que el más rápido.

Los resultados ordenados por el más rápido al más lento:

  1. CreateReader Instancia Hunter (0.113 segundos)
  2. El viejo y simple System.Xml - Greg Hurlman (0.134 segundos)
  3. Agregado con la concatenación de cadenas - Mike Powell (0.324 segundos)
  4. StringBuilder - Vin (0.333 segundos)
  5. De la cadena.Unirse a la matriz de Terry (0.360 segundos)
  6. De la cadena.Concat en la matriz de Marcin Kosieradzki (0.364)

Método

He utilizado un único documento XML con 20 nodos idénticos (el llamado 'hint'):

<hint>
  <strong>Thinking of using a fake address?</strong>
  <br />
  Please don't. If we can't verify your address we might just
  have to reject your application.
</hint>

Los números que se muestran como segundos anteriores son el resultado de la extracción del interior "XML" de los 20 nodos, 1000 veces en una fila, y tomando el promedio (media) de 5 pistas.Yo no incluyen el tiempo que se tomó para cargar y analizar el XML en una XmlDocument (para el System.Xml método) o XDocument (para todos los demás).

El LINQ de los algoritmos que se utilizaron fueron: (C# - todos a tomar un XElement "padre" y devolver el interior de la cadena XML)

CreateReader:

var reader = parent.CreateReader();
reader.MoveToContent();

return reader.ReadInnerXml();

Agregado con la concatenación de cadenas:

return parent.Nodes().Aggregate("", (b, node) => b += node.ToString());

StringBuilder:

StringBuilder sb = new StringBuilder();

foreach(var node in parent.Nodes()) {
    sb.Append(node.ToString());
}

return sb.ToString();

De la cadena.Unirse en un array:

return String.Join("", parent.Nodes().Select(x => x.ToString()).ToArray());

De la cadena.Concat en la matriz:

return String.Concat(parent.Nodes().Select(x => x.ToString()).ToArray());

No he mostrado el "Plain old System.Xml" algoritmo de aquí, ya que a sólo llamadas .InnerXml en los nodos.


Conclusión

Si el rendimiento es importante (por ejemplo,un montón de XML, se analiza con frecuencia), me gustaría el uso de Daniel CreateReader método cada vez.Si solo tienes que hacer un par de consultas, puede que desee utilizar Mike es más conciso método de agregación.

Si usted está usando XML en grandes elementos con un montón de nodos (tal vez de 100), que probablemente iba a comenzar a ver los beneficios de usar StringBuilder sobre el método de agregación, pero no más de CreateReader.No creo que el Join y Concat métodos cada vez más eficaces en estas condiciones a causa de la pena de convertir una gran lista para una gran variedad (incluso obvio aquí con listas de menor tamaño).

Otros consejos

Creo que esta es mucho mejor método (en VB, no debería ser difícil de traducir):

Dado un XElement x:

Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml

Cómo sobre el uso de esta "extensión" método de XElement?trabajó para mí !

public static string InnerXml(this XElement element)
{
    StringBuilder innerXml = new StringBuilder();

    foreach (XNode node in element.Nodes())
    {
        // append node's xml string to innerXml
        innerXml.Append(node.ToString());
    }

    return innerXml.ToString();
}

O utilizar un poco de Linq

public static string InnerXml(this XElement element)
{
    StringBuilder innerXml = new StringBuilder();
    doc.Nodes().ToList().ForEach( node => innerXml.Append(node.ToString()));

    return innerXml.ToString();
}

Nota:El código anterior se tiene que utilizar element.Nodes() como contraposición a element.Elements().Cosa muy importante a recordar la diferencia entre los dos. element.Nodes() da todo igual XText, XAttribute etc, pero XElement sólo un Elemento.

Con todo el debido crédito a aquellos que descubrió y demostró ser el mejor enfoque (¡gracias!), aquí está envuelto en un método de extensión:

public static string InnerXml(this XNode node) {
    using (var reader = node.CreateReader()) {
        reader.MoveToContent();
        return reader.ReadInnerXml();
    }
}

Mantenerlo simple y eficiente:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • El agregado es el de la memoria y el rendimiento ineficiente cuando la concatenación de cadenas
  • El uso de Join("", sth) es el uso de dos veces más grande de matriz de cadena de Concat...Y se ve muy extraño en el código.
  • Usando += se ve muy extraño, pero al parecer no es mucho peor que el uso de '+' - probablemente estaría optimizado para el mismo código, becase asignación de resultado no está en uso y puede eliminarse con seguridad por el compilador.
  • StringBuilder es imprescindible - y todo el mundo sabe que innecesaria "estado" chupa.

Terminé usando esto:

Body = t.Element("body").Nodes().Aggregate("", (b, node) => b += node.ToString());

Personalmente, terminé de escribir un InnerXml método de extensión mediante el método de agregación:

public static string InnerXml(this XElement thiz)
{
   return thiz.Nodes().Aggregate( string.Empty, ( element, node ) => element += node.ToString() );
}

Mi código de cliente es tan conciso como lo sería con el viejo System.Xml espacio de nombres:

var innerXml = myXElement.InnerXml();

@Greg:Parece que has editado tu respuesta para ser completamente diferente de respuesta.A lo que mi respuesta es sí, podría hacerlo mediante System.Xml pero tenía la esperanza de conseguir mis pies mojados con LINQ to XML.

Yo voy a dejar mi respuesta original a continuación en caso de que alguien más se pregunta por qué no puedo simplemente usar el XElement del .Valor de la propiedad para conseguir lo que necesito:

@Greg:El Valor de la propiedad concatena todos los contenidos de texto de cualquiera de los nodos hijos.Así que si el elemento de cuerpo contiene sólo el texto funciona, pero si contiene XHTML puedo obtener todo el texto se concatenan entre sí, pero ninguna de las etiquetas.

// usar Regex puede ser más rápido simplemente recortar el comienzo y el final de la etiqueta de elemento

var content = element.ToString();
var matchBegin = Regex.Match(content, @"<.+?>");
content = content.Substring(matchBegin.Index + matchBegin.Length);          
var matchEnd = Regex.Match(content, @"</.+?>", RegexOptions.RightToLeft);
content = content.Substring(0, matchEnd.Index);

doc.ToString() o doc.ToString(SaveOptions) hace el trabajo.Ver http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs 110).aspx

Es posible el uso de la System.Xml espacio de nombres de objetos para hacer el trabajo en lugar de usar LINQ?Como ya se mencionó, XmlNode.InnerXml es exactamente lo que usted necesita.

Preguntándose si (aviso me deshice de la b+= y sólo tengo b+)

t.Element( "body" ).Nodes()
 .Aggregate( "", ( b, node ) => b + node.ToString() );

podría ser un poco menos eficiente que

string.Join( "", t.Element.Nodes()
                  .Select( n => n.ToString() ).ToArray() );

No es 100% seguro...pero echando un vistazo a Aggregate() y de cadena.Join() en el Reflector...yo creo Yo lo leí como Agregado sólo añadiendo una devolución de valor, así que, esencialmente, se obtiene:

cadena = cadena + cadena

frente a la cadena.Únete, se ha mencionado allí de FastStringAllocation o algo, que me hace la cosa, la gente de Microsoft podría haber puesto algo más de impulso en el rendimiento de allí.Por supuesto a mi .ToArray() la llamada de mi negar eso, pero yo sólo quería ofrecer otra sugerencia.

sabes?la mejor cosa a hacer es volver a CDATA :( im buscando soluciones, pero me parece que CDATA es por lejos el más sencillo y barato, no es el más conveniente para desarrollar con tho

var innerXmlAsText= XElement.Parse(xmlContent)
                    .Descendants()
                    .Where(n => n.Name.LocalName == "template")
                    .Elements()
                    .Single()
                    .ToString();

Hará el trabajo para usted

public static string InnerXml(this XElement xElement)
{
    //remove start tag
    string innerXml = xElement.ToString().Trim().Replace(string.Format("<{0}>", xElement.Name), "");
    ////remove end tag
    innerXml = innerXml.Trim().Replace(string.Format("</{0}>", xElement.Name), "");
    return innerXml.Trim();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top