PRANG::Graph - XML mapping by peppering Moose attributes


 # declaring a /language/
 package My::XML::Language;
 use Moose;
 use PRANG::Graph;
 sub xmlns { "some:urn" }
 sub root_element { "Root" }
 with 'PRANG::Graph';
 has_element "data" =>
     is => "ro",
     isa => "My::XML::Language::Node",

 # declaring a /node/ in a language
 package My::XML::Language::Node;
 use Moose;
 use PRANG::Graph;
 has_attr "count" =>
     is => "ro",
     isa => "Num",
 has_element "text" =>
     is => "ro",
     isa => "Str",
     xml_nodeName => "",

 package main;
 # example document for the above.
 my $xml = q[<Root xmlns="some:urn"><data count="2">blah</data></Root>];

 # loading XML to data structures
 my $parsed = My::XML::Language->parse($xml);

 # alternatives
 $parsed = My::XML::Language->parse_file($filename);
 $parsed = My::XML::Language->parse_fh($fh);
 $parsed = My::XML::Language->from_dom($dom); # $dom isa XML::LibXML::Document

 # converting back to XML
 print $parsed->to_xml;


PRANG::Graph allows you to mark attributes on your Moose classes as corresponding to XML attributes and child elements. This allows your class structure to function as an XML graph (a generalized form of an specification for the shape of an XML document; ie, what nodes and attributes are allowed at which point).

PRANG::Graph for document types

If a class implements a document type, that is, it is a valid root node for your language, it must both use and implement the PRANG::Graph role, and implement the required functions xmlns and root_element;

  package XML::Language;
  use Moose;
  use PRANG::Graph;
  sub xmlns { }
  sub root_element { "rootNode" }
  with 'PRANG::Graph';

If no URL is returned by the xmlns function, XML namespaces are not permitted in the input documents.

PRANG::Graph for element types

If a class implements an element, that is, a node which appears somewhere other than the root node of your language, it must use the PRANG::Graph package. Well, actually, it must apply the PRANG class trait, but using the package will also define the useful functions has_attr and has_element, so most users will want that.

The minimum is;

 package XML::Language::SomeElement;
 use PRANG::Graph;

Note, the name of the node is not defined in the class itself, it is defined in the class which includes it, using the has_element method. See PRANG::Graph::Meta::Element for more information.

PRANG::Graph for multi-root document types

If your language has multiple valid root elements, then you must define one document type for each valid root element. These must all implement a particular role, which should bundle the PRANG::Graph role. The common role should not define root_element, but probably should define xmlns in most cases:


  package XML::Language::Family;
  use Moose::Role;
  sub xmlns { }
  with 'PRANG::Graph';

  package XML::Language::One;
  use Moose;
  use PRANG::Graph;
  sub root_element { "one" }
  with 'XML::Language::Family';

  package XML::Language::Two;
  use Moose;
  use PRANG::Graph;
  sub root_element { "two" }
  with 'XML::Language::Family';

PRANG::Graph for plug-in element types

Normally, the details on the node name of elements is in the class which includes those elements, not the target classes. However, it is also possible to refer to roles instead of classes. The first time the parser encounters this, it sees which loaded classes implement that role, and then builds the map from element name to class.

The classes should implement the PRANG::Graph role, effectively defining a document type.

The effect of specifying a role as a type is to immediate search all loaded packages to see which consume the specified role.

Given the example in the previous section, if you used:

 has_element 'some_attr' =>
     is => "ro",
     isa => "XML::Language::Family",

It would be the same as:

 has_element 'some_attr' =>
     is => "ro",
     isa => "XML::Language::One|XML::Language::Two",
     xml_nodeName => {
         one => "XML::Language::One",
         two => "XML::Language::Two",

Otherwise, the requirements are the same as for regular element definition as described in PRANG::Graph::Meta::Element. In particular, two types cannot share the same xmlns and root_element; that would be ambiguous.


These exports are delivered to the class which says use PRANG::Graph;


This is the same as declaring an attribute using the regular Moose has keyword, but adds the PRANG::Attr trait, declared in PRANG::Graph::Meta::Attr - the attribute will behave just like a regular Moose attribute, but the marshalling machinery will convert it to and from a particular XML attribute (or even multiple attributes). See PRANG::Graph::Meta::Attr for more information.


Same as has_attr, except it adds the PRANG::Element trait, indicating to the marshalling machinery to emit an XML node or sequence of nodes. See PRANG::Graph::Meta::Element.


These methods are defined in, or required classes which implement the PRANG::Graph role. In general, that means they are a document type.

parse($class: Str $xml, Bool $lax) returns Object

Parse an XML string according to the PRANG Graph and return the built object. Throws exceptions on error.

By example, this is:

 my $object = $class->parse($xml);

The $lax param tells PRANG to use 'lax' parsing mode. This ignores any extra elements or attributes in the XML that haven't been specified in your definition classes. This is useful if you're parsing XML from a trusted source, but that source may introduce new elements and attributes. Using lax mode, your parser will be forwards compatible for any additions.

Without lax mode, any attributes or elements not defined in your classes will cause PRANG to throw an exception.

to_xml(PRANG::Graph $object: Int $format = 0) returns Str

Converts an object to an XML string. The returned XML will include the XML declaration and so on. Output will be indented by LibXML2 if the format parameter is set to 1. The default is 0 (do not indent). See "toString" in XML::LibXML::Document for other valid values.

By example, this is:

 my $xml = $object->to_xml;

xmlns(Moose::Meta::Class $class:) returns Maybe[Str]

This is a required class method which returns the XML namespace of this document type. This is used for emitting, and when parsing the input namespace must generally be either unset or match this XML namespace.

If you are not using namespaces, just define the method in your class, and return a false value.

encoding() returns Maybe[Str]

Class method that defines the encoding for this class when emitting XML. Defaults to 'UTF-8'.

root_element(Moose::Meta::Class $class:) returns Str

This is a required class method which returns the XML name of the element which corresponds to this document type.


These details are not important for regular use of PRANG, however if you can understand this you will grok the module much more quickly.

This class applies a trait to your classes' metaclass. This means, that when you use PRANG::Graph, there is an implied;

  use Moose -traits => ["PRANG"];

Which is something like;


That sets up the metaclass to be capable of being used by the marshalling machinery. This machinery expects Moose attributes which have the PRANG::Element or PRANG::Attr traits applied to connect XML attributes and elements to object attributes. These are in turn implemented by the PRANG::Graph::Meta::Attr and PRANG::Graph::Meta::Element classes.

Applying the PRANG::Graph role happens separately, and delivers a separate set of super-powers. It is roughly equivalent to;


So, the key difference between these two aspects are the source package, and the destination meta-object; in one, it is the class, in the other, the metaclass.


PRANG, PRANG::Graph::Meta::Class, PRANG::Graph::Meta::Attr, PRANG::Graph::Meta::Element, PRANG::Graph::Node


Development commissioned by NZ Registry Services, and carried out by Catalyst IT -

Copyright 2009, 2010, NZ Registry Services. This module is licensed under the Artistic License v2.0, which permits relicensing under other Free Software licenses.