处理Linq XML和复杂查询

时间:2011-09-11 08:40:46

标签: c# xml linq

这对我来说是个脑筋急转弯:

假设我有类似以下的XML数据:

<?xml version="1.0"?>
<site>
<open_auction id="open_auction0">
  <initial>25.39</initial>
  <reserve>67.91</reserve>
  <bidder>
    <date>09/27/1999</date>
    <time>18:47:09</time>
    <personref person="person308"/>
    <increase>18.00</increase>
  </bidder>
  <bidder>
    <date>06/03/2000</date>
    <time>17:47:49</time>
    <personref person="person942"/>
    <increase>19.50</increase>
  </bidder>
  <bidder>
    ...
  </bidder>
  <current>202.39</current>
  <itemref item="item2"/>
  <seller person="person573"/>
  <annotation>
    <author person="person128"/>
    <description>
      <parlist>
        <listitem>
          <text>
            niece <keyword> till banqueting little unkindly </keyword> 
          </text>
        </listitem>
        <listitem>
          <text>
            emilia jack standards  
          </text>
        </listitem>
      </parlist>
    </description>
    <happiness>1</happiness>
  </annotation>
  <quantity>1</quantity>
  <type>Regular</type>
  <interval>
    <start>12/23/2001</start>
    <end>11/11/2000</end>
  </interval>
</open_auction>
<open_auction id="open_auction1">
...
</open_auction>
<open_auction id="open_auction2">
...
</open_auction>
</site>

您可以从www.megamatz.de/auction.xml下载完整且更大(约5MB)的测试文件

我需要确定某个特定人(Person1)在另一个人(Person2)之前发出出价的所有open_auctions的ID。 Person1的“bidder”节点意义上的“之前”在一个open_auction中的Person2的“bidder”节点之前发生,因此它不涉及日期或时间。

这个问题的XPath-Query可能会更好地解释问题:

let $auction := doc("auction.xml") return
for $b in $auction/site/open_auctions/open_auction
where
  some $pr1 in $b/bidder/personref[@person = "person20"],
       $pr2 in $b/bidder/personref[@person = "person51"]
  satisfies $pr1 << $pr2
return <history>{$b/reserve/text()}</history>

我知道我需要某种嵌套查询,但我不能直接使用这个Linq概念和这些XElement。因此,我不仅对解决方案感兴趣,而且还理解任何(),包含()的概念,以及用linq处理这些查询的其他内容。

我真的很绝望,需要一些帮助,我一直在寻找整个晚上的尝试,但我根本无法理解,没有任何作用。

已编辑添加

很抱歉再次提问,但不,它不起作用。我尝试过并试过,这非常好,因为我学到了很多东西。我感觉我几乎找到了答案,但是当涉及到“ElementAfterSelf”时,我会在这个复杂查询的上下文中使用一些暗示和解释。

这是我的方法,我根据我从Jon Skeets得到的答案得出的答案:

List<XElement> = doc.Descendants("open_auction")
                 .Where(x => x.Element("bidder") != null && x.Elements("bidder")
                    .Any(b => b.Elements("personref")
                        .First(c => c.IsBidder("person540"))
                           .ElementsAfterSelf("bidder").Any(d => d.IsBidder("person1068"))
                         )).ToList();  

但它也不起作用。但是...:

List<XElement> j = XMarkXML.Element("site").Element("open_auctions").Elements("open_auction")
                 .Where(x => x.Element("bidder") != null && x.Elements("bidder")
                    .Any(b => b.Elements("personref")
                        .Any(c => c.IsBidder("person540")) //I changed First with Any
                     //.ElementsAfterSelf().Any(d => d.IsBidder("person1068")) //and commented the last part out
                         )).ToList();

......至少提出3“open_auction”,其中“person540”是投标人。要在我的测试文件中具体,它是id =“open_auction11”,其中“person1068”在“person540”之后出价。

我是从内心深处问,有人可以尝试一下Testfile。我将其上传到www.megamatz.de/auction.xml。在文件内部,公开拍卖的路径​​是:元素(“站点”)。元素(“open_auctions”)。元素(“open_auction”)

2 个答案:

答案 0 :(得分:3)

编辑:好的,再次编辑。我认为这没关系,但即使你的样本文件,如果你能给我们一个输入和预期输出的具体例子也会很好:

// Extension methods
internal static bool IsBidder(this XElement bidder, string person)
{
    return (string) bidder.Element("personref")
        .Attribute("person") == person;
}

internal static IEnumerable<XElement> ElementsAfterFirst
    (this IEnumerable<XElement> source, XName name)
{
    var first = source.FirstOrDefault();
    if (first != null)
    {
        foreach (var element in first.ElementsAfterSelf(name))
        {
            yield return element;
        }
    }
}

var query = doc
    .Descendants("open_auction")
        .Where(x => x.Elements("bidder")
               // All the person20 bids...
               .Where(b => b.IsBidder("person20"))
               // Now project to bids after the first one...
               .ElementsAfterFirst("bidder")
               // Are there any from person51? If so, include this auction.
               .Any(b => b.IsBidder("person51"))
               );

第二种扩展方法也可以写成:

internal static IEnumerable<XElement> ElementsAfterFirst
    (this IEnumerable<XElement> source, XName name)
{
    var first = source.FirstOrDefault();
    return first == null ? new XElement[0] : first.ElementsAfterSelf(name);
}

它稍微改变了时间,但在这种特殊情况下不应该有任何区别......

答案 1 :(得分:0)

很多Jon!您的代码帮助我学习了Linq to XML的这个概念 好的,我已经做了一个没有任何辅助方法的查询,这很好。我已经形成了一些不寻常的东西,但它可能会帮助像我这样的其他类似的人看看这里发生了什么:

 List<XElement> i = XMarkXML.Element("site").Element("open_auctions").Descendants("open_auction")
        .Where(x => (x.Element("bidder") != null )
                    &&
                    (x.Elements("bidder").Any(b => (b.Element("personref").Attribute("person").Value == "person540")))
                    &&
                    (
                      x.Elements("bidder").Where(
                                                 b1 => (b1.Element("personref").Attribute("person").Value == "person540")
                                                 ).First().ElementsAfterSelf("bidder").Any(
                                                                                            b2 => (b2.Element("personref").Attribute("person").Value == "person1068")
                                                                                            )
                    )
               ).ToList();

在集合上执行操作时,重要的是内部有一些内容,因此检查Collection.Count()!= 0是否很重要。之后可以进行查询。