Moose has a very nice method modifier called augment that allows one to use OO inheritance to separate general and specific XML rendering. For instance, if all of your XML must have certain outer wrapper tags, then your base class method can build that XML and call inner(). Using XML::Generator your base class would be:

QBXML( QBXMLMsgsRq( {onError => "stopOnError"}, inner() ));

And a specific inner class to build inner XML might be:

augment 'as_xml' => sub { my ($self, $name)=@_; VendorAddRq( VendorAdd( Name($name))); };

Well, that's a very nice start on taking the directed-graph nature of XML and layering it on the directed-graph nature of class-based oo inheritance.

problem statement

Ok, so what happens when there are numerous optional, nested child nodes? For instance, the code above generates the general wrapper and specific code for the required fields of a Quickbooks VendorAdd request:

<?xml version="1.0" encoding="utf-8"?> <?qbxml version="10.0"?> <QBXML> <QBXMLMsgsRq onError="stopOnError"> <VendorAddRq> <VendorAdd> <!-- required --> <Name >STRTYPE</Name> <!-- required --> </VendorAdd> </VendorAddRq> </QBXMLMsgsRq> </QBXML>
</CODE>

but as you can see, there are numerous optional nodes that are children or grandchildren of the VendorAdd element.

<?xml version="1.0" encoding="utf-8"?> <?qbxml version="10.0"?> <QBXML> <QBXMLMsgsRq onError="stopOnError"> <VendorAddRq> <VendorAdd> <!-- required --> <Name >STRTYPE</Name> <!-- required --> <IsActive >BOOLTYPE</IsActive> <!-- optional --> <CompanyName >STRTYPE</CompanyName> <!-- optional --> <Salutation >STRTYPE</Salutation> <!-- optional --> <FirstName >STRTYPE</FirstName> <!-- optional --> <MiddleName >STRTYPE</MiddleName> <!-- optional --> <LastName >STRTYPE</LastName> <!-- optional --> <VendorAddress> <!-- optional --> <Addr1 >STRTYPE</Addr1> <!-- optional --> <Addr2 >STRTYPE</Addr2> <!-- optional --> <Addr3 >STRTYPE</Addr3> <!-- optional --> <Addr4 >STRTYPE</Addr4> <!-- optional --> <Addr5 >STRTYPE</Addr5> <!-- optional --> <City >STRTYPE</City> <!-- optional --> <State >STRTYPE</State> <!-- optional --> <PostalCode >STRTYPE</PostalCode> <!-- optional --> </VendorAddress> </VendorAdd> </VendorAddRq> </QBXMLMsgsRq> </QBXML>

so the question is

How do you handle conditional, nested generation of XML using object-oriented mechanisms? As I write this, it seems the best way is for these various children to be various Roles (Moose::Role) which you can can call to build the various children, all of which know where in the tree to place themselves, and only place themselves there if they were constructed with renderable data. Ie, if they were null, then they simply dont render.

Practically, if you pull a row from the database, then you want to throw that row at a series of constructors, heedless of whether the values or defined or not and only have the instances render in the tree is the particular data column were defined

CPAN

XML::Toolkit

XML::Toolkit does parse a sample XML file and turn it into a bunch of classes. It is much more typing to build XML than with XML::Generator and it does not appear to subclass the parsed XML into an inheritance tree mimicking the XML structure --- it just makes a class per XML element.

So, any conditional rendering based on data would require tortuous conditionals.

XML::Rabbit

XML::Rabbit is a very neat idea for *consuming* XML. It uses XPath so that each attribute knows where to get itself from. So, if XML::Toolkit had parsed the XML and created a bunch of attributes with XPath saying where it belonged, then you could simply instantiate an object with a hashref of data and only the defined attributes would go about rendering themselves!

XML::Writer::Nest

XML::Writer::Nest is an extension of XML::Writer that basically allows for nested XML to be created by leveraging the automatic destructor call for scalars leaving scope. Thus for the generating XML similar to above we would have:
{ my $VendorAddRq = XML::Writer::Nest->new(tag => 'VendorAddRq'); { my $VendorAdd = $VendorAddRq->nest('VendorAdd'); { my $Name = $VendorAdd->data(Name => $name) } } }
And then for optional nested nodes, we would have nested code which would optionally create objects:
{ my $VendorAdd = $VendorAddRq->nest('VendorAdd'); { my $Name = $VendorAdd->data(Name => $name) } { my $IsActive = $row->{active} ? $VendorAdd->data(IsActive => 1) + : 0 } }

What happens is the Inactive node gets rendered as a function of whether or not the incoming hashref has active set. If it's set, then $IsActive will be an object that is designed to render nested XML. Otherwise, $IsActive is a normal scalar and no XML will be rendered.

XML::Generator

XML::Generator code would be similar. But each conditional rendering would have to be a subroutine which returns empty string or auto-generates XML:
VendorAddRq( VendorAdd( Name($name), maybeRenderIsActive($row) ) )

SUMMARY

perhaps the direct approach of XML::Generator and XML::Writer::Nest is all that is needed? They certainly have a lot less requirements in terms of understanding heavy-duty Moose concepts like Traits. But maybe there a good wedding between the tree that is XML and the tree that is a hiearchy of Moose objects?

For configurable software construction, the problem with XML::Generator and XML::Writer::Nest is separating XML construction from the boolean function to decide whether to render. In other words, each user of your nested optional XML "specification" should be able to hook in their desired subroutines for deciding which nodes should render. Thus, the above XML::Generator really should look like this:

augment 'as_xml' => sub { my ($self, $name, $optionaldata)=@_; VendorAddRq( VendorAdd( Name($name), $self->logic_engine->maybeRenderIsActive(@_) # just give + it the whole indata ) )
And the programmer can override the default logic_engine with methods based on the data and business requirements. For instance, the default logic_engine might look like this:
package XML::Quickbooks::LogicEngine; sub IsActive { my ($self, $name, $databaserow)=@_; $databaserow->{active}; } 1;
And then a developer has the choice of mapping his hashref to the same values as the logic engine or supplying his own logic engine.



The mantra of every experienced web application developer is the same: thou shalt separate business logic from display. Ironically, almost all template engines allow violation of this separation principle, which is the very impetus for HTML template engine development.

-- Terence Parr, "Enforcing Strict Model View Separation in Template Engines"


In reply to Moose, the tree structure of XML and object-oriented inheritance hiearchies by metaperl

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.