Как получить элемент из узла в XDocument, где известны два других элемента

#c# #.net-3.5 #elements #linq-to-xml

#c# #.net-3.5 #элемент #linq-to-xml

Вопрос:

Я в тупике (снова) из-за неправильного понимания XDocument / Linq. Для приведенного ниже XML у меня есть nameEn provinceCode переменные and as в моем коде. Я пытаюсь идентифицировать code (например, s0000002 ) и nameFr учитывая, что у меня есть два других элемента. Объединенные provinceCode и NameEn уникальны в XML (без дублирования).

 <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>
  

Вот код, который я пытаюсь (мой XML находится в «загруженном» 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();
  

Строка strSiteCode возвращает: System.Linq.Enumerable WhereSelectEnumerableIterator 2[System.Xml.Linq.XElement,System.String] and strNameFR returns «»`.

Я не могу понять, как должен выглядеть рабочий код. Спасибо за любую помощь.

Doug

Ответ №1:

Попробуйте

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

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

Select() Вызов возвращает коллекцию (которая, в вашем случае, имеет только один элемент). Поэтому вам придется вызвать SingleOrDefault() (или Single() ), чтобы получить один элемент. Кроме того, я удалил второе Where() и включил условие в первое Where() .

Комментарии:

1. Привет, Бала. Я не мог заставить это работать. Я продолжал получать эту ошибку «Оператор ‘amp;amp;’ не может быть применен к операндам типа ‘bool’ и ‘System.Xml.Linq. XElement'» Спасибо

2. @Doug Ах! Я кое-что упустил при объединении двух Where() элементов. Теперь это исправлено.

Ответ №2:

Имейте в виду, что возможно, что метод «x.Element (…)» вернет значение null, в результате чего доступ к «Значению» для него приведет к нулевой ссылке. Это предполагает, что ваш xml не всегда может иметь provinceCode или nameEn . Если это произойдет, у вас не возникнет проблем, но вы все равно не захотите помещать эту возможную нулевую ссылку в код выпуска. Следующее решает проблему null ref.

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

if (site == null)
{
    return
}

var siteCode = (string)site.Attribute("code");
var nameFr = (string)site.Element("nameFr");
  

Ответ №3:

Я бы, вероятно, переписал его так:

  • получите список совпадающих <site> узлов один раз
  • перебор всех совпадений (обычно только одного)
  • получить code атрибут и nameFr элемент из этих совпадающих элементов

Код будет выглядеть следующим образом:

 // 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;
}
  

Комментарии:

1. Большое спасибо. В итоге я получил почти тот же код, с которого начал. В вашем коде было то, чего не было в моем, так это то, что вы использовали «Атрибут» вместо «Элемент». Вот почему код для strSiteCode не работал. (спасибо !!) И мой код для strNameFR действительно работал, за исключением того, что я случайно сделал это =»» во время тестирования. (Ого! Бьет по лбу)

2. @Doug: классический ПИКНИК : проблема в кресле, а не в компьютере 🙂

3. Кстати, при использовании этого кода может возникнуть несколько проблем с нулевой ссылкой. 🙂

4. @xixonia: да, очевидно — это абсолютно предполагало, что «provinceCode» и «nameEn» на узлах <site> всегда есть (а также атрибут «code» на <site> и элемент «nameFr» внутри этого узла) — наверняка! Я не пытался показать, как идеально обрабатывать все потенциальные ошибки …

5. Правильно. Я просто был взволнован, потому что явные операторы в наборе классов XNode действительно хороши для подобных вещей. Использование ‘(string)x.Element (…)’ такое же, как ‘x.Element (…).Value’, за исключением того, что если элемент имеет значение null, он просто вернет null при приведении. 🙂