2016-12-26 2 views
1

Как использовать concat() в выражении Xpath Nokogiri? Я пробовал:Nokogiri и concat()

xml.xpath("concat(/root/a/text(), /root/b/text())") 

Но это терпит неудачу с:

ArgumentError: node_set must be a Nokogiri::XML::NodeSet

На самом деле это, кажется, как Nokogiri принимает только XPATH выражения, которые начинаются с "" или «/», так что я попробовал эти:

xml.xpath(".concat(/root/a/text(), /root/b/text())") 
xml.xpath("/concat(/root/a/text(), /root/b/text())") 
xml.xpath("/concat('foo', 'bar')") 

Но все они терпят неудачу с этой ошибкой:

Nokogiri::XML::XPath::SyntaxError: Invalid expression: /concat('foo', 'bar')

Я знаю Nokogiri основан на libxml2 и как таковой он реализует только XPath 1.0. Но concat() является частью XPath 1.0. Тем не менее, подобная функция в XPath 2.0 строка присоединиться(), и я дал ему шанс:

xml.xpath("string-join('foo', 'bar')")  

Сообщение об ошибке:

RuntimeError: xmlXPathCompOpEval: function string-join not found

Видите, это другое сообщение об ошибке, чем для CONCAT(). Таким образом, по крайней мере, функция concat() найдена. Еще один намек, что она может работать как-то ...

xml.xpath("concat()")               

... возвращает ожидаемое сообщение об ошибке:

Nokogiri::XML::XPath::SyntaxError: Invalid number of arguments: concat()

Любой шанс получить CONCAT() работает с аргументами?

P.S .: Основные выражения xpath, такие как xml.xpath("/root/a/text()") работают нормально.

+1

Можете ли вы представить полный пример своей проблемы? XPath с 'concat' работает хорошо для меня, я подозреваю, что вы используете результат где-нибудь, где Nokogiri ожидает чего-то еще. – matt

ответ

2

Запрос XPath обычно возвращает набор узлов , набор узлов из документа. В Нокигири это объект Nokogiri::XML::NodeSet.

Nokogiri также позволяет использовать NodeSet с помощью метода xpath. В этом случае Nokogiri executes the query individually against each of the nodes in the NodeSet in turn, and combines them all into a new NodeSet which it returns as the result.

Обычно это работает так, как вы ожидали бы, и результатом будет комбинация всех узлов, соответствующих запросу. Однако это не работает при использовании запроса XPath, который возвращает что-то, отличное от набора узлов, как в этом случае.

После выполнения запроса Nokogiri пытается добавить результат (который он ожидает NodeSet) к вновь созданному NodeSet. Когда результатом является строка this fails with the error you are getting.

Это было поднято в Nokogiri bug и добавлено к их roadmap, но пока нет разрешения.

Обходное решение/решение заключается в том, чтобы все ваши запросы, не связанные с узлом, возвращали запросы XPath на отдельных узлах, а не на узлы. at_xpath вернет первый соответствующий узел для запроса, и этот узел может быть использован для дальнейших вызовов xpath без этой проблемы.

1

Казаться работать для меня

body = "<root><a>aaa</a><b>bbb</b></root>" 
xml = Nokogiri::XML(body) 
xml.xpath("concat(/root/a/text(), /root/b/text())") # => "aaabbb" 

Учитывая ваше обновление, может попробовать это?

body = "<wrapper><root><a>aaa</a><b>bbb</b></root></wrapper>" 
xml = Nokogiri::XML(body) 
xml = xml.xpath("wrapper") # returns a node set 
xml.map { |each| each.xpath("concat(./root/a/text(), ./root/b/text())") } # => ["aaabbb"] 

NB, обратите внимание на ведущую . закрепить запрос на текущем узле.