3 Replies - 2925 Views - Last Post: 19 December 2011 - 01:26 PM Rate Topic: -----

#1 NotarySojac  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 53
  • View blog
  • Posts: 428
  • Joined: 30-September 10

Hash to XML, standard gem/method?

Posted 12 December 2011 - 05:07 PM

Hey, I recently had a project where I needed to create an XML file out of data that I put into a hash. I'm proud to say that I finished the class to do just that! I did google searching and was having trouble getting any of the standardized gems to actually go from a hash to an XML file, so I figured since I'm still skilling up with ruby that I would do the code myself before asking here. With that said, should I think about publishing my code as a gem, or is there a standard gem already out there?

Here's what I needed for my project:

Input
data = { "my_tag_one" => "Rats off to ya!", "pril" => "tooth paste", "gambit" => "contents here"}




Output
<?xml version="1.0" encoding="iso-8859-1"?>
<contents>
  <content id="my_tag_one">
    <string>
      Rats off to ya!
    </string>
  </content>
  <content id="pril">
    <string>
      tooth_paste
    </string>
  </content>
  <content id="gambit">
    <string>
      contents here
    </string>
  </content>
</contents>



The code it took to accomplish this was:
htx = HashToXml.new
htx.mode = :basic   # expects a data hash like { "id" => "value" }
# htx.mode = :advanced   # expects data hash like { "id" => { "child_element_or_attribute1" => "some value", "child_element_or_attribute2" => "value for second child element" }

htx.parents = ["contents"] # allows you to specify parent nodes
htx.element = "content"    # specify the element name

htx.element_attributes = { "id" => :key }   # specify any attributes
                           # This allows us to specify the attribute to
                           # be named "id" and have it's value be
                           # :key which points to the key in the data hash

htx.child_elements = ["string"]  # if it were in advanced mode... you'd have more.  But not allowed in basic
#htx.child_attributes = nil
htx.child_values = [:value]
data = htx.go(data)

puts data



Rubish or rubyish? I come from a C# background, so I might be misusing hashes completely, and coming at it in an odd way =/

Here's the class (probably a little more bloated and erratic due to my noviceship):
class HashToXml
  attr_accessor :mode, :parents, :element, :value, :element_attributes, :child_elements, :child_values
  
  def go(data)
    case @mode
    when :basic
      #puts "basic expects the data hash to be a simple series of key value pairs."
      return basic(data)
    end
  end
  
  private
  
  def basic(data)
    output_string = ""
    header = '<?xml version="1.0" encoding="iso-8859-1"?>'
    output_string += header + "\n"
    output_string << open_parents
    
    attribs = get_attributes(data)
    
    output_string << compile_elements(data, attribs) # attribs = [" id="kara", " id="rockefeller"]
      
    output_string << close_parents
    return output_string
  end
  
  def compile_elements(data, attribs=nil)
    ret_string = ""
    tabs = (@parents.length).times.map{"  "}.join
    
    i = 0
    data.each do |key, val|
      
      @child_elements.each do |child_elements|
        ret_string << tabs + "<#{@element}#{attribs[i]}>\n"
        ret_string << tabs + "  " + "<#{child_elements}>\n"
        ret_string << tabs + "    " + val + "\n"
        ret_string << tabs + "  " + "</#{child_elements}>\n"
        ret_string << tabs + "</#{@element}>\n"
      end
      i += 1
    end
    
    return ret_string
  end
  
  # This function will figure out what the attributes payload will be for each 
  # element all at once.  
  def get_attributes(data)
    ret_array = []
    i = 0
    
    data.each do |key, val|  # for each data pair in the data population
      
      # only tested for it iterating once...
      @element_attributes.each do |attribute_name, attribute_pointer|  # for each element pair in the specifier
        tag_name = key if attribute_name == :key
        tag_name = val if attribute_name == :value
        tag_name = attribute_name if attribute_name.is_a?(String)
        raise 'element_attributes was set improperly...' if tag_name.nil?
        
        value = key if attribute_pointer == :key 
        value = val if attribute_pointer == :value
        value = attribute_pointer if attribute_pointer.is_a?(String)   #untested
        raise 'element_attributes was set improperly...' if value.nil?

        ret_array << " #{tag_name}=\"#{value}\""
      end
      
      #puts ret_string
    end
    ret_array = nil if ret_array.empty? # untested
    return ret_array
  end
  
  def open_parents
    ret_string = ""
    i = 0
    @parents.each do |parent|
      tabs = i.times.map { "  " }.join
      ret_string += tabs + "<#{parent}>\n"
      i += 1
    end
    return ret_string
  end
  
  def close_parents
    ret_string = ""
    i = @parents.length-1
    
    @parents.reverse.each do |parent|
      tabs = i.times.map { "  " }.join
      ret_string += tabs + "</#{parent}>\n"
      i -= 1
    end
    return ret_string
  end
end



The class is very much unfinished since I only needed it in one very narrow test case. If you guys think the ruby world needs it though, I'd start a gem and begin writing up test cases and all that.

This post has been edited by NotarySojac: 12 December 2011 - 05:12 PM


Is This A Good Question/Topic? 0
  • +

Replies To: Hash to XML, standard gem/method?

#2 NotarySojac  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 53
  • View blog
  • Posts: 428
  • Joined: 30-September 10

Re: Hash to XML, standard gem/method?

Posted 12 December 2011 - 05:56 PM

gah! I just found http://builder.rubyforge.org/ after googling [how to escape characters to xml] instead of [how to convert hashes to xml]... This builder all but obviates my class =/
Was This Post Helpful? 0
  • +
  • -

#3 Karel-Lodewijk  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 449
  • View blog
  • Posts: 849
  • Joined: 17-March 11

Re: Hash to XML, standard gem/method?

Posted 13 December 2011 - 04:28 AM

I like nokogiri both for parsing xml as creating it

require 'nokogiri'

data = { "my_tag_one" => "Rats off to ya!", "pril" => "tooth paste", "gambit" => "contents here"}

builder = Nokogiri::XML::Builder.new do
    contents {
        data.each {|key,value|
            content(:id => key) {
                string {
                    text value
                }
            }
        }
    }
end


puts builder.to_xml()



It even looks a little like xml.

As for the parsing part, I like to just throw xpath expressions at it, which nokogiri supports excellently. And nokogiri is not to picky about formatting, so it is useful even when dealing with poorly formatted html.

This is offcourse if you don't need anythong high performance, that is probably another story.

This post has been edited by Karel-Lodewijk: 13 December 2011 - 04:29 AM

Was This Post Helpful? 1
  • +
  • -

#4 noahgibbs  Icon User is offline

  • New D.I.C Head

Reputation: 8
  • View blog
  • Posts: 17
  • Joined: 18-December 11

Re: Hash to XML, standard gem/method?

Posted 19 December 2011 - 01:26 PM

If you look at the multi_xml gem, you can see what the standard Ruby back-ends are for this, and use whichever one you want with the same simple interface.

There's a similar multi_json gem to do the same thing in JSON.
Was This Post Helpful? 1
  • +
  • -

Page 1 of 1