.net 获取 XElement 的 InnerXml 的最佳方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3793/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 09:36:38  来源:igfitidea点击:

Best way to get InnerXml of an XElement?

.netxmlxelementinnerxml

提问by Mike Powell

What's the best way to get the contents of the mixed bodyelement in the code below? The element might contain either XHTML or text, but I just want its contents in string form. The XmlElementtype has the InnerXmlproperty which is exactly what I'm after.

body在下面的代码中获取混合元素内容的最佳方法是什么?该元素可能包含 XHTML 或文本,但我只想要字符串形式的内容。该XmlElement类型具有InnerXml正是我所追求的属性。

The code as written almostdoes what I want, but includes the surrounding <body>...</body>element, which I don't want.

编写的代码几乎可以满足我的要求,但包括我不想要的周围<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()
                };

回答by Luke Sampson

I wanted to see which of these suggested solutions performed best, so I ran some comparative tests. Out of interest, I also compared the LINQ methods to the plain old System.Xmlmethod suggested by Greg. The variation was interesting and not what I expected, with the slowest methods being more than 3 times slower than the fastest.

我想看看这些建议的解决方案中哪一个表现最好,所以我进行了一些比较测试。出于兴趣,我还将 LINQ 方法与Greg 建议的普通旧System.Xml方法进行了比较。这种变化很有趣,并不是我所期望的,最慢的方法比最快的方法慢3 倍以上

The results ordered by fastest to slowest:

结果按最快到最慢排序:

  1. CreateReader - Instance Hunter (0.113 seconds)
  2. Plain old System.Xml - Greg Hurlman (0.134 seconds)
  3. Aggregate with string concatenation - Mike Powell (0.324 seconds)
  4. StringBuilder - Vin (0.333 seconds)
  5. String.Join on array - Terry (0.360 seconds)
  6. String.Concat on array - Marcin Kosieradzki (0.364)
  1. CreateReader - Instance Hunter(0.113 秒)
  2. 普通的旧 System.Xml - Greg Hurlman(0.134 秒)
  3. 使用字符串串联聚合 - Mike Powell(0.324 秒)
  4. StringBuilder - Vin(0.333 秒)
  5. String.Join on array - Terry(0.360 秒)
  6. 数组上的 String.Concat - Marcin Kosieradzki (0.364)


Method

方法

I used a single XML document with 20 identical nodes (called 'hint'):

我使用了一个带有 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>

The numbers shown as seconds above are the result of extracting the "inner XML" of the 20 nodes, 1000 times in a row, and taking the average (mean) of 5 runs. I didn't include the time it took to load and parse the XML into an XmlDocument(for the System.Xmlmethod) or XDocument(for all the others).

上面显示的秒数是提取 20 个节点的“内部 XML”,连续 1000 次,并取 5 次运行的平均值(平均值)的结果。我没有包括加载 XML 并将其解析为XmlDocument(对于System.Xml方法)或XDocument(对于所有其他方法)所花费的时间。

