.net - La meilleure façon d'obtenir InnerXml d'un XElement?

Translate

Quelle est la meilleure façon d'obtenir le contenu du mixbodyélément dans le code ci-dessous? L'élément peut contenir du XHTML ou du texte, mais je veux juste que son contenu soit sous forme de chaîne. leXmlElementtype a leInnerXmlpropriété qui est exactement ce que je recherche.

Le code tel qu'il est écritpresquefait ce que je veux, mais inclut l'environnement<body>...</body>élément, dont je ne veux pas.

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()
                };
This question and all comments follow the "Attribution Required."

Toutes les réponses

Translate

Je voulais voir laquelle de ces solutions suggérées fonctionnait le mieux, j'ai donc effectué des tests comparatifs. Par intérêt, j'ai également comparé les méthodes LINQ à l'ancienneSystem.Xmlméthode suggérée par Greg. La variation était intéressante et pas ce à quoi je m'attendais, les méthodes les plus lentes étantplus de 3 fois plus lent que le plus rapide.

Les résultats classés du plus rapide au plus lent:

  1. CreateReader - Instance Hunter (0,113 seconde)
  2. Plain old System.Xml - Greg Hurlman (0,134 secondes)
  3. Agrégat avec concaténation de chaînes - Mike Powell (0,324 seconde)
  4. StringBuilder - Vin (0,333 seconde)
  5. String.Join on array - Terry (0,360 seconde)
  6. String.Concat sur tableau - Marcin Kosieradzki (0.364)

Méthode

J'ai utilisé un seul document XML avec 20 nœuds identiques (appelé `` indice ''):

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

Les nombres indiqués en secondes ci-dessus sont le résultat de l'extraction du "XML interne" des 20 nœuds, 1000 fois de suite, et de la moyenne (moyenne) de 5 exécutions. Je n'ai pas inclus le temps nécessaire pour charger et analyser le XML dans unXmlDocument(pour leSystem.Xmlméthode) ouXDocument(pour tous les autres).

Les algorithmes LINQ que j'ai utilisés étaient:(C # - tous prennent unXElement"parent" et renvoie la chaîne XML interne)

CreateReader:

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

return reader.ReadInnerXml();

Agréger avec concaténation de chaînes:

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 sur le tableau:

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

String.Concat sur tableau:

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

Je n'ai pas montré ici l'algorithme "Plain old System.Xml" car il appelle simplement .InnerXml sur les nœuds.


Conclusion

Si les performances sont importantes (par exemple, beaucoup de XML, analysé fréquemment), jeutiliser Daniel'sCreateReaderméthode à chaque fois. Si vous ne faites que quelques requêtes, vous voudrez peut-être utiliser la méthode Aggregate plus concise de Mike.

Si vous utilisez XML sur de grands éléments avec beaucoup de nœuds (peut-être des centaines), vous commencerez probablement à voir l'avantage d'utiliserStringBuildersur la méthode agrégée, mais pas surCreateReader. Je ne pense pas queJoinetConcatLes méthodes seraient toujours plus efficaces dans ces conditions en raison de la pénalité de convertir une grande liste en un grand tableau (même évident ici avec des listes plus petites).

La source
Translate

Je pense que c'est une bien meilleure méthode (en VB, ne devrait pas être difficile à traduire):

Étant donné un XElement x:

Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml
La source
Vin
Translate

Que diriez-vous d'utiliser cette méthode "d'extension" sur XElement? travaillé pour moi!

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

Remarque: Le code ci-dessus doit utiliserelement.Nodes()par opposition àelement.Elements(). Chose très importante pour se rappeler la différence entre les deux.element.Nodes()vous donne tout commeXText, XAttributeetc, maisXElementseulement un élément.

La source
Translate

Avec tout le mérite de ceux qui ont découvert et prouvé la meilleure approche (merci!), La voici résumée dans une méthode d'extension:

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

Restez simple et efficace:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • L'agrégat est la mémoire et les performances inefficaces lors de la concaténation de chaînes
  • Utiliser Join ("", sth) utilise un tableau de chaînes deux fois plus grand que Concat ... Et cela semble assez étrange dans le code.
  • Utiliser + = semble très étrange, mais apparemment n'est pas bien pire que d'utiliser '+' - serait probablement optimisé pour le même code, car le résultat de l'affectation de cas est inutilisé et pourrait être supprimé en toute sécurité par le compilateur.
  • StringBuilder est tellement impératif - et tout le monde sait qu'un «état» inutile est nul.
La source
Translate

J'ai fini par utiliser ceci:

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

Personnellement, j'ai fini par écrire unInnerXmlméthode d'extension à l'aide de la méthode Aggregate:

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

Mon code client est alors aussi laconique qu'il le serait avec l'ancien espace de noms System.Xml:

var innerXml = myXElement.InnerXml();
La source
Translate

@Greg: Il semble que vous ayez modifié votre réponse pour être une réponse complètement différente. Ce à quoi ma réponse est oui, je pourrais le faire en utilisant System.Xml mais j'espérais me mouiller les pieds avec LINQ to XML.

Je laisserai ma réponse originale ci-dessous au cas où quelqu'un d'autre se demanderait pourquoi je ne peux pas simplement utiliser la propriété .Value de XElement pour obtenir ce dont j'ai besoin:

@Greg: la propriété Value concatène tout le contenu textuel de tous les nœuds enfants. Donc, si l'élément body contient uniquement du texte, cela fonctionne, mais s'il contient du XHTML, je récupère tout le texte concaténé mais aucune des balises.

La source
Translate

// l'utilisation de Regex peut être plus rapide pour simplement couper la balise d'élément de début et de fin

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);
La source
Translate

doc.ToString () ou doc.ToString (SaveOptions) fait le travail. Voirhttp://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx

La source
Translate

Est-il possible d'utiliser les objets d'espace de noms System.Xml pour effectuer le travail ici au lieu d'utiliser LINQ? Comme vous l'avez déjà mentionné, XmlNode.InnerXml est exactement ce dont vous avez besoin.

La source
Ralap Lee
Translate

Je me demande si (remarquez que je me suis débarrassé du b + = et que j'ai juste b +)

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

pourrait être légèrement moins efficace que

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

Pas sûr à 100% ... mais en regardant Aggregate () et string.Join () in Reflector ... IpenseJe l'ai lu comme agrégat en ajoutant simplement une valeur de retour, donc vous obtenez essentiellement:

chaîne = chaîne + chaîne

versus string.Join, il contient une mention de FastStringAllocation ou quelque chose du genre, ce qui me fait penser que les gens de Microsoft ont peut-être amélioré les performances. Bien sûr, mon .ToArray () appelle mon nier cela, mais je voulais juste proposer une autre suggestion.

La source
Translate

vous connaissez? la meilleure chose à faire est de revenir à CDATA: (je cherche des solutions ici mais je pense que CDATA est de loin le plus simple et le moins cher, pas le plus pratique à développer avec tho

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

Fera le travail pour vous

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