1

I'm stumped (again) with an misunderstanding of XDocument/Linq. For the XML below, I have nameEn and provinceCode as variables in my code. I'm trying to identify the code (e.g., s0000002) and nameFr given I have the other two elements. The provinceCode and NameEn combined are unique in the XML (no duplication).

<siteList>
  <site code="s0000001">
    <nameEn>Edmonton</nameEn>
    <nameFr>Edmonton</nameFr>
    <provinceCode>AB</provinceCode>
  </site>
  <site code="s0000002">
    <nameEn>Algonquin Park</nameEn>
    <nameFr>Parc Algonquin</nameFr>
    <provinceCode>ON</provinceCode>
  </site>
...
</siteList>

Here's the code I'm trying (my XML is in the "loaded" XDocument:

selectedProvince = "ON";
selectedCity = "Algonquin Park";      
strSiteCode = loaded.Descendants("site")
    .Where(x => x.Element("provinceCode").Value == selectedProvince)
    .Where(x => x.Element("nameEn").Value == selectedCity)
    .Select(x => x.Element("code").Value)
    .ToString();
strNameFR = loaded.Descendants("site")
    .Where(x => x.Element("provinceCode").Value == selectedProvince)
    .Where (x => x.Element("nameEn").Value == selectedCity)
    .Select(x => x.Element("nameFr").Value)
    .ToString();

The string strSiteCode returns: System.Linq.Enumerable+WhereSelectEnumerableIterator2[System.Xml.Linq.XElement,System.String]andstrNameFRreturns""`.

I can't figure out what the working code should look like. Thanks for any help.

Doug

svick
  • 236,525
  • 50
  • 385
  • 514
Doug
  • 61
  • 1
  • 7

3 Answers3

1

Try

var result = loaded.Descendants("site")
    .Where(x => (x.Element("provinceCode").Value == selectedProvince) &&
                 (x.Element("nameEn").Value == selectedCity) )
    .Select(x => x.Element("code").Value)
    .SingleOrDefault();

if (result != null)
{
    strSiteCode = result.ToString();
}

The Select() call returns a collection (which, in your case happens to have just one element). So you'll have to call SingleOrDefault() (or Single() ) to get the one item. Also, I removed the second Where() and included the condition to the first Where().

Bala R
  • 107,317
  • 23
  • 199
  • 210
  • Hi, Bala. I couldn't get this to work. I kept getting this error "Operator '&&' cannot be applied to operands of type 'bool' and 'System.Xml.Linq.XElement'" Thanks – Doug Oct 01 '11 at 20:23
  • @Doug Ah! I overlooked something when combining the two `Where()`s. It's fixed now. – Bala R Oct 01 '11 at 20:25
1

Keep in mind that it's possible that the "x.Element(...)" method will return null, whereby accessing "Value" on it will cause a null ref. This is assuming that your xml may not always have the provinceCode or nameEn. If it does, you won't have a problem, but you wouldn't want to put that possible null ref ex in release code, anyways. The following solves the null ref problem.

var site = loaded
    .Descendants("site")
    .FirstOrDefault(x => (string)x.Element("provinceCode") == selectedProvince &&
                    x => (string)x.Element("nameEn") == selectedCity);    

if (site == null)
{
    return
}

var siteCode = (string)site.Attribute("code");
var nameFr = (string)site.Element("nameFr");
cwharris
  • 17,835
  • 4
  • 44
  • 64
0

I would probably rewrite it like this:

  • get the list of matching <site> nodes once
  • iterate over all matches (typically only one)
  • get the code attribute and nameFr element from those matching items

Code would look like this:

// determine the matching list of <site> nodes ...
var selectedSites = loaded
                       .Descendants("site")
                       .Where(x => x.Element("provinceCode").Value == selectedProvince)
                       .Where(x => x.Element("nameEn").Value == selectedCity);

// iterate over all matching <site> nodes
foreach (var site in selectedSites)
{
    // grab the code attribute and nameFr element from <site> node
    var siteCode = site.Attribute("code").Value;
    var nameFR = site.Element("nameFr").Value;
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • 1
    Thanks a lot. I ended up with much the same code I started with. What your code had that mine didn't was that you used "Attribute" instead of "Element". So that is why the code for strSiteCode wasn't working. (thank you!!) And my code for strNameFR was actually working, except I had accidentally made it ="" while testing. (Doh! Smacks forehead) – Doug Oct 01 '11 at 20:21
  • @Doug: a classical **PICNIC** : Problem In Chair - Not In Computer :-) – marc_s Oct 01 '11 at 20:28
  • There's a few null ref problems that could arise using this code, btw. :) – cwharris Oct 01 '11 at 20:31
  • @xixonia: yes, obviously - this was absolutely assuming that the "provinceCode" and "nameEn" on the nodes are **always** there (and also the "code" attribute on and the "nameFr" element inside that node) - for sure! I wasn't trying to show how to perfectly handle all potential errors ... – marc_s Oct 01 '11 at 20:37
  • 1
    Righto. I just got excited, because the explicit operators on the XNode set of classes are really cool for things like this. Using '(string)x.Element(...)' is the same as 'x.Element(...).Value', except if the element comes up null, it'll just return null on the cast. :) – cwharris Oct 01 '11 at 20:41