在构建xml时引用特定块

时间:2012-05-17 00:21:42

标签: ruby xml nokogiri gephi

我正在使用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的建造者或其他任何东西?

1 个答案:

答案 0 :(得分:0)

在构建节点时,不要试图在节点上保留句柄,而是在需要时使用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|