2010-12-30 5 views
1

Grundsätzlich möchte ich den absoluten Pfad von einem Knoten zum root extrahieren und es an die Konsole oder eine Datei melden. Im Folgenden finden Sie die aktuelle Lösung:Wie bekomme ich den absoluten Knotenpfad in XML mit XPath und Ruby?

require "rexml/document" 

include REXML 

def get_path(xml_doc, key) 
    XPath.each(xml_doc, key) do |node| 
    puts "\"#{node}\"" 
    XPath.each(node, '(ancestor::#node)') do |el| 
     # puts el 
    end 
    end 
end 

test_doc = Document.new <<EOF 
    <root> 
    <level1 key="1" value="B"> 
    <level2 key="12" value="B" /> 
    <level2 key="13" value="B" /> 
    </level1> 
    </root> 
EOF 

get_path test_doc, "//*/[@key='12']" 

Das Problem ist, dass es mir "<level2 value='B' key='12'/>" als Ausgang gibt. Gewünschte Ausgabe ist <root><level1><level2 value='B' key='12'/> (Format könnte anders sein, das Hauptziel ist es, einen vollständigen Pfad zu haben). Ich habe nur grundlegendes Wissen von XPath und würde jede mögliche Hilfe/Anleitung schätzen, wohin man schaut und wie man das erreicht.

+0

ich frage mich, ob es irgendwelche Lösungen mit REXML sind. Die Installation von Nokogiri ist ziemlich kompliziert und ich habe keinen Root-Zugriff auf die Server-Box, wo das Skript ausgeführt wird. –

+0

Nokogiri Installation ist überhaupt nicht kompliziert. Es erfordert libxml, aber viele XML-Parser benötigen es auch, also ist es schon auf vielen Systemen. Probiere 'locate libxml2 | grep/lib/'zu sehen; Es sollte in etwas wie '/ usr/lib/libxml2' erscheinen. Wenn Sie keinen Zugriff auf die Installation als Root haben, können Sie in alternative Verzeichnisse installieren, einschließlich Ihrer eigenen. Viele von uns verwenden und empfehlen [RVM] (http://rvm.beginrescueend.com), um lokale ('~/.rvm') Ruby-Installationen zu verwalten. –

+0

Danke für die Hilfe. Leider muss ich mit REXML gehen (vorerst ist es sehr teuer, diese alte Server-Box und Cygwin-Installation auf mehreren Dev-Boxen zu aktualisieren). Langfristig werde ich definitiv auf Nokogiri umsteigen (hauptsächlich wegen der Leistung). –

Antwort

2

Wenn Sie auf REXML gesetzt sind, ist hier eine REXML Lösung:

require 'rexml/document' 

test_doc = REXML::Document.new <<EOF 
    <root> 
    <level1 key="1" value="B"> 
     <level2 key="12" value="B" /> 
     <level2 key="13" value="B" /> 
    </level1> 
    </root> 
EOF 

def get_path(xml_doc, key) 
    node = REXML::XPath.first(xml_doc, key) 
    path = [] 
    while node.parent 
    path << node 
    node = node.parent 
    end 
    path.reverse 
end 

path = get_path(test_doc, "//*[@key='12']") 
p path.map{ |el| el.name }.join("/") 
#=> "root/level1/level2" 

Oder, wenn Sie möchten, verwenden Sie die gleiche get_path Implementierung von der anderen Antwort, können Sie monkeypatch REXML eine ancestors Methode hinzufügen:

class REXML::Child 
    def ancestors 
    ancestors = [] 

    # Presumably you don't want the node included in its list of ancestors 
    # If you do, change the following line to node = self 
    node = self.parent 

    # Presumably you want to stop at the root node, and not its owning document 
    # If you want the document included in the ancestors, change the following 
    # line to just while node 
    while node.parent 
     ancestors << node 
     node = node.parent 
    end 

    ancestors.reverse 
    end 
end 
3

Beachten Sie zuerst, dass Ihr Dokument nicht, denke ich, was Sie beabsichtigt haben. Ich vermute, dass Sie nicht <level1> selbstschließend sein wollten, aber die <level2> Elemente als Kinder enthalten.

Zweitens bevorzuge und befürworte ich Nokogiri anstelle von REXML. Es ist schön, dass REXML mit Ruby kommt, aber Nokogiri ist schneller und bequemer, IMHO. Also:

require 'nokogiri' 

test_doc = Nokogiri::XML <<EOF 
    <root> 
    <level1 key="1" value="B"> 
     <level2 key="12" value="B" /> 
     <level2 key="13" value="B" /> 
    </level1> 
    </root> 
EOF 

def get_path(xml_doc, key) 
    xml_doc.at_xpath(key).ancestors.reverse 
end 

path = get_path(test_doc, "//*[@key='12']") 
p path.map{ |node| node.name }.join('/') 
#=> "document/root/level1" 
4

Dies sollte Ihnen den Einstieg:

require 'nokogiri' 

test_doc = Nokogiri::XML <<EOF 
    <root> 
    <level1 key="1" value="B"> 
    <level2 key="12" value="B" /> 
    <level2 key="13" value="B" /> 
    </level1> 
    </root> 
EOF 

node = test_doc.at('//level2') 
puts [*node.ancestors.reverse, node][1..-1].map{ |n| "<#{ n.name }>" } 
# >> <root> 
# >> <level1> 
# >> <level2> 

Nokogiri ist wirklich schön, weil es Ihnen CSS Accessoren können anstelle von XPath, wenn Sie wählen. CSS ist intuitiver zu einigen Leuten, und kann sauberer als eine äquivalente XPath sein:

node = test_doc.at('level2') 
puts [*node.ancestors.reverse, node][1..-1].map{ |n| "<#{ n.name }>" } 
# >> <root> 
# >> <level1> 
# >> <level2> 
Verwandte Themen