开发者

dealing with Linq XML and complex queries

this is quite a brainteaser to me:

Assume i have XML data similar to the following:

<?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&开发者_StackOverflowlt;/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>

you can download a complete and much bigger (about 5MB) test-file from www.megamatz.de/auction.xml

I need to determine the IDs of all open_auctions where a certain given person (Person1) issued a bid before another given person (Person2). "Before" in the meaning of the the "bidder"node of Person1 occures before the "bidder"node of Person2 within one open_auction, so its not concerning date or time.

Here is the XPath-Query for this problem maybe it explains the problem better:

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>

I know somehow that i need some kind of nested queries, but I can´t get it straight with this Linq-concept and these XElement(s). So I am not only interested in a solution but also understanding the concept espacially of any(), contains() and what else there is to cope with queries like these with linq.

I am really desperate and need some help, I have been searching an trying the whole night long, but I simply can´t get it straight and nothing works.

Edited to add

I am sorry to ask again, but no, it doesn´t work. I have tried and tried, which was very good, because I have learned a lot of things. I have the feeling I am nearly finding the answer, but when it comes to the "ElementAfterSelf" there is something going on where I could use a little hint and explanaition in the context of this complex query.

Here is my approach, which I have figured out on the basis of what I learned from Jon Skeets answer:

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();  

But it doesn´t work either. But...:

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();

... at least brought up the 3 "open_auction"s where "person540" is a bidder. To be specific in my testfile it is id="open_auction11" where "person1068" bidds "after" "person540".

I am asking from the bottom of my heart, can somebody please give it a try with the Testfile. I uploaded it to www.megamatz.de/auction.xml . Inside the file the path to the open auctions is: Element("site").Element("open_auctions").Elements("open_auction")


EDIT: Okay, edited again. I think this is okay, but even with your sample file it would be nice if you could give us a concrete example of inputs and expected outputs:

// 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"))
               );

The second extension method can also be written as:

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

It changes the timing slightly, but it shouldn't make any difference in this particular case...


Thx a lot Jon! Your code helped me a lot to learn this concept of Linq to XML Ok, I have made a query without any helper methods, which works fine. I have formated a bit unusal but it might help other noops like me who take a look here to understand what is going on:

 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();

When taking actions on Collections, it is important, that there is something inside, so it is important to check if Collection.Count() != 0. After that it is possible to do the query.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