[ANN] xml-mapping 0.8

O

Olaf Klischat

Project Homepage: http://xml-mapping.rubyforge.org/
Download, CVS etc.: http://rubyforge.org/projects/xml-mapping


xml-mapping is an easy to use, extensible library for mapping Ruby
objects to XML trees and back. It includes an XPath interpreter (see
below).


===========================
Example:

----------------------------
Input document:
----------------------------
(example document stolen + extended from
www.castor.org/xml-mapping.html)

<?xml version="1.0" encoding="ISO-8859-1"?>

<order reference="12343-AHSHE-314159">
<client>
<name>Jean Smith</name>
<address where="home">
<city>San Mateo</city>
<state>CA</state>
<zip>94403</zip>
<street>2000, Alameda de las Pulgas</street>
</address>
<address where="work">
<city>San Francisco</city>
<state>CA</state>
<zip>94102</zip>
<street>98765, Fulton street</street>
</address>
</client>

<item reference="RF-0001">
<description>Stuffed Penguin</description>
<quantity>10</quantity>
<unit-price>8.95</unit-price>
</item>

<item reference="RF-0034">
<description>Chocolate</description>
<quantity>5</quantity>
<unit-price>28.50</unit-price>
</item>

<item reference="RF-3341">
<description>Cookie</description>
<quantity>30</quantity>
<unit-price>0.85</unit-price>
</item>

<signed-by>
<signature>
<name>John Doe</name>
<position>product manager</position>
</signature>

<signature>
<name>Jill Smith</name>
<position>clerk</position>
</signature>

<signature>
<name>Miles O'Brien</name>
</signature>
</signed-by>

</order>


----------------------------
Mapping class declarations:
----------------------------

require 'xml/mapping'

## forward declarations
class Client; end
class Address; end
class Item; end
class Signature; end


class Order
include XML::Mapping

text_node :reference, "@reference"
object_node :client, "client", :class=>Client
hash_node :items, "item", "@reference", :class=>Item
array_node :signatures, "signed-by", "signature", :class=>Signature, :default_value=>[]

def total_price
items.values.map{|i| i.total_price}.inject(0){|x,y|x+y}
end
end


class Client
include XML::Mapping

text_node :name, "name"
object_node :home_address, "address[@where='home']", :class=>Address
object_node :work_address, "address[@where='work']", :class=>Address, :default_value=>nil
end


class Address
include XML::Mapping

text_node :city, "city"
text_node :state, "state"
numeric_node :zip, "zip"
text_node :street, "street"
end


class Item
include XML::Mapping

text_node :descr, "description"
numeric_node :quantity, "quantity"
numeric_node :unit_price, "unit-price"

def total_price
quantity*unit_price
end
end


class Signature
include XML::Mapping

text_node :name, "name"
text_node :position, "position", :default_value=>"Some Employee"
end



----------------------------
Usage
----------------------------

