如何抓取一个节点并将其作为一个新对象进行处理

How to grab a node and work on it as a new object

我需要从一个大的 XML 文件中提取一个片段,并且只使用该片段。

xml = <<XMLEND
<CFRDOC xsi:noNamespaceSchemaLocation="CFRMergedXML.xsd">
    <TITLE>
        <SUBTITLE>
            <CHAPTER>
                <TOC></TOC>
                <PART></PART>
                <PART></PART>
                <PART>
                    <EAR>Pt. 1903</EAR>
                    <HD SOURCE="HED">PART 1903—INSPECTIONS, CITATIONS AND PROPOSED PENALTIES</HD>
                    <CONTENTS></CONTENTS>
                    <AUTH></AUTH>
                    <SOURCE></SOURCE>
                    <SECTION>section1</SECTION>
                    <SECTION>section2</SECTION>
                    <SECTION>section3</SECTION>
                    <SECTION>section4</SECTION>
                </PART>
            </CHAPTER>
        </SUBTITLE>
    </TITLE>
</CFRDOC>
XMLEND

doc = Nokogiri::HTML(xml)

section = doc.xpath("//section")

# I can grab a specific node...
section[3].text          
=> "section4"

# copy it 
temp = section[3].dup
=> #<Nokogiri::XML::Element:0x261ce64 name="section" children=[#<Nokogiri::XML::Text:0x261c98c "section4">]>

# but the variable still refers to the whole...
doc.xpath("//part").size
=> 3
section.xpath("//part").size
=> 3
temp.xpath("//part").size 
=> 3

来自 PHP 背景,我不得不重新考虑一下变量。我知道 Ruby 中的变量不同;它们是指向对象的指针。

因此,当我 运行 temp.xpath 时,我实际上是 运行 在 doc 上使用它。但我想获取一个特定的节点及其子节点,然后将其作为一个新对象进行处理。这将极大地缩小大海捞针的范围,并使我的其余工作变得更加轻松!

如何仅使用我选择的节点创建新对象?我想将 section[3] 变成一个看不到其他 <part> 及其关联的 <section> 标签的新对象。

"//part" 表示“从文档的顶部开始搜索到底部,找到所有 <part> 个节点。

这不是你想要的。

相反你想要:

"./part"

这意味着“从当前位置开始并在其中搜索。

最容易将 XPath 想象成您在磁盘上的目录结构中导航。如果你想在你使用的驱动器的根目录下找到一个文件:

/foo

如果您想在当前目录中查找文件,您可以使用:

./foo

XPath 使用 // 表示 "search from the top to the bottom":

//foo

除非我需要 XPath 的强大功能,否则我建议使用 CSS 选择器而不是 XPath。我发现 XPath 在视觉上很嘈杂。所以,相反,我会使用:

section = doc.search('section')

section.search('part')

现在,冥想一下:

require 'nokogiri'

xml = <<XMLEND
<CFRDOC xsi:noNamespaceSchemaLocation="CFRMergedXML.xsd">
  <TITLE>
    <SUBTITLE>
      <CHAPTER>
        <PART></PART>
        <PART>
          <SECTION>section1</SECTION>
          <SECTION>section2</SECTION>
          <SECTION>section3</SECTION>
          <SECTION>section4</SECTION>
        </PART>
      </CHAPTER>
    </SUBTITLE>
  </TITLE>
</CFRDOC>
XMLEND

doc = Nokogiri::XML(xml)

为了便于阅读,我减少了 XML。

doc.search('SECTION').map(&:text) # => ["section1", "section2", "section3", "section4"]
doc.search('PART').size # => 2
doc.search('PART[2]').text # => "\n          section1\n          section2\n          section3\n          section4\n        "
doc.search('PART[2]').search('SECTION').map(&:text) # => ["section1", "section2", "section3", "section4"]
doc.search('PART[2] SECTION').map(&:text) # => ["section1", "section2", "section3", "section4"]
doc.search('PART SECTION').map(&:text) # => ["section1", "section2", "section3", "section4"]

使用简单的选择器可以轻松深入文档。有时不可能编写一个简单的选择器,因此我们必须在文档中找到路径点并从中导航,但基于示例 XML 它非常简单。

另见“”。

使用to_xmltemp变回XML字符串,然后再次使用Nokogiri::XML得到一个新对象。

my_section = Nokogiri::XML(temp.to_xml)
my_section.xpath('//part').size
# => 0

puts my_section
# <?xml version="1.0"?>
# <section><section4</section>

(我不确定您为什么一开始就使用 Nokogiri::HTML,但如果您认为需要,可以在此处将其替换为 XML。)