如何在节点集中搜索并从同一节点集中删除节点
How to search within a nodeset and delete a node from that same nodeset
我有以下 xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document mc:Ignorable="w14 w15 wp14" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mo="http://schemas.microsoft.com/office/mac/office/2008/main" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape">
<w:body>
<w:p w14:paraId="56037BEC" w14:textId="1188FA30" w:rsidR="001665B3" w:rsidRDefault="008B4AC6">
<w:r>
<w:t xml:space="preserve">This is the story of a man who </w:t>
</w:r>
<w:ins w:author="Mitchell Gould" w:date="2016-09-28T09:15:00Z" w:id="0">
<w:r w:rsidR="003566BF">
<w:t>went</w:t>
</w:r>
</w:ins>
<w:del w:author="Mitchell Gould" w:date="2016-09-28T09:15:00Z" w:id="1">
<w:r w:rsidDel="003566BF">
<w:delText>goes</w:delText>
</w:r>
</w:del>
...
我使用 Nokogiri 解析 xml 如下:
zip = Zip::File.open("test.docx")
doc = zip.find_entry("word/document.xml")
file = Nokogiri::XML.parse(doc.get_input_stream)
我有一个包含所有 w:del 元素的 'deletions' 节点集:
@deletions = file.xpath("//w:del")
我在这个节点集内部搜索以查看是否存在如下元素:
my_node_set = @deletions.search("//w:del[@w:id='1']" && "//w:del/w:r[@w:rsidDel='003566BF']")
如果存在,我想将其从删除节点集中删除。我用以下方法做到这一点:
deletions.delete(my_node_set.first)
这似乎有效,因为没有返回任何错误,并且在终端中显示已删除的节点集。
但是,当我检查我的@deletions 节点集时,该项目似乎仍然存在:
@deletions.search("//w:del[@w:id='1']" && "//w:del/w:r[@w:rsidDel='003566BF']")
我只是想了解 Nokogiri,所以我显然没有在我的@deletions 节点集中正确搜索元素,而是在搜索整个文档。
如何在@deletions 节点集中搜索元素,然后将其从节点集中删除?
考虑一下:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
<body>
<div id="foo"><p>foo</p></div>
<div id="bar"><p>bar</p></div>
</body>
</html>
EOT
divs
包含 div
标签,它们是一个节点集:
divs = doc.css('div')
divs.class # => Nokogiri::XML::NodeSet
并包含:
divs.to_html # => "<div id=\"foo\"><p>foo</p></div><div id=\"bar\"><p>bar</p></div>"
您可以使用 at
搜索节点集以找到第一个匹配项:
divs.at('#foo').to_html # => "<div id=\"foo\"><p>foo</p></div>"
而且您可以轻松删除它:
divs.at('#foo').remove
从文档本身中删除它:
puts doc.to_html
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html>
# >> <body>
# >>
# >> <div id="bar"><p>bar</p></div>
# >> </body>
# >> </html>
它不会从NodeSet中删除它,但我们不关心这个,NodeSet只是一个指向文档本身中的节点的指针,用来给要删除的内容的列表。
如果您在删除某些节点后想要更新节点集,请重新扫描文档并重建节点集:
divs = doc.css('div')
divs.to_html # => "<div id=\"bar\"><p>bar</p></div>"
如果您的目标是删除 NodeSet 中的所有节点,而不是搜索该列表,您可以简单地使用:
divs.remove
puts doc.to_html
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html>
# >> <body>
# >>
# >>
# >> </body>
# >> </html>
当我删除节点时,我不会收集中间节点集,而是使用类似以下的方法即时执行:
doc = Nokogiri::HTML(<<EOT)
<html>
<body>
<div id="foo"><p>foo</p></div>
<div id="bar"><p>bar</p></div>
</body>
</html>
EOT
doc.at('div#bar p').remove
puts doc.to_html
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html>
# >> <body>
# >> <div id="foo"><p>foo</p></div>
# >> <div id="bar"></div>
# >> </body>
# >> </html>
删除 #bar
中嵌入的 <p>
标签。通过放松选择器并将 at
更改为 search
我可以将它们全部删除:
doc.search('div p').remove
puts doc.to_html
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html>
# >> <body>
# >> <div id="foo"></div>
# >> <div id="bar"></div>
# >> </body>
# >> </html>
如果您坚持遍历 NodeSet,请记住它们就像数组,您可以这样对待它们。下面是使用 reject
跳过特定节点的示例:
doc = Nokogiri::HTML(<<EOT)
<html>
<body>
<div id="foo"><p>foo</p></div>
<div id="bar"><p>bar</p></div>
</body>
</html>
EOT
divs = doc.search('div').reject{ |d| d['id'] == 'foo' }
divs.map(&:to_html) # => ["<div id=\"bar\"><p>bar</p></div>"]
虽然您不会收到 NodeSet,但您会收到一个数组:
divs.class # => Array
虽然可以这样做,但最好使用特定的选择器来减少集合,而不是依赖 Ruby 到 select
或 reject
个元素。
我有以下 xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document mc:Ignorable="w14 w15 wp14" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mo="http://schemas.microsoft.com/office/mac/office/2008/main" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape">
<w:body>
<w:p w14:paraId="56037BEC" w14:textId="1188FA30" w:rsidR="001665B3" w:rsidRDefault="008B4AC6">
<w:r>
<w:t xml:space="preserve">This is the story of a man who </w:t>
</w:r>
<w:ins w:author="Mitchell Gould" w:date="2016-09-28T09:15:00Z" w:id="0">
<w:r w:rsidR="003566BF">
<w:t>went</w:t>
</w:r>
</w:ins>
<w:del w:author="Mitchell Gould" w:date="2016-09-28T09:15:00Z" w:id="1">
<w:r w:rsidDel="003566BF">
<w:delText>goes</w:delText>
</w:r>
</w:del>
...
我使用 Nokogiri 解析 xml 如下:
zip = Zip::File.open("test.docx")
doc = zip.find_entry("word/document.xml")
file = Nokogiri::XML.parse(doc.get_input_stream)
我有一个包含所有 w:del 元素的 'deletions' 节点集:
@deletions = file.xpath("//w:del")
我在这个节点集内部搜索以查看是否存在如下元素:
my_node_set = @deletions.search("//w:del[@w:id='1']" && "//w:del/w:r[@w:rsidDel='003566BF']")
如果存在,我想将其从删除节点集中删除。我用以下方法做到这一点:
deletions.delete(my_node_set.first)
这似乎有效,因为没有返回任何错误,并且在终端中显示已删除的节点集。
但是,当我检查我的@deletions 节点集时,该项目似乎仍然存在:
@deletions.search("//w:del[@w:id='1']" && "//w:del/w:r[@w:rsidDel='003566BF']")
我只是想了解 Nokogiri,所以我显然没有在我的@deletions 节点集中正确搜索元素,而是在搜索整个文档。
如何在@deletions 节点集中搜索元素,然后将其从节点集中删除?
考虑一下:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
<body>
<div id="foo"><p>foo</p></div>
<div id="bar"><p>bar</p></div>
</body>
</html>
EOT
divs
包含 div
标签,它们是一个节点集:
divs = doc.css('div')
divs.class # => Nokogiri::XML::NodeSet
并包含:
divs.to_html # => "<div id=\"foo\"><p>foo</p></div><div id=\"bar\"><p>bar</p></div>"
您可以使用 at
搜索节点集以找到第一个匹配项:
divs.at('#foo').to_html # => "<div id=\"foo\"><p>foo</p></div>"
而且您可以轻松删除它:
divs.at('#foo').remove
从文档本身中删除它:
puts doc.to_html
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html>
# >> <body>
# >>
# >> <div id="bar"><p>bar</p></div>
# >> </body>
# >> </html>
它不会从NodeSet中删除它,但我们不关心这个,NodeSet只是一个指向文档本身中的节点的指针,用来给要删除的内容的列表。
如果您在删除某些节点后想要更新节点集,请重新扫描文档并重建节点集:
divs = doc.css('div')
divs.to_html # => "<div id=\"bar\"><p>bar</p></div>"
如果您的目标是删除 NodeSet 中的所有节点,而不是搜索该列表,您可以简单地使用:
divs.remove
puts doc.to_html
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html>
# >> <body>
# >>
# >>
# >> </body>
# >> </html>
当我删除节点时,我不会收集中间节点集,而是使用类似以下的方法即时执行:
doc = Nokogiri::HTML(<<EOT)
<html>
<body>
<div id="foo"><p>foo</p></div>
<div id="bar"><p>bar</p></div>
</body>
</html>
EOT
doc.at('div#bar p').remove
puts doc.to_html
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html>
# >> <body>
# >> <div id="foo"><p>foo</p></div>
# >> <div id="bar"></div>
# >> </body>
# >> </html>
删除 #bar
中嵌入的 <p>
标签。通过放松选择器并将 at
更改为 search
我可以将它们全部删除:
doc.search('div p').remove
puts doc.to_html
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html>
# >> <body>
# >> <div id="foo"></div>
# >> <div id="bar"></div>
# >> </body>
# >> </html>
如果您坚持遍历 NodeSet,请记住它们就像数组,您可以这样对待它们。下面是使用 reject
跳过特定节点的示例:
doc = Nokogiri::HTML(<<EOT)
<html>
<body>
<div id="foo"><p>foo</p></div>
<div id="bar"><p>bar</p></div>
</body>
</html>
EOT
divs = doc.search('div').reject{ |d| d['id'] == 'foo' }
divs.map(&:to_html) # => ["<div id=\"bar\"><p>bar</p></div>"]
虽然您不会收到 NodeSet,但您会收到一个数组:
divs.class # => Array
虽然可以这样做,但最好使用特定的选择器来减少集合,而不是依赖 Ruby 到 select
或 reject
个元素。