####read access
o=Order.load_from_file("order.xml")
=> #<Order:0x40461390 @signatures=[#<Signature[....] @reference="12343-AHSHE-314159">
o.reference
=> "12343-AHSHE-314159"
o.client
=> #<Client:0x404609e0 @name="Jean Smith", @work_address=#<Address:0x4045de5c @city="San Francisco", @street="98765, Fulton street", @zip=94102, @state="CA">, @home_address=#<Address:0x4045fd10 @city="San Mateo", @street="2000, Alameda de las Pulgas", @zip=94403, @state="CA">>
o.items.keys
=> ["RF-3341", "RF-0034", "RF-0001"]
o.items["RF-0034"].descr
=> "Chocolate"
o.items["RF-0034"].total_price
=> 142.5
o.signatures
=> [#<Signature:0x40456f30 @name="John Doe", @position="product manager">, #<Signature:0x40456314 @name="Jill Smith", @position="clerk">, #<Signature:0x404556f8 @name="Miles O'Brien", @position="Some Employee">]
o.signatures[2].name
=> "Miles O'Brien"
o.signatures[2].position
=> "Some Employee"
## default value was set

o.total_price
=> 257.5


####write access
o.client.name="James T. Kirk"
o.items['RF-4711'] = Item.new
o.items['RF-4711'].descr = 'power transfer grid'
o.items['RF-4711'].quantity = 2
o.items['RF-4711'].unit_price = 29.95

s=Signature.new
s.name='Harry Smith'
s.position='general manager'
o.signatures << s
xml=o.save_to_xml #convert to REXML node; there's also o.save_to_file(name)
=> <order reference='12343-AHSHE-314159'> ... </>
xml.write($stdout,2)
<order reference='12343-AHSHE-314159'>
<client>
<name>James T. Kirk</name>
<address where='home'>
<city>San Mateo</city>
<state>CA</state>
<zip>94403</zip>
<street>2000, Alameda de las Pulgas</street>
</address>
<address where='work'>
<city>San Francisco</city>
<state>CA</state>
<zip>94102</zip>
<street>98765, Fulton street</street>
</address>
</client>
<item reference='RF-3341'>
<description>Cookie</description>
<quantity>30</quantity>
<unit-price>0.85</unit-price>
</item>
<item reference='RF-0034'>
<description>Chocolate</description>
<quantity>5</quantity>
<unit-price>28.5</unit-price>
</item>
<item reference='RF-0001'>
<description>Stuffed Penguin</description>
<quantity>10</quantity>
<unit-price>8.95</unit-price>
</item>
<item reference='RF-4711'>
<description>power transfer grid</description>
<quantity>2</quantity>
<unit-price>29.95</unit-price>
</item>
<signed-by>
<signature>
<name>John Doe</name>
<position>product manager</position>
</signature>
<signature>
<name>Jill Smith</name>
<position>clerk</position>
</signature>
<signature>
<name>Miles O&apos;Brien</name>
</signature>
<signature>
<name>Harry Smith</name>
<position>general manager</position>
</signature>
</signed-by>
</order>



####Starting a new order from scratch
o = Order.new
=> #<Order:0x4043c9f0 @signatures=[]>
## attributes with default values (here: signatures) are set
## automatically

xml=o.save_to_xml
XML::MappingError: no value, and no default value, for attribute: reference
from ../../lib/xml/../xml/mapping/base.rb:377:in `obj_to_xml'
from ../../lib/xml/../xml/mapping/base.rb:157:in `fill_into_xml'
from ../../lib/xml/../xml/mapping/base.rb:156:in `each'
from ../../lib/xml/../xml/mapping/base.rb:156:in `fill_into_xml'
from ../../lib/xml/../xml/mapping/base.rb:168:in `save_to_xml'
## can't save as long as there are still unset attributes without
## default values

o.reference = "FOOBAR-1234"

o.client = Client.new
o.client.name = 'Ford Prefect'
o.client.home_address = Address.new
o.client.home_address.street = '42 Park Av.'
o.client.home_address.city = 'small planet'
o.client.home_address.zip = 17263
o.client.home_address.state = 'Betelgeuse system'

o.items={'XY-42' => Item.new}
o.items['XY-42'].descr = 'improbability drive'
o.items['XY-42'].quantity = 3
o.items['XY-42'].unit_price = 299.95

o.save_to_xml.write($stdout,2)

<order reference='FOOBAR-1234'>
<client>
<name>Ford Prefect</name>
<address where='home'>
<city>small planet</city>
<state>Betelgeuse system</state>
<zip>17263</zip>
<street>42 Park Av.</street>
</address>
</client>
<item reference='XY-42'>
<description>improbability drive</description>
<quantity>3</quantity>
<unit-price>299.95</unit-price>
</item>
</order>
## the root element name when saving an object to XML will by default
## be derived from the class name (in this example, "Order" became
## "order"). This can be overridden on a per-class basis; see
## XML::Mapping::ClassMethods#root_element_namefor details.



=======

As shown in the example, you generally include XML::Mapping in a class
to turn it into a "mapping class", and then map instance attributes to
XML sub-trees at will. XPath expressions like "@reference",
"signed-by", or "address[@where='home']" in the mapping class
definition locate the sub-trees; an XPath interpreter is bundled with
xml-mapping for reading and writing data from/to them.

The XPath interpreter can also be used on its own; it does not depend
on the rest of the library.

Several "node types" like text_node, numeric_node, array_node etc. are
provided; it is easy to write your own ones and register them with the
xml-mapping library. There is even documentation on all this :)=

Olaf
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top