如何判断一个Nokogiri::XML::Node是否包含另一个的部分节点?
判断Nokogiri XML节点是否包含指定子结构
我使用Ruby的Nokogiri库,自定义了ConstituencyXMLNode类继承自Nokogiri::XML::Node,现在有两个节点:
节点结构示例
pp_leaf 节点
#(ConstituencyXMLNode:0xc3c8 { name = "PP", children = [ #(ConstituencyXMLNode:0xc3dc { name = "IN", children = [ #(Text "out")] }), #(ConstituencyXMLNode:0xc3f0 { name = "PP", children = [ #(ConstituencyXMLNode:0xc404 { name = "IN", children = [ #(Text "of")] }), #(ConstituencyXMLNode:0xc418 { name = "NP", children = [ #(ConstituencyXMLNode:0xc42c { name = "JJ", children = [ #(Text "non-living")] }), #(ConstituencyXMLNode:0xc440 { name = "NNS", children = [ #(Text "resources")] })] })] })] })
current_leaf 节点
#(ConstituencyXMLNode:0xc3f0 { name = "PP", children = [ #(ConstituencyXMLNode:0xc404 { name = "IN", children = [ #(Text "of")] }), #(ConstituencyXMLNode:0xc418 { name = "NP", children = [ #(ConstituencyXMLNode:0xc42c { name = "JJ", children = [ #(Text "non-living")] }), #(ConstituencyXMLNode:0xc440 { name = "NNS", children = [ #(Text "resources")] })] })] })
我需要判断pp_leaf是否包含current_leaf对应的节点结构。注意到Nokogiri::XML::NodeSet有include?方法,但不清楚如何将单个Node转为NodeSet,且Node本身没有直接判断结构包含的操作符,求解决思路。
解决思路
1. 区分对象引用匹配与结构匹配
如果是判断current_leaf是否是pp_leaf的后代节点(同一对象引用),直接用NodeSet的include?即可:
# 检查current_leaf是否是pp_leaf的后代节点 pp_leaf.descendants.include?(current_leaf)
如果要判断的是结构完全一致的节点(即使是不同对象),则需要递归比较节点结构。
2. 递归比较节点结构
编写辅助方法递归校验节点的名称、子节点数量及内容:
def structure_matches?(node_a, node_b) # 节点名称必须一致 return false unless node_a.name == node_b.name # 子节点数量必须一致 return false unless node_a.children.size == node_b.children.size # 逐个比较子节点 node_a.children.zip(node_b.children).all? do |child_a, child_b| if child_a.text? && child_b.text? # 文本节点忽略多余空格后比较 child_a.text.strip == child_b.text.strip else # 非文本节点递归比较结构 structure_matches?(child_a, child_b) end end end # 检查pp_leaf的后代中是否有结构匹配current_leaf的节点 contains_target = pp_leaf.descendants.any? { |node| structure_matches?(node, current_leaf) }
3. 快速序列化比较(简单场景)
如果节点结构简单、无复杂属性,可将节点序列化为XML字符串后判断包含关系:
# 序列化时去除多余空格,避免格式差异影响判断 current_xml = current_leaf.to_xml.strip.gsub(/\s+/, ' ') pp_xml = pp_leaf.to_xml.strip.gsub(/\s+/, ' ') contains_target = pp_xml.include?(current_xml)
4. 将单个Node转为NodeSet
如果确实需要将单个Node转为NodeSet,直接通过构造方法创建即可:
current_node_set = Nokogiri::XML::NodeSet.new(current_leaf.document, [current_leaf])
但注意NodeSet#include?仅判断对象引用是否存在,不校验结构一致性。
内容的提问来源于stack exchange,提问作者Kelsey Hannan
相关产品推荐
相关产品推荐

