150

body以下のコードで混合要素の内容を取得するための最良の方法は何ですか?要素にはXHTMLまたはテキストが含まれている可能性がありますが、その内容は文字列形式である必要があります。XmlElementタイプには、まさに私が求めているものであるプロパティInnerXmlがあります。

書かれたコードはほとんど私が望むことをしますが、私が望まない周囲の<body>...</body>要素を含みます。

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

15 に答える 15

213

これらの提案されたソリューションのどれが最も効果的かを見たかったので、いくつかの比較テストを実行しました。興味深いことに、私はLINQメソッドをGregによって提案された単純な古いSystem.Xmlメソッドとも比較しました。バリエーションは面白く、私が期待したものではありませんでした。最も遅いメソッドは最も速いメソッドより3倍以上遅くなりました

結果は、最も速いものから最も遅いものの順に並べられています。

  1. CreateReader-インスタンスハンター(0.113秒)
  2. プレーンな古いSystem.Xml-GregHurlman(0.134秒)
  3. 文字列連結を使用した集計-MikePowell(0.324秒)
  4. StringBuilder-Vin(0.333秒)
  5. String.Join on array-Terry(0.360秒)
  6. 配列上のString.Concat-MarcinKosieradzki(0.364)

方法

20個の同一ノード(「ヒント」と呼ばれる)を持つ単一のXMLドキュメントを使用しました。

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

上記の秒数は、20ノードの「内部XML」を1000回連続で抽出し、5回の実行の平均(平均)をとった結果です。XmlDocumentXMLを(System.Xmlメソッドの場合)またはXDocument(他のすべての場合)にロードして解析するのにかかる時間を含めませんでした。

私が使用したLINQアルゴリズムは次のとおりです。(C#-すべてXElement「親」を取り、内部XML文字列を返します)

CreateReader:

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

return reader.ReadInnerXml();

文字列連結を使用して集計:

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:

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

ここでは、ノードで.InnerXmlを呼び出しているだけなので、「PlainoldSystem.Xml」アルゴリズムは示していません。


結論

パフォーマンスが重要な場合(たとえば、大量のXML、頻繁に解析される場合)、毎回ダニエルの方法を使用しますCreateReader。いくつかのクエリを実行しているだけの場合は、Mikeのより簡潔なAggregateメソッドを使用することをお勧めします。

StringBuilder多数のノード(おそらく100)を含む大きな要素でXMLを使用している場合は、Aggregateメソッドを使用することの利点を理解し始めるでしょうが、それ以上ではありませんCreateReader。大きなリストを大きな配列に変換するというペナルティがあるため、これらの条件でJoinandメソッドがより効率的になるとは思いません(ここでは小さなリストでも明らかです)。Concat

于 2009-11-09T23:08:14.530 に答える
71

これははるかに優れた方法だと思います(VBでは、翻訳するのは難しくありません):

XElement x が与えられた場合:

Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml
于 2009-03-18T17:17:25.360 に答える
20

XElementでこの「拡張」メソッドを使用するのはどうですか? 私のために働いた!

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

またはLinqを少し使用する

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

    return innerXml.ToString();
}

: 上記のコードはelement.Nodes()element.Elements(). 両者の違いを覚えておくことは非常に重要です。などのすべてを提供しますが、element.Nodes()要素のみを提供します。XTextXAttributeXElement

于 2008-08-19T20:29:41.767 に答える
17

最良のアプローチを発見し、証明した人々の功績により (感謝します!)、ここでは拡張メソッドにまとめます。

public static string InnerXml(this XNode node) {
    using (var reader = node.CreateReader()) {
        reader.MoveToContent();
        return reader.ReadInnerXml();
    }
}
于 2013-01-04T20:21:41.603 に答える
10

シンプルで効率的なものにしてください。

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • 集計は、文字列を連結するときにメモリとパフォーマンスが非効率的です
  • Join("", sth) を使用すると、Concat よりも 2 倍大きな文字列配列が使用されます...そして、コードでは非常に奇妙に見えます。
  • += の使用は非常に奇妙に見えますが、明らかに '+' を使用するよりもそれほど悪くはありません。割り当て結果が使用されず、コンパイラによって安全に削除される可能性があるため、おそらく同じコードに最適化されるでしょう。
  • StringBuilder は非常に重要であり、不必要な「状態」が最悪であることは誰もが知っています。
于 2009-10-31T17:22:59.363 に答える
7

私はこれを使用することになりました:

Body = t.Element("body").Nodes().Aggregate("", (b, node) => b += node.ToString());
于 2008-08-06T19:36:02.667 に答える
3

個人的にはInnerXml、Aggregate メソッドを使用して拡張メソッドを作成することになりました。

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

私のクライアント コードは、古い System.Xml 名前空間の場合と同じくらい簡潔になります。

var innerXml = myXElement.InnerXml();
于 2010-03-17T08:47:45.507 に答える
2

@Greg:回答を編集して、まったく別の回答にしたようです。私の答えは「はい」です。System.Xml を使用してこれを行うことができましたが、LINQ to XML に慣れることを望んでいました。

XElement の .Value プロパティを使用して必要なものを取得できない理由を他の誰かが疑問に思う場合に備えて、元の返信を以下に残します。

@Greg: Value プロパティは、子ノードのすべてのテキスト コンテンツを連結します。body 要素にテキストのみが含まれている場合は機能しますが、XHTML が含まれている場合は、すべてのテキストが連結されますが、タグは含まれません。

于 2008-08-06T18:25:08.877 に答える
1

// Regex を使用すると、開始要素タグと終了要素タグを単純にトリムする方が高速になる場合があります

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);
于 2014-02-08T04:55:35.983 に答える
1

doc.ToString() または doc.ToString(SaveOptions) が機能します。http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspxを参照してください。

于 2014-10-13T20:08:32.527 に答える
0

LINQ を使用する代わりに、System.Xml 名前空間オブジェクトを使用してここで作業を行うことはできますか? すでに述べたように、XmlNode.InnerXml はまさに必要なものです。

于 2008-08-06T18:20:18.903 に答える
0

疑問に思っています(b + =を取り除き、b +だけがあることに注意してください)

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

よりもわずかに効率が悪い可能性があります。

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

100%確実ではありません...しかし、リフレクターのAggregate()とstring.Join()をちらりと見ると...戻り値を追加するだけのAggregateとして読んだと思うので、基本的には次のようになります:

文字列 = 文字列 + 文字列

string.Join に対して、そこには FastStringAllocation などについての言及があり、Microsoft の人々がそこにパフォーマンスをさらに向上させたのではないかと思います。もちろん、私の .ToArray() はそれを否定しますが、別の提案をしたかっただけです。

于 2009-03-18T16:57:12.803 に答える
0

ほら?最善の方法は、CDATAに戻ることです:(私はここでソリューションを見ていますが、CDATAは最もシンプルで安価であり、開発するのに最も便利ではないと思います

于 2009-10-25T23:39:21.693 に答える
-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();
}
于 2010-08-13T07:05:02.603 に答える