NAME
Class::XML - Simple XML Abstraction
SYNOPSIS
package Foo;
use base qw/Class::XML/;
__PACKAGE__->has_attributes(qw/length colour/);
__PACKAGE__->has_child('bar' => Bar);
package Bar;
use base qw/Class::XML/;
__PACKAGE__->has_parent('foo');
__PACKAGE__->has_attribute('counter');
# Meanwhile, in another piece of code -
my $foo = Foo->new( xml => # Or filename or ioref or parser
qq!<foo length="3m" colour="pink"><bar /></foo>! );
$foo->length; # Returns "3m"
$foo->colour("purple"); # Sets colour to purple
print $foo; # Outputs <foo length="3m" colour="purple"><bar /></foo>
my $new_bar = new Bar; # Creates empty Bar node
$new_bar->counter("formica");
$foo->bar($new_bar); # Replaces child
$new_bar->foo->colour; # Returns "purple"
$foo->colour(undef); # Deletes colour attribute
print $foo; # Outputs <foo length="3m"><bar counter="formica" /></foo>
DESCRIPTION
Class::XML is designed to make it reasonably easy to create, consume or modify XML from Perl while thinking in terms of Perl objects rather than the available XML APIs; it was written out of a mixture of frustration that JAXB (for Java) and XMLSerializer (for .Net) provided programming capabilities that simply weren't easy to do in Perl with the existing modules, and the sheer pleasure that I've had using Class::DBI.
The aim is to provide a convenient abstraction layer that allows you to put as much of your logic as you like into methods on a class tree, then throw some XML at that tree and get back a tree of objects to work with. It should also be easy to get started with for anybody familiar with Class::DBI (although I doubt you could simply switch them due to the impedance mismatch between XML and relational data) and be pleasant to use from the Template Toolkit.
Finally, all Class::XML objects are also XML::XPath nodes so the full power of XPath is available to you if Class::XML doesn't provide a shortcut to what you're trying to do (but if you find it doesn't on a regular basis, contact me and I'll see if I can fix that ;).
DETAILS
Setup
element_name
__PACKAGE__->element_name('foo');
Sets/gets the default element name for this class. If you don't set it, Class::XML defaults to the last component of the package name - so a class Foo::Bar will by default create 'Bar' elements.
Note that his is *not* necessarily the element name of any given instance - you can override this in the constructor or by calling the XML::XPath::Node::Element setName method. But if you're doing that, presumably you know what you're doing and why ...
has_attribute(s)
__PACKAGE__->has_attribute('attr');
or
__PACKAGE__->has_attributes(qw/attr1 attr2 attr3/);
Creates accessor method(s) for the named attribute(s). Both can be called as many times as you want and will add the specified attributes to the list. Note that setting an attribute to the empty string does *not* delete it - to do that you need to call
$obj->attr( undef );
which will delete the attribute entirely from the object. There's nothing to stop you calling the accessor again later to re-create it though.
Relationships
has_parent
__PACKAGE__->has_parent('foo');
Creates a *read-only* accessor of the specified name that references an instance's parent node in the XML document. Can be specified more than once if you expect the class to be used as a child of more than one different element.
has_child
__PACKAGE__->has_child('name' => 'Class::Name');
Creates an accessor of the specified name that affects a single child node of that name; a runtime exception will be thrown if the instance has more than one child of that name.
When setting you can pass in any object which isa XML::XPath::Node::Element; Class::XML will re-bless it appropriately before it gives you it back later.
has_children
__PACKAGE__->has_children('name' => 'Class::Name');
Functions identically to has_child except the generated accessor returns an array, and can take one to set all such child nodes at once.
has_relation
__PACKAGE__->has_relation('name' => [ '//xpath' => 'Class::Name' ]);
Creates a read-only accessor that returns the nodeset specified by evaluating the given XPath expression with the object as the context node, and returning the results as an array of Class::Name objects.
You can also specify an XPath expression with %s, %i etc. in it; the result will be run through an sprintf on the arguments to the accessor before being used - for example
__PACKAGE__->has_relation('find_person' =>
[ '//person[@name="%s"]' => 'Person' ]);
...
my @ret = $obj->find_person("Barry"); # Evaluates //person[@name="Barry"]
Constructors
new
my $obj = My::Class->new( stuff ... )
Tries to DWIM as much as possible; figures out whether you're asking it to parse something or create an object from scratch and passes it to the appropriate method. This also means that any args to the 'new' methods of either XML::XPath::XMLParser or XML::XPath::Node::Element will both work here in almost all cases.
parse
my $root = My::Class->parse( xml | filename | ioref | parser => source );
All four possible arguments behave pretty much as you'd expect (with the caveat that 'parser' needs to be an XML::Parser object since that's what XML::XPath uses). Returns an object corresponding to the root node of the XML document.
create
my $new = My::Class->create( name?, ns?, { opts }? )
Creates a new instance of the appropriate class from scratch; 'name' if given will override the one stored in element_name, 'ns' is the namespace prefix for the element and 'opts' if given should be a hashref containing name => value pairs for the initial attributes and children of the object.
Searching
search_children
my @res = $obj->search_children( name?, { attr => value, ... }? )
Searches the immediate children of the object for nodes of name 'name' (or any name if not given) with attribute-value pairs matching the supplied hash reference (or all nodes matching the name test if not given). Any child for whose name a has_child or has_children relationship has been declared will be returned as an object of the appropriate class; any other node will be returned as a vanilla XML::XPath::Node::Element object.
Utility
cast
Class::XML::cast($new_class, $obj);
Loads the class specified by $new_class if necessary and then re-blesses $obj into it. Designed for internal use but may come in handy :)
AUTHOR
Matt S Trout <mstrout@cpan.org>
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.