Jack Jack - 1 month ago 8
Ruby Question

How to create, read and transform an XML file with Ruby

I am downloading an XML record from Musicbrainz.org, applying an XSLT transformation and outputting a new and different XML record.

I am running into one issue that I wonder if it is a limitation with my approach, XSLT transformations or applying Ruby to text.

I download the record:

require 'open-uri'
mb_metadata = open('http://musicbrainz.org/ws/2/release/?query=barcode:744861082927', 'User-Agent' => 'MarcBrainz marc4brainz@gmail.com').read
File.open('mb_record.xml', 'w').write(mb_metadata)


This works fine.

Then I want to transform that record. First I tried using Nokogiri:

# mb_metadata to transformed record
mb_record = Nokogiri::XML(File.read('mb_record.xml'))

#if we have the xslt document locally this introduces it
template = Nokogiri::XSLT(File.read('mb_to_marc.xsl'))

# this transforms the input document with the template.xslt
puts template.transform(mb_record)


If I run this on its own it works, however if I download the record and then run this it doesn't, it produces a transformed record which just contains some inserts, no element from the original XML file is transformed.

So I thought this might be an issue with Nokogiri and then I tried using the Ruby/XSLT gem:

xslt = XML::XSLT.new()
xslt.xml = 'mb_record.xml'
xslt.xsl = 'mb_to_marc.xsl'

out = xslt.serve()
print out;


Again, if I'm running this on a local file it works, but if I download it and try to transform it it doesn't work - it produces the following error:

xslt.xml = 'mb_record.xml'


Both methods work fine if I just run them on a file which has been downloaded already.

So what's the issue? Is it a naming problem, an XSLT issue, or something else?

Here's the whole script:

#!/usr/bin/env ruby
# encoding: UTF-8
require 'rubygems' if RUBY_VERSION >= '1.9'
require 'pathname'
require 'httpclient'
require 'xml/xslt'
require 'nokogiri'
require 'open-uri'


# DOWNLOAD RECORD FROM MusicBrainz.org - this works
mb_metadata = open('http://musicbrainz.org/ws/2/release/?query=barcode:744861082927', 'User-Agent' => 'MarcBrainz marc4brainz@gmail.com').read
#puts record
File.open('mb_record.xml', 'w').write(mb_metadata)



# mb_metadata to transformed record - this works on a saved file but not if the file is created earlier in this file .
#

#mb_record = Nokogiri::XML(File.read('mb_record.xml'))

#if we have the xslt document locally this introduces it
#template = Nokogiri::XSLT(File.read('mb_to_marc.xsl'))

# this is supposed to transform the input document with the template.xslt
#puts template.transform(mb_record)

# TRYING ANOTHER TACK
# This works if acting on a saved file. i.e. if I comment out the nokogiri lines above and just run the lines below - to 'print out' the xml is correctly transfored by the xslt to produce more xml.
# I added 'sleep 3' to see if that would help but it doesn't make a difference.
xslt = XML::XSLT.new()
xslt.xml = 'mb_record.xml'
xslt.xsl = 'mb_to_marc.xsl'

out = xslt.serve()
print out;

Answer
File.open('mb_record.xml', 'w').write(mb_metadata)

is better written as

File.write('mb_record.xml', mb_metadata)

The first will result in a file that hasn't been closed, and possibly not flushed to the disk, which can mean the file has no contents, or only partial contents.

The second writes the file and immediately flushes and closes it.

Comments