Как получить позицию (индекс по порядку) XML-элемента с помощью LINQ?

#c# #xml #linq #silverlight-4.0 #linq-to-xml

#c# #xml #linq #silverlight-4.0 #linq-to-xml

Вопрос:

У меня XML точно такого формата:

 <?xml version="1.0" encoding="utf-8"?>
<book>
    <chapter>
        <verse>This is verse 1</verse>
        <verse>This is verse 2</verse>
        <verse>This is verse 3</verse>
        <verse>This is verse 4</verse>
    </chapter>
    <chapter>
        <verse>This is verse 1</verse>
        <verse>This is verse 2</verse>
    </chapter>
    <chapter>
        <verse>This is verse 1</verse>
    </chapter>
</book>
  

В C # с использованием Linq мне нужно иметь возможность получать XML-элемент с определенной позицией или индексом на основе значения функции state.getChapterNumber(). Например, если значение равно 4, мне нужно извлечь элемент 4th chapter из XML-документа.

 XDocument book = XDocument.Load(string.Format("Translations/NWT/{0}.xml", state.BookName));
var verses = from chapter in book.Decendants()
             where state.getChapterNumber() == (WHAT DO I PUT HERE TO MATCH THE VALUE??)
             from verse in chapter.Descendants("p").Elements()
             select new
             {
                 VerseNumber = verse.Attribute("n").Value,
                 Text = verse.Value,
                 LastVerseInParagraph = verse.Parent.Elements().LastOrDefault().Value,
                 FirstVerseInParagraph = verse.Parent.Elements().FirstOrDefault().Value
             };
  

Пожалуйста, помогите, это действительно важно и сдерживает меня в течение нескольких дней. Все, что я хочу сделать, это иметь возможность получать стихи из любого набора глав, который я выберу в книге.

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

1. Почему я не могу сказать where chapter.Index().Value == state.getChapterNumber()

Ответ №1:

Ну, для начала, я не думаю, что вы хотели второго потомка в целом — вы хотели вторую главу, что не обязательно одно и то же. К счастью, это легко сделать:

 // Use 1 for the second chapter, 2 for "element 2" (i.e. the third element) etc
var chapter = book.Descendants("chapter").ElementAt(1);

var verses = from verse in chapter...
  

РЕДАКТИРОВАТЬ: Для номера стиха используйте перегрузку Select , которая предоставляет индекс элемента:

 var verses = chapter.Element("verse")
                    .Select((element, index) => new {
                                VerseNumber = index   1,
                                Text = element.Value
                            });
  

Лично я не думаю, что для каждого стиха имеет смысл иметь первый и последний стих в абзаце. На самом деле это не особенность этого стиха, не так ли?

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

1. Ваша простая техника программирования тоже помогла. Может быть, вы сможете помочь выяснить, как я могу заставить linq возвращать количество итераций для каждого, select new чтобы у меня было VerseNumber = THE ITERATION, вместо строки выше. Мог бы я написать что-то вроде VerseNumber = verse.Index().Value,

2. Мой друг, ты спас мне жизнь! Я готовлю праздничный напиток прямо сейчас! Вот что я сделал (все благодаря вам). Я создал класс с именем Verse , который содержал все мои переменные. И затем я использовал вашу технику следующим образом. var verses = book.Descendants("c").ElementAt(state.GetChapter() - 1).Descendants("p").Elements() .Select((element, index) => new Verse { VerseNumber = (index 1).ToString(), ScriptureText = element.Value }); Это сработало отлично! Большое вам спасибо!

3. Если я тоже могу спросить вас, есть ли способ преобразовать это в linq sql? Как бы вы написали перегрузку select? Другими словами, как бы вы написали это Select((element, index) => new Verse { element, index }); в формате linq sql?

4. @GuyMicciche: Под «форматом LINQ SQL», я полагаю, вы подразумеваете выражение запроса? ( from x in y where x.foo select x.bar ?) В синтаксисе выражения запроса нет поддержки для этой перегрузки.

5. @JonSkeet У меня похожий случай, но для меня стихи уникальны, как я могу получить индекс главы?

Ответ №2:

Еще один правильный ответ, хотя ответ Джона Скита намного эффективнее, заключается в следующем коде:

 chapter.ElementsBeforeSelf().Count()
  

Ответ №3:

Используя обозначение метода расширения, вы можете использовать Select() вызов, который предоставит вам индекс текущего элемента следующим образом:

 var indexedChapters = book.Decendants().Select((Chapter, Index) => new { Chapter, Index });
  

После этого вы можете получить доступ к индексу анонимного типа в любом следующем операторе linq.

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

1. В моем операторе linq после оператора ‘from’ мой оператор where выглядит следующим образом: where book.Decendants("c").Index == state.GetChapterNumber() По сути, я извлекаю значение из функции, чтобы получить соответствующую позицию xml-элемента на основе этого значения. Не могли бы вы помочь, кажется, вы направили меня в правильном направлении.

2. У меня проблема посерьезнее. Где у меня есть VerseNumber = verse.Attribute("n").Value, Мне нужно значение VerseNumber каждый раз, чтобы перебирать количество стихов в XML-файле для каждой главы. О боже.