2012-05-17 17 views
1

我使用Ruby构建将表示网络图的gexf格式的XML结构。该图由几个嵌套节点层组成。我们的想法是解析看起来是这样的文件:构建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“。建筑师或者Nokogiri的建筑师或者其他什么都可以吗?

+1

这可能与Nokogiri。你有什么尝试? –

回答

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| 
+0

啊,谢谢!这是有道理的。我使用“id”的原因是这是gexf格式的工作原理。每个节点都有属性“id”,它应该是唯一的标识符。我的问题中的标识符是示例。在真实情况下,我确定它们是独一无二的。而我的源数据被这样格式化的原因是为了表明它没有任何顺序。 – hriundel

+0

我的荣幸;我希望它有帮助。 – Phrogz