views:

364

answers:

2

We utilise a third party web service that returns XML which looks something like (cut down for brevity):

<Response>
  <block name="availability">
    <block name="cqual">
      <a name="result-code" format="text">L</a>
    </block>
    <block name="exchange">
      <a name="code" format="text">MRDEN</a>
    </block>
    <block name="mqual">
      <a name="rate-adaptive" format="text">G</a>
    </block>
  </block>
  <block name="products">
    <block>
      <a name="product-id" format="counting">1235</a>
      <block name="realms">
        <block>
          <a name="realm" format="text">-u@surfuk1</a>
        </block>
      </block>
    </block>
    <block>
      <a name="product-id" format="counting">1236</a>
      <block name="realms">
        <block>
          <a name="realm" format="text">-u@surfuk2</a>
        </block>
      </block>
    </block>
    <block>
      <a name="product-id" format="counting">1237</a>
      <block name="realms">
        <block>
          <a name="realm" format="text">-u@surfuk3</a>
        </block>
      </block>
    </block>
  </block>
  <status no="0" />
</Response>

For a specific product code I need to obtain the realm name i.e. the inner text of:

<a name="realm" format="text">-u@surfuk2</a>

Because every element name is either <block> or <a> it's a bit troublesome to parse with linq to xml or query expressions.

Is the following the most effective/efficient/expressive way to get at the realm name for a specific product, e.g. 1235:

List<XElement> products = response
    .Element("Response")
    .Elements("block")
    .Where(x => x.Attribute("name").Value == "products")
    .Elements("block").ToList();
//
// I broke down the query to aid readability
//
string realm = products.Elements("a")
    .Where(x => x.Attribute("name").Value == "product-id")
    .Where(y => y.Value == "1235") // hardcoded for example use
    .Ancestors()
    .First()
    .Elements("block")
    .Where(z => z.Attribute("name").Value == "realms")
    .Elements("block")
    .Elements("a")
    .First().Value;

Thanks
Kev

A: 

It seems so, but why do you need all of the ToList() calls? I see three of these calls and I don't think they're needed so they simply slow down your code. But then again, I've not used much Linq-to-XMl.

configurator
Eh...there were leftovers from a debugging session. Sorted now.
Kev
+1  A: 

The definition of realm as provided is the equivalent of the simpler:

string realm = (string) products.XPathEvaluate(
   "string(
      /*/blocks[@name='products']
                 /*/a[@name='product-id' and . = '1236']
                              /following-sibling::block[1]
          )
   "
                                     )

This in fact is both more readable and more compact than the definition of realm provided in the original question.

As efficiency is concerned, it could well be that evaluating a single XPath expression may turn out to be more efficient, too, however to find if this is true we need to write an application that will compare the timings of the two methods.

Dimitre Novatchev
Thanks for the response. I know I could be far more concise with XPath, but I fancied stretching my LINQ legs today on that one :-)
Kev