针对兄弟键值对的强大LINQ to XML查询

时间:2010-04-07 16:28:53

标签: c# linq-to-xml robustness

(首先,请温柔!)

我正在学习LINQ to XML的所有荣耀和脆弱,试图破解它做我想做的事情:

给定像这样的XML文件 -

<list>
<!-- random data, keys, values, etc.-->

  <key>FIRST_WANTED_KEY</key>
  <value>FIRST_WANTED_VALUE</value>

  <key>SECOND_WANTED_KEY</key>
  <value>SECOND_WANTED_VALUE</value> <!-- wanted because it's first -->

  <key>SECOND_WANTED_KEY</key>
  <value>UNWANTED_VALUE</value>  <!-- not wanted because it's second -->

  <!-- nonexistent <key>THIRD_WANTED_KEY</key> -->
  <!-- nonexistent <value>THIRD_WANTED_VALUE</value> -->

<!-- more stuff-->
</list>

我想以健壮的方式提取一组已知的“有用密钥”的值,即如果SECOND_WANTED_KEY出现两次,我只需要SECOND_WANTED_VALUE,而不是UNWANTED_VALUE。此外,THIRD_WANTED_KEY可能会出现也可能不出现,因此查询也应该能够处理。我可以假设FIRST_WANTED_KEY将出现在其他键之前,但不能假设其他键的顺序 - 如果键出现两次,其值不重要,我只想要第一个。一个由字符串组成的匿名数据类型就可以了。

我的尝试集中在这些方面:

var z = from y in x.Descendants()
        where y.Value == "FIRST_WANTED_KEY"
        select new
        {
          first_wanted_value = ((XElement)y.NextNode).Value,
         //...
        }

我的问题是...应该是什么?我试过,例如,(丑陋,我知道)

second_wanted_value = ((XElement)y.ElementsAfterSelf()
                      .Where(w => w.Value=="SECOND_WANTED_KEY")
                      .FirstOrDefault().NextNode).Value

应该有希望允许密钥在任何地方或不存在,但是没有成功,因为.NextNode上的XElement似乎不起作用。

我也尝试添加

.Select(t => { 
    if (t==null) 
        return new XElement("SECOND_WANTED_KEY",""); 
    else return t;
})

子句在where之后,但是也没有用。

我愿意接受建议,(建设性的)批评,链接,参考或谷歌的短语建议等等。我已经公平分享了谷歌搜索和检查SO,所以任何帮助将不胜感激。

谢谢!

修改 让我为此添加一层复杂性 - 我应该首先将其包括在内。假设XML文档如下所示:

<lists>
    <list>
      <!-- as above -->
    </list>
    <list>
      <!-- as above -->
    </list>
</lists>

我希望提取多组这些键值对。问题/警告:如果SECOND_WANTED_KEY没有出现在第一个<list>元素中但出现在第二个元素中,我不想意外地选择第二个列表元素的SECOND_WANTED_KEY

编辑#2:

另一个想法是,我尝试创建一个HashSet我想要的密钥并执行此操作:

HashSet<string> wantedKeys = new HashSet<string>();
wantedKeys.Add("FIRST_WANTED_KEY");
//...add more keys here
var kvp = from a in x.Descendants().Where(a => wantedKeys.Contains(a.Value))
          select new KeyValuePair<string,string>(a.value,
             ((XElement)a.NextNode).Value);

这让我获得了所有的键值对,但我不确定它是否能保证我将这些对与其父元素“正确”关联起来。这两种方法之间的任何想法或比较都会有所帮助。

状态更新4/9/10

截至目前,我仍然主要认为散列集方法是最受欢迎的。似乎.NET完成的大多数XML处理都是按文档顺序完成的 - 到目前为止,我的所有测试用例都已经完成了。

我会提供赏金和/或upvote答案,但没有足够的代表点。今天我会给你答案,所以请他们进去!感谢。

2 个答案:

答案 0 :(得分:0)

这会获得包含<value>的第一个<key>元素后的第一个"SECOND_WANTED_KEY"元素的值:

XDocument doc;

string result = (string)doc.Root
                           .Elements("key")
                           .First(node => (string)node == "SECOND_WANTED_KEY")
                           .ElementsAfterSelf("value")
                           .First();

根据需要添加空检查。

答案 1 :(得分:0)

XDocument doc = ...

var wantedKeyValuePairs =
    from keyElement in doc.Root.Elements("key")
    let valueElement = keyElement.ElementsAfterSelf("value").First()
    select new { Key = keyElement.Value, Value = valueElement.Value } into kvp
    group kvp by kvp.Key into g
    select g.First();

说明:此查询获取每个<key>元素及其后续<value>元素,并使用这些元素创建键值对。然后,它按键对键值对进行分组,并仅为每个键

采用第一个键值对