Melhor maneira de obter InnerXml de um XElement?

votos
136

Qual é a melhor maneira de obter o conteúdo do mista bodyelemento no código abaixo? O elemento pode conter XHTML ou texto, mas eu só quero o seu conteúdo em forma de string. O XmlElementtipo tem a InnerXmlpropriedade, que é exatamente o que eu estou atrás.

O código como escrito quase faz o que eu quero, mas inclui torno <body>... </body>elemento, que eu não quero.

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()
                };
Publicado 06/08/2008 em 19:16
fonte usuário
Em outras línguas...                            


15 respostas

votos
192

Eu queria ver qual destas soluções sugeridas realizada melhor, então eu corri alguns testes comparativos. Fora do interesse, eu também comparou os métodos LINQ para o velho liso System.Xml método sugerido por Greg. A variação foi interessante e não o que eu esperava, com os métodos mais lentos sendo mais de 3 vezes mais lento do que o mais rápido .

Os resultados ordenados por mais rápido para o mais lento:

  1. CreateReader - Instância Hunter (0,113 segundos)
  2. Plain System.Xml velho - Greg Hurlman (0,134 segundos)
  3. Agregada com concatenação - Mike Powell (0,324 segundos)
  4. StringBuilder - Vin (0,333 segundos)
  5. String.Join em array - Terry (0,360 segundos)
  6. String.Concat na matriz - Marcin Kosieradzki (0,364)

Método

Eu usei um único documento XML com 20 nós idênticos (chamado de 'dica'):

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

Os números mostrados como segundos acima são o resultado de extrair o "XML interior" dos 20 nós, 1000 vezes consecutivas, e tomando a média (média) de 5 ensaios. Eu não inclui o tempo que levou para carregar e analisar o XML em um XmlDocument(para o System.Xml método) ouXDocument (para todos os outros).

Os algoritmos LINQ I utilizados foram: (C # - todos tomar um XElement"pai" e retornar a string XML interno)

CreateReader:

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

return reader.ReadInnerXml();

Agregada com concatenação:

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

String.Join em matriz:

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

String.concat em matriz:

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

Eu não mostrei o algoritmo "Plain velho System.Xml" aqui como ele é apenas chamando .InnerXml em nós.


Conclusão

Se o desempenho é importante (por exemplo, lotes de XML, analisados com frequência), eu usar de Daniel CreateReadermétodo de cada vez . Se você está apenas fazendo algumas consultas, você pode querer usar o método Aggregate mais concisa de Mike.

Se você estiver usando XML em grandes elementos com lotes de nós (talvez 100 's), você provavelmente começar a ver o benefício de usar StringBuilderem relação ao método agregado, mas não acabou CreateReader. Eu não acho que o Joine Concatmétodos jamais seria mais eficiente nessas condições por causa da pena de converter uma lista grande para uma grande variedade (mesmo óbvia aqui com listas menores).

Respondeu 10/11/2009 em 00:08
fonte usuário

votos
66

Acho que este é um método muito melhor (em VB, não deve ser difícil de traduzir):

Dado um XElement x:

Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml
Respondeu 18/03/2009 em 18:17
fonte usuário

votos
17

Que tal usar este método "extensão" na XElement? trabalhou para mim!

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

Ou usar um pouco 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 : O código acima tem que usar element.Nodes()ao contrário element.Elements(). Coisa muito importante lembrar a diferença entre os dois. element.Nodes()dá-lhe tudo como XText, XAttributeetc, mas XElementapenas elemento.

Respondeu 19/08/2008 em 21:29
fonte usuário

votos
12

(Thanks!) Com o devido crédito a quem descobriu e provou a melhor abordagem, aqui ele é embrulhado em um método de extensão:

public static string InnerXml(this XNode node) {
    using (var reader = node.CreateReader()) {
        reader.MoveToContent();
        return reader.ReadInnerXml();
    }
}
Respondeu 04/01/2013 em 21:21
fonte usuário

votos
9

Mantenha-o simples e eficiente:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • Agregada é de memória e desempenho ineficiente cordas ao concatenar
  • Usando Junte-( "", sth) está usando duas vezes maior matriz de cadeia de Concat ... E parece bastante estranho no código.
  • Usando + = parece muito estranho, mas aparentemente não é muito pior do que usar '+' - provavelmente seria otimizado para o mesmo código, becase resultado atribuição é não utilizado e pode ser removido com segurança pelo compilador.
  • StringBuilder é tão imperativo - e todos sabem que o "estado" desnecessária suga.
Respondeu 31/10/2009 em 18:22
fonte usuário

votos
7

Acabei usando este:

Body = t.Element("body").Nodes().Aggregate("", (b, node) => b += node.ToString());
Respondeu 06/08/2008 em 20:36
fonte usuário

votos
3

Pessoalmente, eu acabei de escrever um InnerXmlmétodo de extensão, utilizando o método das duas mãos:

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

Meu código de cliente é então tão concisa como seria com o antigo namespace System.Xml:

var innerXml = myXElement.InnerXml();
Respondeu 17/03/2010 em 09:47
fonte usuário

votos
2

@ Greg: Parece que você editou sua resposta ser uma resposta completamente diferente. Para que a minha resposta é sim, eu poderia fazer isso usando System.Xml mas estava esperando para obter meus pés molhados com LINQ to XML.

Vou deixar a minha resposta original abaixo no caso de alguém maravilhas outra pessoa porque eu não posso simplesmente usar a propriedade .Value do XElement para conseguir o que eu preciso:

@ Greg: A propriedade Value concatena todo o conteúdo de texto de todos os nós filho. Então, se o elemento corpo só contém texto ele funciona, mas se ele contém XHTML eu recebo todo o texto concatenados juntos, mas nenhum dos tags.

Respondeu 06/08/2008 em 19:25
fonte usuário

votos
1

doc.ToString () ou doc.ToString (SaveOptions) faz o trabalho. veja http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx

Respondeu 13/10/2014 em 21:08
fonte usuário

votos
1

// usando Regex pode ser mais rápido para cortar simplesmente a começar e elemento fim tag

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);
Respondeu 08/02/2014 em 05:55
fonte usuário

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

Vai fazer o trabalho para você

Respondeu 25/10/2018 em 19:07
fonte usuário

votos
0

você sabe? a melhor coisa a fazer é voltar para CDATA :( im olhando para soluções aqui, mas eu acho que CDATA é de longe o mais simples e mais barato, não é o mais conveniente para desenvolver com tho

Respondeu 26/10/2009 em 00:39
fonte usuário

votos
0

Querendo saber se (Repare que eu me livrei da b + = e apenas se b +)

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

pode ser um pouco menos eficiente do que

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

Não 100% de certeza ... mas olhando para Aggregate () e String.Join () no refletor ... Eu acho que eu lê-lo como Aggregate apenas acrescentando um valor de retorno, de modo essencialmente você obtém:

string = corda + corda

contra String.Join, tem alguma menção lá de FastStringAllocation ou algo assim, o que me faz coisa o pessoal da Microsoft pode ter colocado algum impulso desempenho extra lá. É claro que meu .ToArray () chamar o meu negate isso, mas eu só queria oferecer outra sugestão.

Respondeu 18/03/2009 em 17:57
fonte usuário

votos
0

É possível usar os objetos System.Xml namespace para começar o trabalho feito aqui em vez de usar o LINQ? Como você já mencionado, XmlNode.InnerXml é exatamente o que você precisa.

Respondeu 06/08/2008 em 19:20
fonte usuário

votos
-2
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();
}
Respondeu 13/08/2010 em 08:05
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more