.net - Der beste Weg, um InnerXml eines XElement zu erhalten?

Translate

Was ist der beste Weg, um den Inhalt der Mischung zu bekommenbodyElement im Code unten? Das Element kann entweder XHTML oder Text enthalten, aber ich möchte nur seinen Inhalt in Zeichenfolgenform. DasXmlElementTyp hat dieInnerXmlEigentum, das genau das ist, wonach ich suche.

Der Code wie geschriebenfastmacht was ich will, schließt aber die Umgebung ein<body>...</body>Element, das ich nicht will.

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

Alle Antworten

Translate

Ich wollte sehen, welche dieser Lösungsvorschläge am besten funktioniert, und führte daher einige Vergleichstests durch. Aus Interesse habe ich auch die LINQ-Methoden mit den einfachen alten verglichenSystem.XmlMethode von Greg vorgeschlagen. Die Variation war interessant und nicht das, was ich erwartet hatte, mit den langsamsten Methodenmehr als dreimal langsamer als der schnellste.

Die Ergebnisse sortiert nach schnellsten bis langsamsten:

  1. CreateReader - Instance Hunter (0,113 Sekunden)
  2. Einfache alte System.Xml - Greg Hurlman (0,134 Sekunden)
  3. Aggregat mit String-Verkettung - Mike Powell (0,324 Sekunden)
  4. StringBuilder - Vin (0,333 Sekunden)
  5. String.Join on Array - Terry (0,360 Sekunden)
  6. String.Concat auf Array - Marcin Kosieradzki (0.364)

Methode

Ich habe ein einzelnes XML-Dokument mit 20 identischen Knoten verwendet (als "Hinweis" bezeichnet):

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

Die oben als Sekunden angezeigten Zahlen sind das Ergebnis des Extrahierens des "inneren XML" der 20 Knoten, 1000 Mal hintereinander, und des Durchschnittswerts von 5 Läufen. Ich habe nicht die Zeit angegeben, die zum Laden und Parsen des XML in ein XML benötigt wurdeXmlDocument(für dieSystem.XmlMethode) oderXDocument(für alle anderen).

Die von mir verwendeten LINQ-Algorithmen waren:(C # - alle nehmen eineXElement"parent" und geben Sie die innere XML-Zeichenfolge zurück)

CreateReader:

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

return reader.ReadInnerXml();

Aggregat mit Zeichenfolgenverkettung:

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 on Array:

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

String.Concat auf Array:

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

Ich habe den Algorithmus "Plain old System.Xml" hier nicht gezeigt, da er nur .InnerXml auf Knoten aufruft.


Fazit

Wenn Leistung wichtig ist (z. B. viel XML, häufig analysiert), würde ichbenutze DanielsCreateReaderMethode jedes Mal. Wenn Sie nur ein paar Abfragen durchführen, möchten Sie möglicherweise Mikes präzisere Aggregatmethode verwenden.

Wenn Sie XML für große Elemente mit vielen Knoten (möglicherweise 100) verwenden, werden Sie wahrscheinlich den Nutzen der Verwendung erkennenStringBuilderüber die Aggregatmethode, aber nicht überCreateReader. Ich glaube nichtJoinundConcatMethoden wären unter diesen Bedingungen immer effizienter, da die Konvertierung einer großen Liste in ein großes Array den Nachteil hat (auch hier bei kleineren Listen offensichtlich).

Quelle
Translate

Ich denke, dies ist eine viel bessere Methode (in VB sollte es nicht schwer sein, sie zu übersetzen):

Gegeben ein XElement x:

Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml
Quelle
Vin
Translate

Wie wäre es mit dieser "Erweiterungs" -Methode auf XElement? hat für mich gearbeitet!

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

ODER verwenden Sie ein wenig Linq

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

    return innerXml.ToString();
}

