Это интересный вопрос.Большинство проблем уже упоминалось в ответе @ lwburk и в его комментариях.Просто чтобы раскрыть немного больше сложностей, скрытых в этом вопросе для случайного читателя, мой ответ, вероятно, более сложный или более подробный, чем необходим ОП.
Особенности XPath 1.0, связанные с этой проблемой
В XPath каждый шаг и каждый узел в наборе выбранных узлов работают независимо.Это означает, что
- подвыражение не имеет общего способа доступа к данным, которые были вычислены в предыдущем подвыражении, или обмениваться данными, вычисленными в этом подвыражении, с другими подвыражениями
- узел не имеет общего способадля ссылки на узел, который использовался в качестве узла контекста в предыдущем подвыражении
- , узел не имеет общего способа ссылаться на другие узлы, которые выбраны в данный момент.
- , если каждый из выбранных узловнеобходимо сравнить с тем же определенным узлом, тогда этот узел должен быть однозначно определен способом, который является общим для всех выбранных узлов
(Ну, на самом деле, я не уверен на 100%, если этот списокАбсолютно правильно в каждом случае. Если кто-то лучше знает особенности XPath, пожалуйста, прокомментируйте или исправьте этот ответ, отредактировав его.)
Несмотря на отсутствие общих решений, некоторые из этих ограничений можно преодолеть, если естьправильное знание структуры документа, и / или ранее использованная ось может быть «обращена»другая ось, которая служит в качестве обратной ссылки, то есть соответствует только узлам, которые использовались в качестве узла контекста в предыдущем выражении.Типичным примером этого является случай, когда ось parent
используется после первого использования оси child
(противоположный случай, от дочернего элемента к родительскому, не может быть однозначно обратимым без дополнительной информации).В таких случаях информация с предыдущих шагов более точно воссоздается на более позднем этапе (вместо доступа к ранее известной информации).
К сожалению, в этом случае я не смог придумать какое-либо другое решение, к которому можно обратиться ранееизвестные узлы, за исключением использования переменных XPath (это необходимо определить заранее).
XPath определяет синтаксис для ссылки на переменную, но не определяет синтаксис для определения переменных, способ определения переменных зависит от среды, гдеXPath используется.На самом деле, поскольку в рекомендации говорится, что «привязки переменных, используемые для оценки подвыражения, всегда совпадают с привязками переменных, используемыми для оценки содержащего выражения», вы также можете утверждать, что XPath явно запрещает определение переменных внутри выражения XPath.
Проблема переформулирована
В вашем вопросе проблема будет заключаться в том, чтобы, когда задано <dt>
, идентифицировать следующие элементы <dd>
или первоначально заданный узел после переключения узла контекста.Определение первоначально заданного <dt>
имеет решающее значение, поскольку для каждого узла в наборе узлов, подлежащего фильтрации, выражение предиката оценивается с этим узлом в качестве узла контекста;поэтому нельзя ссылаться на оригинал <dt>
в предикате, если нет способа идентифицировать его после изменения контекста.То же самое относится к <dd>
элементам, которые следуют за братьями и сестрами данного <dt>
.
Если вы используете переменные, можно спорить, есть ли существенное различие между 1) использованием синтаксиса переменных XPath и специфическим для Nokogiriспособ объявления этой переменной или 2) использование расширенного синтаксиса XPath Nokogiri, который позволяет использовать переменные Ruby в выражении XPath.В обоих случаях переменная определяется специфическим для среды способом, и значение XPath ясно только в том случае, если также доступно определение переменной.Аналогичный случай можно увидеть с XSLT, где в некоторых случаях вы можете сделать выбор между 1) определением переменной с помощью <xsl:variable>
перед использованием вашего выражения XPath или 2) использованием current()
(внутри вашего выражения XPath), которое является расширением XSLT.
Решение с использованием переменных набора узлов и метода Кайсиана
Вы можете выбрать все элементы <dd>
после элемента current <dt>
с помощью following-sibling::dd
(набор A).Также вы можете выбрать все элементы <dd>
, следующие за элементом next <dt>
с помощью following-sibling::dt[1]/following-sibling::dd
(набор B).Теперь разница в множестве A\B
оставляет элементы <dd>
, которые вы действительно хотели (элементы, которые находятся в наборе A, но не в наборе B).Если переменная $setA
содержит набор узлов A, а переменная $setB
содержит набор узлов B, разность наборов может быть получена с помощью (модификации) метода Кайсиана:
dds = $setA[count(.|$setB) != count($setB)]
Простой обходной путь без каких-либо переменных
В настоящее время ваш метод состоит в том, чтобы выбрать все элементы <dt>
и затем попытаться связать значение каждого такого элемента со значениями соответствующих элементов <dd>
в одной операции.Можно ли было бы преобразовать эту логику связи, чтобы она работала наоборот?Таким образом, вы сначала должны выбрать все <dd>
элементов, а затем для каждого <dd>
найти соответствующий <dt>
.Это будет означать, что вы получите доступ к одним и тем же элементам <dt>
несколько раз, и при каждой операции вы добавляете только одно новое значение <dd>
.Это может повлиять на производительность, а код Ruby может быть более сложным.
Хорошая сторона - это простота требуемого XPath.При наличии элемента <dd>
найти соответствующий <dt>
удивительно просто: preceding-sibling::dt[1]
Применительно к вашему текущему коду Ruby
dl.xpath('dd').each do |dd|
dt = dd.xpath("preceding-sibling::dt[1]")
## Insert new Ruby magic here ##
end