使用LINQ将XML标记的集合连接到字符串

时间:2009-03-31 15:20:51

标签: c# linq linq-to-xml

我无法使用我无法控制的Web服务,并且正在尝试将该服务返回的XML解析为标准对象。

XML结构的一部分看起来像这样

<NO>
   <L>Some text here </L>
   <L>Some additional text here </L>
   <L>Still more text here </L>
</NO>

最后,我希望最终得到一个String属性,它看起来像“这里有些文字。这里还有一些文字还有更多文字”

我对初始通行证的内容如下。我想我已经走上了正轨,但还没有完成:

XElement source = \\Output from the Webservice
List<IndexEntry> result;

result = (from indexentry in source.Elements(entryLevel)
    select new IndexEntry()
    {
        EtiologyCode = indexentry.Element("IE") == null ? null : indexentry.Element("IE").Value,
        //some code to set other properties in this object
        Note = (from l in indexentry.Elements("NO").Descendants
                select l.value)  //This is where I stop
                               // and don't know where to go
    }

我知道我可以在该查询的末尾添加一个ToList()运算符来返回集合。是否有一个opertaor或技术允许我将该集合的连接内联到一个字符串?

如果不清楚,请随时询问更多信息。

感谢。

4 个答案:

答案 0 :(得分:7)

LINQ to XML确实就是这样的方式:

var textArray = topElement.Elements("L")
                          .Select(x => x.Value)
                          .ToArray();

var text = string.Join(" ", textArray);

编辑:基于评论,看起来你只需要一种表达方式的单表达方式。这很容易,如果有点难看:

result = (from indexentry in source.Elements(entryLevel)
    select new IndexEntry
    {
        EtiologyCode = indexentry.Element("IE") == null 
                           ? null 
                           : indexentry.Element("IE").Value,
        //some code to set other properties in this object
        Note = string.Join(" ", indexentry.Elements("NO")
                                          .Descendants()
                                          .Select(x => x.Value)
                                          .ToArray())
    };

另一种方法是将其提取为单独的扩展方法(它必须位于顶级静态类中):

public static string ConcatenateTextNodes(
    this IEnumerable<XElement> elements)
{
    string[] values = elements.Select(x => x.Value).ToArray();
    // You could parameterise the delimiter here if you wanted
    return string.Join(" ", values);
}

然后将您的代码更改为:

result = (from indexentry in source.Elements(entryLevel)
    select new IndexEntry
    {
        EtiologyCode = indexentry.Element("IE") == null 
                           ? null 
                           : indexentry.Element("IE").Value,
        //some code to set other properties in this object
        Note = indexentry.Elements("NO")
                         .Descendants()
                         .ConcatenateTextNodes()
    }

编辑:关于效率的说明

其他答案建议在效率名称中使用StringBuilder。在使用它之前,我会检查这是正确的方法。如果您考虑一下,StringBuilderToArray做类似的事情 - 他们创建一个比他们需要的更大的缓冲区,向其添加数据,在必要时调整大小,最后得出结果。希望你不需要经常调整大小。

StringBuilderToArray之间的差异就是缓冲的内容 - 在StringBuilder中,它是您构建到目前为止的字符串的全部内容。使用ToArray只是引用。换句话说,调整用于ToArray的内部缓冲区的大小可能比调整StringBuilder特别是的大小更便宜,如果单个字符串很长的话。

ToArray中执行缓存后,string.Join非常高效:它可以查看所有字符串开头,完全 >分配多少空间,然后连接它,而不必复制实际的字符数据。

这是sharp contrast to a previous answer I've given - 但不幸的是我认为我没有写过基准。

我当然不希望ToArray显着变慢,我认为它使代码更简单 - 不需要使用副作用等,聚合等。

答案 1 :(得分:1)

另一种选择是使用Aggregate()

var q = topelement.Elements("L")
                  .Select(x => x.Value)
                  .Aggregate(new StringBuilder(), 
                             (sb, x) => return sb.Append(x).Append(" "),
                             sb => sb.ToString().Trim());

编辑:Aggregate中的第一个lambda是累加器。这将获取所有值并从中创建一个值。在这种情况下,它正在创建一个包含所需文本的StringBuilder。第二个lambda是结果选择器。这允许您将累积的值转换为您想要的结果。在这种情况下,将StringBuilder更改为String。

答案 2 :(得分:0)

我自己没有这方面的经验,但LINQ to XML可以极大地简化您的代码让我感到震惊。选择一个XML文档,然后遍历它并使用StringBuilder将L元素附加到某个字符串。

答案 3 :(得分:0)

我和下一个人一样喜欢LINQ,但你在这里重新发明轮子。 XmlElement.InnerText属性完全符合要求。

试试这个:

using System.Xml;

class Program
{
    static void Main(string[] args)
    {
        XmlDocument d = new XmlDocument();
        string xml =
            @"<NO>
  <L>Some text here </L>
  <L>Some additional text here </L>
  <L>Still more text here </L>
</NO>";
        d.LoadXml(xml);
        Console.WriteLine(d.DocumentElement.InnerText);
        Console.ReadLine();
    }
}