2015-02-03 12 views
6

Come è possibile aggiungere uno Nokogiri::XML::Element a un documento XML che viene creato con Nokogiri::XML::Buider?Aggiunta di un elemento XML a un documento Nokogiri :: XML :: Builder

La mia soluzione attuale è serializzare l'elemento e utilizzare il metodo << per reinterpretarlo.

orig_doc = Nokogiri::XML('<root xmlns="foobar"><a>test</a></root>') 
node = orig_doc.at('/*/*[1]') 

puts Nokogiri::XML::Builder.new do |doc| 
    doc.another { 
     # FIXME: this is the round-trip I would like to avoid 
     xml_text = node.to_xml(:skip_instruct => true).to_s 
     doc << xml_text 

     doc.second("hi") 
    } 
end.to_xml 

# The expected result is 
# 
# <another> 
# <a xmlns="foobar">test</a> 
# <second>hi</second> 
# </another> 

Tuttavia il Nokogiri::XML::Element è piuttosto grande nodo (nell'ordine di kilobyte e migliaia di nodi) e questo codice è nel percorso caldo. La creazione di profili mostra che il giro di serializzazione/analisi è molto costoso.

Come posso chiedere a Nokogiri Builder di aggiungere l'elemento XML esistente node nella posizione "corrente"?

risposta

6

Senza l'utilizzo di un metodo privato è possibile ottenere una maniglia su l'elemento principale corrente utilizzando the parent method of the Builder esempio. Quindi puoi aggiungere un elemento a questo (anche da un altro documento). Ad esempio:

require 'nokogiri' 
doc1 = Nokogiri.XML('<r><a>success!</a></r>') 
a = doc1.at('a') 

# note that `xml` is not a Nokogiri::XML::Document, 
# but rather a Nokogiri::XML::Builder instance. 
doc2 = Nokogiri::XML::Builder.new do |xml| 
    xml.some do 
    xml.more do 
     xml.parent << a 
    end 
    end 
end.doc 

puts doc2 
#=> <?xml version="1.0"?> 
#=> <some> 
#=> <more> 
#=>  <a>success!</a> 
#=> </more> 
#=> </some> 
+0

Questo è molto meglio del mio kludge con '# insert'. – gioele

3

Dopo aver esaminato la fonte Nokogiri ho trovato questa soluzione fragile: utilizzando il metodo protetto #insert(node).

Il codice, modificato per utilizzare tale metodo privato assomiglia a questo:

doc.another { 
    xml_text = node.to_xml(:skip_instruct => true).to_s 
    doc.send('insert', xml_text) # <= use `#insert` instead of `<<` 

    doc.second("hi") 
}