From: Olaf Klischat Date: 2005-07-07T14:30:47+09:00 Subject: [ANN] xml-mapping 0.8 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) Jean Smith
San Mateo CA 94403 2000, Alameda de las Pulgas
San Francisco CA 94102 98765, Fulton street
Stuffed Penguin 10 8.95 Chocolate 5 28.50 Cookie 30 0.85 John Doe product manager Jill Smith clerk Miles O'Brien
---------------------------- 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") => # o.reference => "12343-AHSHE-314159" o.client => #, @home_address=#> 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 => [#, #, #] 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) => ... xml.write($stdout,2) James T. Kirk
San Mateo CA 94403 2000, Alameda de las Pulgas
San Francisco CA 94102 98765, Fulton street
Cookie 30 0.85 Chocolate 5 28.5 Stuffed Penguin 10 8.95 power transfer grid 2 29.95 John Doe product manager Jill Smith clerk Miles O'Brien Harry Smith general manager
####Starting a new order from scratch o = Order.new => # ## 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) Ford Prefect
small planet Betelgeuse system 17263 42 Park Av.
improbability drive 3 299.95
## 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