我正在使用Ruby来构建gexf格式的XML结构,该结构将表示网络图。该图由多个级别的嵌套节点组成。这个想法是解析一个看起来像这样的文件:
| top node | middle node | bottom node |
| a | 1 | "name1" |
| b | 1 | "name6" |
| a | 2 | "name3" |
| b | 2 | "name8" |
| b | 1 | "name5" |
| a | 1 | "name2" |
| b | 2 | "name7" |
| a | 2 | "name4" |
并将其转换为:
<node id = a label = "top node">
<node id = 1 label = "middle node">
<node id = name1 label = "bottom node"/>
<node id = name2 label = "bottom node"/>
</node>
<node id = 2 label = "middle node">
<node id = name3 label = "bottom node"/>
<node id = name4 label = "bottom node"/>
</node>
</node>
<node id = b label = "top node">
<node id = 1 label = "middle node">
<node id = name5 label = "bottom node"/>
<node id = name6 label = "bottom node"/>
</node>
<node id = 2 label = "middle node">
<node id = name7 label = "bottom node"/>
<node id = name8 label = "bottom node"/>
</node>
</node>
如您所见,由于文件中的行不按任何特定顺序排列,因此在构建 XML 文件时,我需要能够引用每个节点和子节点。
如果我的问题仍然不清楚,当我阅读该行时:
| b | 1 | "name6" |
我需要能够告诉构建器将此节点"name6"粘贴在"顶部节点 b"和"中间节点 1"中。Builder或Nokogiri的Builder或其他任何东西都有可能吗?
在构建节点时尝试保留节点的句柄,不如使用 Nokogiri 的 CSS(或 XPath)查询功能在需要时查找已添加到文档中的节点:
require 'nokogiri'
# Create an array of the top/middle/bottom node ids
rows = File.readlines('my.data')[1..-1].map{ |row| row.scan(/[^|s"]+/) }
# Look underneath a parent node for another node with a specific id
# If you can't find one, create one (with the label) and return it.
def find_or_create_on(parent,id,label)
parent.at("node[id='#{id}']") or
parent.add_child("<node id='#{id}' label='#{label}' />")[0]
end
# Since an XML document can only ever have one root node,
# and your data can have many, let's wrap them all in a new document
root = Nokogiri.XML('<root></root>').root
# For each triplet, find or create the nodes you need, in order
# (When iterating an array of arrays, you can automagically convert
# each item in the sub-array to a named variable.)
rows.each do |top_id, mid_id, bot_id|
top = find_or_create_on( root, top_id, 'top node' )
mid = find_or_create_on( top, mid_id, 'middle node' )
bot = find_or_create_on( mid, bot_id, 'bottom node' )
end
puts root
#=> <root>
#=> <node id="a" label="top node">
#=> <node id="1" label="middle node">
#=> <node id="name1" label="bottom node"/>
#=> <node id="name2" label="bottom node"/>
#=> </node>
#=> <node id="2" label="middle node">
#=> <node id="name3" label="bottom node"/>
#=> <node id="name4" label="bottom node"/>
#=> </node>
#=> </node>
#=> <node id="b" label="top node">
#=> <node id="1" label="middle node">
#=> <node id="name6" label="bottom node"/>
#=> <node id="name5" label="bottom node"/>
#=> </node>
#=> <node id="2" label="middle node">
#=> <node id="name8" label="bottom node"/>
#=> <node id="name7" label="bottom node"/>
#=> </node>
#=> </node>
#=> </root>
请注意,您可能需要重新考虑属性 id
的用法,因为您在此处提供的值既不是 a) 在整个文档中全局唯一的,也不是 b) 有效的标识符(数字不能是 XML 中的ID
值)。
此外,输出中的某些子节点的排序顺序与它们在源数据中的显示顺序不同。例如,b/2/name8
出现在 b/2/name7
之前,因此我的解决方案按此顺序创建它们。如果您需要对它们进行排序,请先对rows
进行排序,例如:
rows.sort.each do |top_id,mid_id,bot_id|