获得 XElement 的 InnerXml 的最佳方法?

在下面的代码中获取混合 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()
};
87498 次浏览

是否可以使用系统。XML 名称空间对象来完成这里的工作,而不是使用 LINQ?如前所述,XmlNode。InnerXml 正是您所需要的。

@ 格雷格: 看起来你把你的答案编辑成了一个完全不同的答案。我的答案是肯定的,我可以用 System 来做这件事。但是希望能够对 LINQtoXML 有所了解。

我将在下面留下我的原始回复,以防其他人想知道我为什么不能只使用 XElement。为了得到我所需要的东西而对财产进行估值:

@ Greg: Value 属性连接任何子节点的所有文本内容。因此,如果 body 元素只包含文本,那么它就可以工作,但是如果它包含 XHTML,那么我就可以得到所有连接在一起的文本,但是没有标记。

我最后用了这个:

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

在 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()给你一切像 XTextXAttribute等,但 XElement只有一个元素。

想知道是否(注意,我去掉了 b + = ,只有 b +)

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

可能会比

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

不是100% 确定... ... 但是看一下聚合()和字符串。加入()在反射器... 我 好好想想我读它作为聚合只是附加一个返回值,所以本质上你得到:

字符串 = 字符串 + 字符串

对阵绳子。加入,它有一些提到快速分配或东西,这使我认为,在微软的人可能已经把一些额外的性能提高在那里。当然。ToArray ()调用我的假设,但是我想提供另一个建议。

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

给定一个 XElement x:

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

你知道吗?最好的办法是回到 CDATA: (我正在寻找解决方案,但是我认为 CDATA 是目前为止最简单和最便宜的,而不是用 tho 开发最方便的

保持简单有效:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • 当串联字符串时,聚合的内存和性能效率低下
  • 使用 Join (“”,something)是使用比 Concat 大两倍的字符串数组... ... 而且在代码中看起来很奇怪。
  • 使用 + = 看起来很奇怪,但显然并不比使用’+’糟糕多少——可能会针对相同的代码进行优化,因为赋值结果是未使用的,并且可能被编译器安全地删除。
  • StringBuilder 是如此必要——而且每个人都知道不必要的“状态”糟透了。

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

按照最快到最慢顺序排列的结果:

  1. CreateReader-实例捕捉器(0.113秒)
  2. 普通的旧系统. Xml-Greg Hurlman (0.134秒)
  3. 用字符串串联聚合-Mike Powell (0.324秒)
  4. StringBuilder-Vin (0.333秒)
  5. String. Join on array-Terry (0.360秒)
  6. 字符串。在数组上连接-Marcin Kosieradzki (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次。我没有包括将 XML 加载并解析为 XmlDocument(对于 System.Xml方法)或 XDocument(对于所有其他方法)所花费的时间。

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

读者:

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

连接到数组:

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

连接到数组:

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

这里没有显示“普通的旧 System.Xml”算法,因为它只是在节点上调用.InnerXml。


结论

如果性能很重要(比如大量 XML,需要频繁解析) ,我会选择 每次都使用丹尼尔的 CreateReader方法。如果您只是在进行一些查询,那么您可能需要使用 Mike 的更简洁的聚合方法。

如果您在具有大量节点(可能是100个)的大型元素上使用 XML,那么您可能会开始看到使用 StringBuilder优于聚合方法的好处,但是不会优于使用 CreateReader。我不认为 JoinConcat方法在这些情况下会更有效,因为将大型列表转换为大型数组会带来损失(在这里,对于较小的列表甚至更明显)。

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

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:

var innerXml = myXElement.InnerXml();
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();
}

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

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

//使用正则表达式可能更快地简单地修剪开始和结束元素标记

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 ()或 doc.ToString (SaveOptions)完成这项工作。 请参阅 http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring (v = vs. 110) .aspx”rel = “ nofollow”> http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx

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

会替你完成任务