The LINQ algorithms I used were: (C# - all take an XElement"parent" and return the inner XML string)

我使用的 LINQ 算法是:(C# - 都采用XElement“父”并返回内部 XML 字符串)

CreateReader:

创建阅读器:

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

return reader.ReadInnerXml();

Aggregate with string concatenation:

用字符串连接聚合:

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:

String.Join 数组:

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

String.Concat on array:

数组上的 String.Concat:

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

I haven't shown the "Plain old System.Xml" algorithm here as it's just calling .InnerXml on nodes.

我没有在这里展示“Plain old System.Xml”算法,因为它只是在节点上调用 .InnerXml 。



Conclusion

结论

If performance is important (e.g. lots of XML, parsed frequently), I'd use Daniel's CreateReadermethod every time. If you're just doing a few queries, you might want to use Mike's more concise Aggregate method.

如果性能很重要(例如大量 XML,经常解析),我每次都会使用 Daniel 的CreateReader方法。如果您只是进行一些查询,您可能希望使用 Mike 更简洁的 Aggregate 方法。

If you're using XML on large elements with lots of nodes (maybe 100's), you'd probably start to see the benefit of using StringBuilderover the Aggregate method, but not over CreateReader. I don't think the Joinand Concatmethods would ever be more efficient in these conditions because of the penalty of converting a large list to a large array (even obvious here with smaller lists).

如果您在具有大量节点(可能有 100 个)的大型元素上使用 XML,您可能会开始看到使用StringBuilderAggregate 方法的好处,但不会超过CreateReader. 我不认为JoinConcat方法在这些条件下会更有效,因为将大列表转换为大数组会带来损失(即使在这里使用较小的列表也很明显)。

回答by Instance Hunter

I think this is a much better method (in VB, shouldn't be hard to translate):

我认为这是一个更好的方法(在 VB 中,应该不难翻译):

Given an XElement x:

给定一个 XElement x:

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

回答by Vin

How about using this "extension" method on XElement? worked for me !

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

OR use a little bit of Linq

或者使用一点 Linq

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

    return innerXml.ToString();
}

Note: The code above has to use element.Nodes()as opposed to element.Elements(). Very important thing to remember the difference between the two. element.Nodes()gives you everything like XText, XAttributeetc, but XElementonly an Element.

注意:上面的代码必须使用element.Nodes()而不是element.Elements(). 记住两者之间的区别非常重要。element.Nodes()让你喜欢的一切XTextXAttribute等等,但XElement只有一个元素。

回答by Todd Menier

With all due credit to those who discovered and proved the best approach (thanks!), here it is wrapped up in an extension method:

感谢那些发现并证明了最佳方法的人(谢谢!),这里将它封装在一个扩展方法中:

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

回答by Marcin Kosieradzki

Keep it simple and efficient:

保持简单和高效:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • Aggregate is memory and performance inefficient when concatenating strings
  • Using Join("", sth) is using two times bigger string array than Concat... And looks quite strange in code.
  • Using += looks very odd, but apparently is not much worse than using '+' - probably would be optimized to the same code, becase assignment result is unused and might be safely removed by compiler.
  • StringBuilder is so imperative - and everybody knows that unnecessary "state" sucks.
  • 连接字符串时,聚合是内存和性能低效的
  • 使用 Join("", sth) 是使用比 Concat 大两倍的字符串数组......而且在代码中看起来很奇怪。
  • 使用 += 看起来很奇怪,但显然并不比使用 '+' 差多少 - 可能会优化为相同的代码,因为分配结果未使用并且可能被编译器安全删除。
  • StringBuilder 非常重要 - 每个人都知道不必要的“状态”很糟糕。

回答by Mike Powell

I ended up using this:

我最终使用了这个:

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

回答by Martin R-L

Personally, I ended up writing an InnerXmlextension method using the Aggregate method:

就个人而言,我最终InnerXml使用 Aggregate 方法编写了一个扩展方法:

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

My client code is then just as terse as it would be with the old System.Xml namespace:

我的客户端代码就像使用旧的 System.Xml 命名空间一样简洁:

var innerXml = myXElement.InnerXml();

回答by Mike Powell

@Greg: It appears you've edited your answer to be a completely different answer. To which my answer is yes, I could do this using System.Xml but was hoping to get my feet wet with LINQ to XML.

@Greg:看来您已将答案编辑为完全不同的答案。我的回答是肯定的,我可以使用 System.Xml 来做到这一点,但我希望能用 LINQ to XML 弄湿我的脚。

I'll leave my original reply below in case anyone else wonders why I can't just use the XElement's .Value property to get what I need:

我会在下面留下我的原始回复,以防其他人想知道为什么我不能只使用 XElement 的 .Value 属性来获得我需要的东西:

@Greg: The Value property concatenates all the text contents of any child nodes. So if the body element contains only text it works, but if it contains XHTML I get all the text concatenated together but none of the tags.

@Greg:Value 属性连接任何子节点的所有文本内容。因此,如果 body 元素仅包含文本,则它可以工作,但如果它包含 XHTML,我会将所有文本连接在一起,但没有任何标签。

回答by user950851

// using Regex might be faster to simply trim the begin and end element tag

// 使用 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);

回答by user1920925

doc.ToString() or doc.ToString(SaveOptions) does the work. See http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx

doc.ToString() 或 doc.ToString(SaveOptions) 完成这项工作。请参阅http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx