[ANN] xml-mapping 0.8

Discussion in 'Ruby' started by Olaf Klischat, Jul 7, 2005.

  1. 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
    Olaf Klischat, Jul 7, 2005
    #1
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Stylus Studio
    Replies:
    0
    Views:
    341
    Stylus Studio
    Jul 6, 2004
  2. Bomb Diggy
    Replies:
    0
    Views:
    435
    Bomb Diggy
    Jul 28, 2004
  3. Replies:
    0
    Views:
    339
  4. Stylus Studio
    Replies:
    0
    Views:
    337
    Stylus Studio
    Jul 2, 2004
  5. Replies:
    0
    Views:
    359
Loading...

Share This Page