Hinweis: Der obige Code muss verwendet werdenelement.Nodes()im Gegensatz zuelement.Elements(). Sehr wichtig, um sich an den Unterschied zwischen den beiden zu erinnern.element.Nodes()gibt dir alles wieXText, XAttributeetc, aberXElementnur ein Element.

Quelle
Translate

Mit allem Dank an diejenigen, die den besten Ansatz entdeckt und bewiesen haben (danke!), Wird er hier in eine Erweiterungsmethode eingepackt:

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

Halten Sie es einfach und effizient:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • Aggregat ist Speicher und Leistung ineffizient, wenn Zeichenfolgen verkettet werden
  • Die Verwendung von Join ("", etw) verwendet ein doppelt so großes String-Array wie Concat ... und sieht im Code ziemlich seltsam aus.
  • Die Verwendung von + = sieht sehr seltsam aus, ist aber anscheinend nicht viel schlimmer als die Verwendung von '+' - würde wahrscheinlich auf denselben Code optimiert, da das Zuweisungsergebnis nicht verwendet wird und möglicherweise vom Compiler sicher entfernt wird.
  • StringBuilder ist so wichtig - und jeder weiß, dass unnötiger "Zustand" scheiße ist.
Quelle
Translate

Am Ende habe ich Folgendes verwendet:

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

Persönlich schrieb ich am Ende eineInnerXmlErweiterungsmethode mit der Aggregatmethode:

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

Mein Client-Code ist dann genauso knapp wie beim alten System.Xml-Namespace:

var innerXml = myXElement.InnerXml();
Quelle
Translate

@ Greg: Anscheinend haben Sie Ihre Antwort so bearbeitet, dass sie eine völlig andere Antwort ist. Auf die ich mit Ja antworte, könnte ich dies mit System.Xml tun, hatte aber gehofft, meine Füße mit LINQ to XML nass zu machen.

Ich werde meine ursprüngliche Antwort unten hinterlassen, falls sich jemand fragt, warum ich nicht einfach die .Value-Eigenschaft des XElement verwenden kann, um das zu erhalten, was ich brauche:

@Greg: Die Value-Eigenschaft verkettet den gesamten Textinhalt aller untergeordneten Knoten. Wenn das body-Element nur Text enthält, funktioniert es, aber wenn es XHTML enthält, wird der gesamte Text verkettet, aber keines der Tags.

Quelle
Translate

// Die Verwendung von Regex ist möglicherweise schneller, um das Anfangs- und Endelement-Tag einfach zu kürzen

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);
Quelle
Translate

doc.ToString () oder doc.ToString (SaveOptions) erledigt die Arbeit. Sehenhttp://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx

Quelle
Translate

Ist es möglich, die System.Xml-Namespace-Objekte zu verwenden, um den Job hier zu erledigen, anstatt LINQ zu verwenden? Wie Sie bereits erwähnt haben, ist XmlNode.InnerXml genau das, was Sie brauchen.

Quelle
Ralap Lee
Translate

Ich frage mich, ob (beachte, dass ich das b + = losgeworden bin und nur b + habe)

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

könnte etwas weniger effizient sein als

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

Nicht 100% sicher ... aber ein Blick auf Aggregate () und string.Join () in Reflector ... I.ÜberlegenIch habe es als Aggregat gelesen und nur einen Rückgabewert angehängt, sodass Sie im Wesentlichen Folgendes erhalten:

string = string + string

versus string.Join, es gibt dort einige Erwähnungen von FastStringAllocation oder so, was mich dazu bringt, dass die Leute bei Microsoft dort möglicherweise einen zusätzlichen Leistungsschub eingebracht haben. Natürlich nennt mein .ToArray () mein Negieren so, aber ich wollte nur einen weiteren Vorschlag machen.

Quelle
Translate

Wissen Sie? Das Beste, was Sie tun können, ist, zu CDATA zurückzukehren :( Ich suche hier nach Lösungen, aber ich denke, CDATA ist bei weitem die einfachste und billigste, nicht die bequemste, die man mit tho entwickeln kann

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

Erledigt den Job für Sie

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