2014-02-19 29 views
4

我想遍歷XML中與引入nokogiri一個文件夾結構,但我在這很卡:如何遍歷XML嵌套的元素與引入nokogiri在Ruby中

<test> 
    <folder name="Folder A"> 
     <folder name="Folder A1"> 
     <file name="a.txt">Cool file</file> 
     </folder> 
     <folder name="Folder A2"></folder> 
    </folder> 
    <folder name="Folder B"> 
     <folder name="Folder B1"></folder> 
     <folder name="Folder B2"> 
     <folder name="Folder B21"> 
      <file name="b.txt"></file> 
     </folder> 
    </folder> 
</test> 

所以,我想遍歷這個爲了能夠創建文件夾和文件樹(文件夾A1和A2位於文件夾A內,文件夾B1和B2位於文件夾B內,文件夾B21位於文件夾B2內)。

所以我這樣做:

nodes = allnodes.xpath('//folder') 
nodes.each do |node| 
    puts "name => #{node.attributes['name']}" 
end 

但這列出了我所有的文件夾(A,A1,A2,B,B1,B2,B21)。我怎麼做到這一點,以便我不檢查以前的文件夾裏面的更多文件夾,然後我發送它到相同的遞歸函數?

非常感謝您的幫助:)

+0

你是問如何得到給定文件夾的子文件夾?例如,如果您指定了「文件夾A」,則只會獲得「A1」和「A2」? –

+0

不錯的xml結構.. +1 –

+0

你是什麼意思由「我怎樣才能使它,以便我不檢查以前的文件夾裏面更多的文件夾」? '//文件夾'只給你一個文件夾一次。你想做什麼? –

回答

6

當您使用帶有//foo的XPath時,您會在任何級別找到foo元素。如果您改爲使用./foo或僅使用foo,那麼您只會找到子元素。因此:

# Given an XML node, yields the node and all <file> children 
# Then recursively does the same with every <folder> child 
def process_files_and_folders(node,&blk) 
    yield node, node.xpath('file') 
    node.xpath('folder').each{ |folder| process_files_and_folders(folder,&blk) } 
end 

到此的鍵的(a)遞歸(具有該方法調用自身的所有子文件夾)和(b)捕獲由用戶與&blk符號通過了塊,然後使該阻止後來的呼叫。

在行動中看到:

require 'nokogiri' 
doc = Nokogiri.XML(my_xml) 
process_files_and_folders(doc.root) do |folder,files| 
    depth = folder.ancestors.length-1 # Just for pretty text output indenting 
    indent = " "*depth     # Just for pretty text output indenting 
    if folder['name'] 
    puts "#{indent}Processing the folder named #{folder['name']}" 
    else 
    puts "#{indent}No folder name; probably the root element." 
    end 
    unless files.empty? 
    puts "#{indent}There are #{files.length} files in '#{folder['name']}':" 
    files.each{ |file| print indent, file['name'], "\n" } 
    end 
end 

結果:

No folder name; probably the root element. 
    Processing the folder named Folder A 
    Processing the folder named Folder A1 
    There are 1 files in 'Folder A1': 
    a.txt 
    Processing the folder named Folder A2 
    Processing the folder named Folder B 
    Processing the folder named Folder B1 
    Processing the folder named Folder B2 
     Processing the folder named Folder B21 
     There are 1 files in 'Folder B21': 
     b.txt 
+0

@Tiago如果(且僅當)此答案解決了您的問題,請記住[接受它](http://meta.stackexchange.com/a/5235/153741)。如果沒有,請隨時發佈後續評論以獲取更多的說明或幫助。 – Phrogz

+0

@Phhogz enve雖然我已經解決了這個問題,但我會盡快嘗試,謝謝:) – Tiago

1

我下面做:

require 'nokogiri' 

doc = Nokogiri::XML(<<-xml) 
<test> 
    <folder name="Folder A"> 
     <folder name="Folder A1"> 
     <file name="a.txt">Cool file</file> 
     </folder> 
     <folder name="Folder A2"></folder> 
    </folder> 
    <folder name="Folder B"> 
     <folder name="Folder B1"></folder> 
     <folder name="Folder B2"> 
     <folder name="Folder B21"> 
      <file name="b.txt"></file> 
     </folder> 
    </folder> 
</test> 
xml 

# Here I am collecting all folders, which has at-least one child. 
parent_folders = doc.xpath("//folder").select do|folder_node| 
    folder_node.xpath("./folder").size > 0 
end 

# Here I will iterate each parent directory, and would collect the corresponding 
# sub-directories names. 
parent_directory = parent_folders.each_with_object({}) do |parent_dir,dir_hash| 
    dir_hash[parent_dir['name']] = parent_dir.xpath("./folder").collect do |sub_dir| 
    sub_dir['name'] 
    end 
end 

parent_directory 
# => {"Folder A"=>["Folder A1", "Folder A2"], 
#  "Folder B"=>["Folder B1", "Folder B2", "Folder B21"], 
#  "Folder B2"=>["Folder B21"]} 

現在,你有一個哈希parent_directory,它維護的所有目錄(鍵)/子目錄(價值)關係。現在使用Hash#[]方法,可以很容易地提取給定目錄的子目錄。一個例子 -

parent_directory['Folder A'] # => ["Folder A1", "Folder A2"] 
0

這是有點不清楚你想要做什麼,但假設你正在Linux系統上的磁盤上創建一個新的目錄結構。

doc.xpath("//folder[not(folder)]").each do |f| 
    path = f.xpath("ancestor-or-self::folder").map{|f| f['name']}.join("/") 
    system("mkdir -p #{path}") 
end 

這裏就是這樣做:

  • 第一行查找所有最低級別的文件夾(在XML葉節點)
  • 下一行查找所有包含文件夾的名稱,加入斜槓以獲得完整的「路徑」。
  • 最後,系統命令「mkdir -p」創建最低級別的文件夾和其中的每個文件夾。
0

所以,後來我發現如何解決這個問題。

只是爲了澄清,我打算有這樣的功能:

def create_structure(nodeset, current_folder) 
    new_folder = "#{current_folder }/#{nodeset.attributes['name']" 
    Dir.makedir(new_folder) 
    create_files_in_current_folder(nodeset, new_folder) 
    subnodeset = nodeset.xpath('/folder') 
    subnodeset.each do |node| 
     create_structure(node, new_folder) 
    end 
end 

所以,我可以複製我有結構的XML文件系統。

所以,就解決方案而言,這是正確的在我眼前。我無法使用「//文件夾」,而是使用「/文件夾」,因爲第一個文件夾將返回所有的文件夾,而不管它們在xml結構中的位置,第二個文件夾將僅返回根目錄中的文件夾當前節點。

我希望這有助於並感謝大家的答案。我會盡快嘗試。