获得XElement的InnerXml的最佳方法是什么?

时间:2008-08-06 18:16:56

标签: .net xml 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()
                };

15 个答案:

答案 0 :(得分:200)

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

按最快到最慢排序的结果:

  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)

  7. 方式

    我使用了一个包含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“parent”并返回内部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());
    

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


    <强>结论

    如果性能很重要(例如大量XML,经常解析),我每次都使用Daniel的CreateReader方法。如果你只是做了一些查询,你可能想要使用迈克更简洁的聚合方法。

    如果您在具有大量节点(可能是100个)的大型元素上使用XML,您可能会开始看到使用StringBuilder优于聚合方法的好处,但不会超过CreateReader 。我不认为JoinConcat方法在这些条件下会更有效率,因为将大型列表转换为大型数组会受到惩罚(在这里使用较小的列表时甚至很明显)。

答案 1 :(得分:67)

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

给出XElement x:

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

答案 2 :(得分:18)

在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只有一个元素。

答案 3 :(得分:13)

对于那些发现并证明是最佳方法的人(谢谢!),我们将其归结为一种扩展方法:

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

答案 4 :(得分:10)

保持简单高效:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • 在连接字符串时,聚合是内存和性能效率低下
  • 使用Join(“”,sth)使用比Concat大两倍的字符串数组......在代码中看起来很奇怪。
  • 使用+ =看起来很奇怪,但显然不比使用'+'差 - 可能会针对相同的代码进行优化,因为分配结果未使用,可能会被编译器安全删除。
  • StringBuilder是如此迫切 - 每个人都知道不必要的“状态”很糟糕。

答案 5 :(得分:7)

我最终使用了这个:

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

答案 6 :(得分:3)

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

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

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

var innerXml = myXElement.InnerXml();

答案 7 :(得分:2)

@Greg:看来你编辑的答案是一个完全不同的答案。我的答案是肯定的,我可以使用System.Xml来做到这一点,但希望能够使用LINQ to XML。

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

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

答案 8 :(得分: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);

答案 9 :(得分:1)

doc.ToString()或doc.ToString(SaveOptions)可以正常工作。 见http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx

答案 10 :(得分:0)

是否可以使用System.Xml命名空间对象来完成此处的工作而不是使用LINQ?正如您已经提到的,XmlNode.InnerXml正是您所需要的。

答案 11 :(得分:0)

想知道是否(通知我摆脱了b + =并且只有b +)

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

的效率可能略低于

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

不是100%肯定......但是在Reflector中看了一下Aggregate()和string.Join()...我想想我把它读作Aggregate只是追加一个返回值,所以基本上你得到:

string = string + string

与string.Join相比,它在FastStringAllocation中有一些提及,这让我觉得微软的人可能会在那里增加一些额外的性能。当然我的.ToArray()称我的否定,但我只想提出另一个建议。

答案 12 :(得分:0)

你知道吗?最好的办法是回到CDATA :(我在这里看解决方案,但我认为CDATA是迄今为止最简单,最便宜的,不是最方便的开发

答案 13 :(得分:0)

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

将为您完成工作

答案 14 :(得分:-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();
}