2
\$\begingroup\$

I have an XML stream that looks like the following (attribute and node quantity , and node depth reduced for demonstration purposes.)

<itinerary>
  <organizer>
    <company name="ACME" billingLocation="London" adminOffice="Manchester">
  </organizer>
  <traveller name="John Smith" homeCity="London" />
  <flight date="20160317" code="BA101" origin="London" destination="New York" />
  <vehicleHire start="20160317" end="20160317">
    <depot type="pickup" location="London" />
    <depot type="return" location="Heathrow" />
  </vehicleHire>
  <hotel location="New York" checkin="20160317" checkout="20160318" />
  <journey>
    <train date="20160318">
      <origin station="New York" />
      <destination station="Boston" />
    </train>
</itinerary>

I'd like to get a distinct list of attribute values where the attribute name is in (billingLocation,adminOffice,homeCity,origin,destination,location,station), but my final/actual list of attribute names will number around 30.

I have this C# code working using XmlDocument and an XPath query, but I'm unsure if this is the most efficient method.

XmlDocument xd = new XmlDocument();
xd.Load(customProvider.GetStream());
XmlNodeList nl = xd.SelectNodes(@"//@billingLocation|//@adminOffice|//@homeCity|//@origin|//@destination|//@location|//@station");
var lst = nl.Cast<XmlAttribute>().Select(a => a.Value).Distinct().ToList();

The final XML will be more complex and have a deeper hierarchy, and the attributes might occur across different nodes.

Is XmlDocument and XPath the most efficient aproach, or would Linq to XML perhaps be faster, and what would that look like?

\$\endgroup\$
  • \$\begingroup\$ I don't understand, why would you want to combine cities and languages like this? Also, do you really not know anything about the elements that you want to search? \$\endgroup\$ – svick Mar 14 '16 at 16:38
  • \$\begingroup\$ XML updated to be more realistic. Query now just seeks a distinct list of cities/locations \$\endgroup\$ – ThunderFrame Mar 16 '16 at 22:56
2
\$\begingroup\$

As long as you don't face noticable performance issues I would chose whatever method seems to be better for you. The XPath works so you may stick to it.

I would however build the path in a more generic way by specifying an array of attribute names first:

var attributeNames = new[]
{
    "billingLocation",
    "adminOffice",
    "homeCity",
    "origin",
    "destination",
    "location",
    "station"
}

and the combining them into a query:

var xPath = string.Join("|", attributeNames.Select(n => $"//@{n}"));
...xd.SelectNodes(xPath);

In this cas however it is a little bit easier to do the same with linq:

Parse the xml first (which is btw invalid in your example - some elements are not closed):

var xElement = XElement.Parse(text);

Then use the same array for the linq query:

var cities =
    (from attr in xElement.Descendants().Attributes()
     where attributeNames.Contains(attr.Name.LocalName)
     select attr.Value).Distinct();
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.