Frage

Was ist der beste Weg, um den Inhalt der Mischung zu erhalten? body Element im Code unten?Das Element kann entweder XHTML oder Text enthalten, aber ich möchte seinen Inhalt nur in String-Form haben.Der XmlElement Typ hat die InnerXml Immobilie, die genau das ist, was ich suche.

Der Code wie geschrieben fast macht was ich will, bezieht aber die Umgebung mit ein <body>...</body> Element, was 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()
                };
War es hilfreich?

Lösung

Ich wollte herausfinden, welche dieser vorgeschlagenen Lösungen am besten funktioniert, und habe daher einige Vergleichstests durchgeführt.Aus Interesse habe ich auch die LINQ-Methoden mit den einfachen alten verglichen System.Xml Von Greg vorgeschlagene Methode.Die Variation war interessant und nicht das, was ich erwartet hatte, da die Methoden langsamer waren mehr als dreimal langsamer als der Schnellste.

Die Ergebnisse sortiert nach den schnellsten und langsamsten:

  1. CreateReader – Instanzjäger (0,113 Sekunden)
  2. Einfaches altes System.Xml – Greg Hurlman (0,134 Sekunden)
  3. Mit String-Verkettung aggregieren – Mike Powell (0,324 Sekunden)
  4. StringBuilder – Vin (0,333 Sekunden)
  5. String.Join auf 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 (genannt „Hinweis“) verwendet:

<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 der 1000-maligen Extraktion des „inneren XML“ der 20 Knoten hintereinander und der Bildung des Durchschnitts (Mittelwerts) aus 5 Durchläufen.Ich habe die Zeit, die zum Laden und Analysieren des XML in eine benötigt wurde, nicht berücksichtigt XmlDocument (für die System.Xml Methode) bzw XDocument (für alle anderen).

Die von mir verwendeten LINQ-Algorithmen waren: (C# – alle nehmen eine XElement „parent“ und gibt den inneren XML-String zurück)

CreateReader:

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

return reader.ReadInnerXml();

Mit String-Verkettung aggregieren:

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

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

String.Concat im Array:

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

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


Abschluss

Wenn Leistung wichtig ist (z.B.viel XML, häufig analysiert), würde ich Benutze Daniels CreateReader Methode jedes Mal.Wenn Sie nur ein paar Abfragen durchführen, möchten Sie möglicherweise Mikes prägnantere Aggregate-Methode verwenden.

Wenn Sie XML für große Elemente mit vielen Knoten (vielleicht Hunderten) verwenden, werden Sie wahrscheinlich die Vorteile der Verwendung erkennen StringBuilder über die Aggregate-Methode, aber nicht über CreateReader.Ich glaube nicht, dass Join Und Concat Methoden wären unter diesen Bedingungen jemals effizienter, da die Konvertierung einer großen Liste in ein großes Array Nachteile mit sich bringt (hier bei kleineren Listen sogar offensichtlich).

Andere Tipps

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

Gegeben ein XElement x:

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

Wie wäre es mit der Verwendung dieser „Erweiterungs“-Methode auf XElement?hat bei mir funktioniert!

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

Notiz:Der obige Code muss verwendet werden element.Nodes() im Gegensatz zu element.Elements().Es ist sehr wichtig, sich an den Unterschied zwischen den beiden zu erinnern. element.Nodes() gibt dir alles wie XText, XAttribute usw., aber XElement nur ein Element.

Bei allem Dank an diejenigen, die den besten Ansatz entdeckt und bewiesen haben (Danke!), wird er hier in einer Erweiterungsmethode zusammengefasst:

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

Halten Sie es einfach und effizient:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • Aggregate ist beim Verketten von Zeichenfolgen speicher- und leistungsineffizient
  • Bei Verwendung von Join("", sth) wird ein doppelt so großes String-Array verwendet wie bei Concat ...Und sieht im Code ziemlich seltsam aus.
  • Die Verwendung von += sieht sehr seltsam aus, ist aber anscheinend nicht viel schlechter als die Verwendung von „+“ – wahrscheinlich wäre sie für denselben Code optimiert, da das Zuweisungsergebnis nicht verwendet wird und möglicherweise sicher vom Compiler entfernt wird.
  • StringBuilder ist so wichtig – und jeder weiß, dass unnötiger „Zustand“ scheiße ist.

Am Ende habe ich Folgendes verwendet:

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

Persönlich habe ich am Ende eine geschrieben InnerXml Erweiterungsmethode mit der Aggregate-Methode:

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 mit dem alten System.Xml-Namespace:

var innerXml = myXElement.InnerXml();

@Greg:Anscheinend haben Sie Ihre Antwort so bearbeitet, dass sie eine völlig andere Antwort ist.Darauf antworte ich: Ja, ich könnte dies mit System.Xml tun, hatte aber gehofft, mit LINQ to XML meine ersten Erfahrungen zu sammeln.

Ich hinterlasse meine ursprüngliche Antwort unten, falls sich jemand anderes wundert, warum ich nicht einfach die .Value-Eigenschaft des XElements verwenden kann, um das zu bekommen, was ich brauche:

@Greg:Die Value-Eigenschaft verkettet alle Textinhalte aller untergeordneten Knoten.Wenn das Body-Element also nur Text enthält, funktioniert es, wenn es jedoch XHTML enthält, wird der gesamte Text zusammengefügt, aber keines der Tags.

// Die Verwendung von Regex könnte schneller sein, wenn einfach das Anfangs- und Endelement-Tag gekürzt wird

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() oder doc.ToString(SaveOptions) erledigt die Arbeit.Sehen http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx

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

Ich frage mich, ob (beachten Sie, dass ich das b+= entfernt habe und nur noch 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 denken Ich habe es als Aggregat gelesen, bei dem einfach ein Rückgabewert angehängt wird. Im Wesentlichen erhalten Sie also:

string = string + string

im Vergleich zu string.Join wird dort etwas von FastStringAllocation oder so etwas erwähnt, was mich zu dem Schluss bringt, dass die Leute bei Microsoft dort möglicherweise eine zusätzliche Leistungssteigerung vorgenommen haben.Natürlich negiert mein .ToArray()-Aufruf das, aber ich wollte nur einen weiteren Vorschlag machen.

Du weisst?Das Beste, was man tun kann, ist, zu CDATA zurückzukehren :( Ich suche hier nach Lösungen, aber ich denke, dass CDATA bei weitem das einfachste und billigste ist, aber nicht das bequemste, mit dem man es entwickeln kann

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

Erledigt die Arbeit für Sie

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();
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top