Xapper - another XML mapper for Ruby

A simple mapper that takes a mapping and converts your ugly-XML to a lovely-ruby hash.

Install

Use Bundler + git, add this to your Gemfile

gem "xapper"

In your code

require "xapper"

Usage

Setup

Get a mapper object

mapper = Xapper::Mapper.new

And some hopefully less contrived XML

<response type="book_list">
  <title>A list of books on my shelf</title>
  <books>
    <book author="Boris Akunin">
      <title>The Winter Queen</title>
    </book>
    <book author="Neil Gaiman">
      <title>Neverwhere</title>
    </book>
    <book author="Hermann Hesse">
      <title>Steppenwolf</title>
    </book>
  </books>
</response>

Mapping text values and attributes using xpath

mapper.mappings = {
  :type   => "/response/@type",
  :title  => "/response/title"
}

data = mapper.map(xml)

data[:type]  #=> "book_list"
data[:title] #=> "A list of books on my shelf"

Mapping lists of XML nodes into arrays of hashes

mapper.mappings = {
  :books => ["/response/books/book", {
    :title  => "title",
    :author => "@author"
  }]
}

data = mapper.map(xml)

data[:books].size        #=> 3
data[:books][0][:title]  #=> "The Winter Queen"
data[:books][0][:author] #=> "Boris Akunin"

Using a lambda to convert values

You can map via a lambda, the current Nokogiri node is passed as an argument

mapper.mappings = {
  :books => ["/response/books/book", {
    :title  => "title",
    :author => {
      :name => "@author",
      :wiki => lambda do |node|
        "http://en.wikipedia.org/wiki/" + node.attribute('author').value.gsub(" ", "_")
      end
    }
  }]
}

data = mapper.map(xml)
data[:books][0][:author][:wiki] #=> "http://en.wikipedia.org/wiki/Boris_Akunin"

Using namespaces

You can map XML namespaces, the mapper just needs to know about them first

xml = %q{
  <root xmlns:h="http://www.w3.org/TR/html4/" xmlns:f="http://www.w3schools.com/furniture">
    <h:table>
      <h:tr>
        <h:td>Apples</h:td>
        <h:td>Bananas</h:td>
      </h:tr>
    </h:table>
    <f:table>
      <f:name>African Coffee Table</f:name>
      <f:width>80</f:width>
      <f:length>120</f:length>
    </f:table>
  </root>
}

mapper.mappings = {
  :html_table_rows => ["/root/h:table/h:tr/h:td", { :text => "." }],
  :an_actual_table => {
    :name   => "/root/f:table/f:name",
    :width  => "/root/f:table/f:width",
    :length => "/root/f:table/f:length"
  }
}

mapper.namespaces = {
  "h" => "http://www.w3.org/TR/html4/",
  "f" => "http://www.w3schools.com/furniture"
}

data = mapper.map(xml)

data[:html_table_rows].size      # => 2
data[:html_table_rows][0][:text] # => "Apples"

data[:an_actual_table][:name]    # => "African Coffee Table"
data[:an_actual_table][:width]   # => "80"

Examples

See examples/*.rb, run with rake examples

Development

Please send new code in the form of a pull requests with tests. Run the current test suite with ...

rake spec         # Runs spec/*_spec.rb

Contributors: