代码之家  ›  专栏  ›  技术社区  ›  Mike Powell

获取Xelement InnerXML的最佳方法?

  •  137
  • Mike Powell  · 技术社区  · 16 年前

    最好的方法是什么 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 回复  |  直到 6 年前
        1
  •  196
  •   Markus Safar    9 年前

    我想看看这些建议的解决方案中哪一个性能最好,所以我做了一些比较测试。出于兴趣,我还将Linq方法与普通的旧方法进行了比较 命名空间 格雷格建议的方法。变化很有趣,不是我所期望的,最慢的方法是 比最快的慢3倍以上 .

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

    1. CreateReader-实例猎人(0.113秒)
    2. 普通旧系统.xml-greg hurlman(0.134秒)
    3. 字符串串联聚合-Mike Powell(0.324秒)
    4. StringBuilder-VIN(0.333秒)
    5. 字符串。加入数组-特里(0.360秒)
    6. string.concat on array-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 (对于 命名空间 方法) 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();
    

    字符串。在数组上联接:

    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,经常解析),我会 使用丹尼尔 CreateReader 每次的方法 . 如果您只是进行一些查询,那么可能需要使用Mike更简洁的聚合方法。

    如果在具有大量节点(可能100个)的大型元素上使用XML,您可能会开始看到使用XML的好处 StringBuilder 超过聚合方法,但不超过 创作者 . 我不认为 Join Concat 在这些情况下,方法将更加有效,因为将大列表转换为大数组会带来不利影响(在这里,小列表更明显)。

        2
  •  66
  •   Instance Hunter    16 年前

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

    给定Xelement X:

    Dim xReader = x.CreateReader
    xReader.MoveToContent
    xReader.ReadInnerXml
    
        3
  •  17
  •   Markus Safar    9 年前

    在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() . 记住两者的区别是非常重要的。 元素() 给你一切 XText , XAttribute 等,但 XElement 只有一个元素。

        4
  •  12
  •   Todd Menier    12 年前

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

    public static string InnerXml(this XNode node) {
        using (var reader = node.CreateReader()) {
            reader.MoveToContent();
            return reader.ReadInnerXml();
        }
    }
    
        5
  •  9
  •   Marcin Kosieradzki    15 年前

    保持简单高效:

    String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
    
    • 聚合在连接字符串时内存和性能低下
    • 使用join(“”,sth)是使用比concat大两倍的字符串数组…代码上看起来很奇怪。
    • 使用+=看起来很奇怪,但显然并不比使用“+”差多少-可能会优化到相同的代码,因为基本分配结果是未使用的,并且可能被编译器安全地删除。
    • StringBuilder是如此的必要——每个人都知道不必要的“状态”很糟糕。
        6
  •  7
  •   Jeff Atwood    16 年前

    最后我用了这个:

    Body = t.Element("body").Nodes().Aggregate("", (b, node) => b += node.ToString());
    
        7
  •  3
  •   Martin R-L    15 年前

    就我个人而言,我最后写了一篇 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();
    
        8
  •  2
  •   Mike Powell    16 年前

    @格雷格:看来你把答案编辑成了一个完全不同的答案。我的答案是肯定的,我可以使用system.xml来实现这一点,但我希望用linq-to-xml让我的脚湿透。

    如果有人想知道为什么我不能使用Xelement的.Value属性来获取我需要的内容,我将在下面留下我的原始答复:

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

        9
  •  1
  •   user950851    11 年前

    //使用regex可能更快,只需修剪begin和end元素标记

    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);
    
        10
  •  1
  •   user1920925    10 年前

    doc.toString()或doc.toString(saveoptions)可以完成这项工作。 见 http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx

        11
  •  0
  •   Greg Hurlman    16 年前

    是否可以使用System.xml命名空间对象在此处完成作业,而不是使用Linq?正如您已经提到的,xmlnode.innerxml正是您所需要的。

        12
  •  0
  •   Terry    16 年前

    想知道是否(注意我去掉了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()…i 认为 我把它读作聚合,只是附加一个返回值,所以本质上你可以得到:

    字符串=字符串+字符串

    与string.join相比,这里提到了fastStringAllocation或其他一些东西,这让我觉得微软的人可能已经在其中增加了一些额外的性能提升。当然,我的.toarray()调用我的negate that,但我只是想提供另一个建议。

        13
  •  0
  •   Ayyash    15 年前

    你知道的?最好的做法是回到CDATA:(我在这里研究解决方案,但我认为,迄今为止,CDATA是最简单、最便宜的,不是最方便使用tho开发的。

        14
  •  0
  •   Vinod Srivastav    6 年前
    var innerXmlAsText= XElement.Parse(xmlContent)
                        .Descendants()
                        .Where(n => n.Name.LocalName == "template")
                        .Elements()
                        .Single()
                        .ToString();
    

    会帮你做的

        15
  •  -2
  •   Shivraj    14 年前
    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();
    }