Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this the most efficient way to express this XDocument query?

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;
like image 436
Kev Avatar asked Nov 06 '22 22:11

Kev


1 Answers

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.

like image 70
Dimitre Novatchev Avatar answered Nov 12 '22 15:11

Dimitre Novatchev