XPath для выбора только дочерних элементов (не пустых текстовых узлов) - PullRequest
5 голосов
/ 17 января 2012

Я анализирую некоторые XML, используя Nokogiri и XPath. Когда я делаю это:

doc.xpath('//Order/child::node()').each do |node|
  puts node.name
end

Он печатает все узлы, но также и между именами, он печатает «текст». Я думаю, я знаю почему:

В моем xml есть пробелы между узлами, как это: "<a1>hi</a1> \n <a2>bye</a2>"

Есть ли способ, которым я могу сказать, чтобы игнорировать вещи между узлами?

Ответы [ 2 ]

7 голосов
/ 17 января 2012

Используйте :

//Order/node()[not(self::text()[not(normalize-space())])]

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

Проверка на основе XSLT :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>


 <xsl:template match="/*">
     <xsl:variable name="vSel1" select="//Order/node()"/>
     <xsl:variable name="vSel2" select=
     "//Order/node()[not(self::text()[not(normalize-space())])]"/>

     <xsl:for-each select="$vSel1">
       <xsl:value-of select="concat('&#xA;',position(), ': ')"/>
       <xsl:copy-of select="."/>
       <xsl:text>&#xA;</xsl:text>
     </xsl:for-each>
================
     <xsl:for-each select="$vSel2">
       <xsl:value-of select="concat('&#xA;',position(), ': ')"/>
       <xsl:copy-of select="."/>
       <xsl:text>&#xA;</xsl:text>
     </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

, когда это преобразование применяется к следующему документу XML :

<t>
 <Order>
  <a/>
  <b>xxx</b>
  <c/>
 </Order>
 <Order>
  <d/>
  <e>xxx</e>
  <f/>
 </Order>
</t>

вычисляются два выражения XPath и выводятся узлы двух соответствующих наборов выбранных узлов, каждому из которых предшествует номер позиции :

1: 


2: <a/>

3: 


4: <b>xxx</b>

5: 


6: <c/>

7: 


8: 


9: <d/>

10: 


11: <e>xxx</e>

12: 


13: <f/>

14: 


================

1: <a/>

2: <b>xxx</b>

3: <c/>

4: <d/>

5: <e>xxx</e>

6: <f/>
4 голосов
/ 17 января 2012

Если вам нужны только элементы, используйте лучший XPath: запрос /* найдет вас всех детей элементов :

require 'nokogiri'
doc = Nokogiri.XML("<r><a>1</a>\n\t<b>2</b></r>")
p doc.xpath('/r/child::node()').map(&:name)
#=> ["a", "text", "b"]

p doc.xpath('/r/*').map(&:name)
#=> ["a", "b"]

В качестве альтернативы, вы можете попросить Нокогири:выбросить любые текстовые заметки, которые являются только пробелами:

doc2 = Nokogiri.XML("<r><a>1</a>\n\t<b>2</b></r>",&:noblanks)
p doc2.xpath('/r/child::node()').map(&:name)
#=> ["a", "b"]

Или вы можете использовать Ruby для дальнейшей фильтрации вашего NodeSet на основе произвольных критериев:

mine = doc.xpath('/r/child::node()').select do |node| 
  node.type != Nokogiri::XML::Node::TEXT_NODE || node.content =~ /\S/
end
p mine.map(&:name)
#=> ["a", "b"]
...