How to replace XML node contents using Nokogiri

nokogiri, ruby, xml, xml-parsing

Solution

def amend_parent_xml(folder, target_file, new_file)
  Dir["#{@target_folder}/#{folder}/*.xml"]
  .sort.select{|f| !File.directory? f }
  .each do |xml_file|
    doc = Nokogiri.XML( File.read(xml_file) )
    if file = doc.at("//Route//To//Node//Filename[.='#{target_file}']")
      file.content = new_file # set the text of the node
      File.open(xml_file,'w'){ |f| f<<doc }
      break
    end
  end
end

Improvements:

- Use `File.read` instead of `File.open` so that you don't leave a file handle open.

- Uses an XPath expression to find the SINGLE matching node by looking for a node with the correct text value.

- Alternatively you could find all the files and then `if file=files.find{ |f| f.text==target_file }`

- Shows how to serialize a `Nokogiri::XML::Document` back to disk.

- Breaks out of processing the files as soon as it finds a matching XML file.

Problem

I'm using Ruby to read an XML document and update a single node, if it exists, with a new value. http://www.nokogiri.org/tutorials/modifying_an_html_xml_document.html is not obvious to me how to change the node data, let alone how to save it back to the file. ``` def ammend_parent_xml(folder, target_file, new_file) # open parent XML file that contains file reference get_xml_files = Dir.glob("#{@target_folder}/#{folder}/*.xml").sort.select {|f| !File.directory? f} get_xml_files.each { |xml| f = File.open(xml) # Use Nokgiri to read the file into an XML object doc = Nokogiri::XML(f) filename = doc.xpath('//Route//To//Node//FileName') filename.each_with_index { |fl, i| if target_file == fl.text # we found the file, now rename it to new_file # ??????? end } } end ``` This is some example XML: ``` <?xml version="1.0" encoding="utf-8"> <my_id>123</my_id> <Route> <To> <Node> <Filename>file1.txt</Filename> <Filename>file2.mp3</Filename> <Filename>file3.doc</Filename> <Filename>file4.php</Filename> <Filename>file5.jpg</Filename> </Node> </To> </Route> </xml> ``` I want to change "file3.doc" to "file3_new.html". I would call: ``` def ammend_parent_xml("folder_location", "file3.doc", "file3_new.html") ```

Original source