Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Comment on

( #3333=superdoc: print w/ replies, xml ) Need Help??

This is a continuation to my previous (RFC) XML::TransformRules node. The first version of the module, for now without support for namespaces and processing instructions (and comments - you might want to process even those in some cases) may be found at my site.

A few weeks ago someone compared XML to Lisp. I can't find the node now, but it was something about how you could just transform the XML to Lisp and let it execute to produce the result you need. This forced me to think ... if we can transform the XML to Lisp, we can just as well transform it to Perl:

<root> <foo x="5"> <bar>hello</bar> <baz>world</baz> </foo> </root> => root( foo( x => 5, bar("hello"), baz("world") ) )
but the question is whether we would gain anything. Of course it would be silly to convert the whole XML to Perl code and then eval("") it, we can instead execute the subroutines as we parse the closing tag and just remember the results so that we could pass them to the subroutine for the parent tag.

I tried to come up with a few examples of things I might want to do with a XML and tried to implement them in this style and I like the results. Maybe it's my functional programming affected brain, but I find this style convenient.

$xml = <<'*END*'; <doc> <person> <fname>Jane</fname> <lname>Luser</lname> <email>JLuser@bogus.com</email> <address> <street>Washington st.</street> <city>Old Creek</city> <country>The US</country> <bogus>bleargh</bogus> </address> <phones> <phone type="home">123-456-7890</phone> <phone type="office">663-486-7890</phone> <phone type="fax">663-486-7000</phone> </phones> </person> <person> <fname>John</fname> <lname>Other</lname> <email>JOther@silly.com</email> <address> <street>Grant's st.</street> <city>New Creek</city> <country>Canada</country> <bogus>sdrysdfgtyh degtrhy <foo>degtrhy werthy</foo>werthy drthyu</ +bogus> </address> <phones> <phone type="office">663-486-7891</phone> </phones> </person> </doc> *END* %rules = ( _default => 'content', bogus => undef, # means "ignore" address => sub {address => "$_[1]->{street}, $_[1]->{city} ($_[1]- +>{country})"}, person => sub { '@person' => "$_[1]->{lname}, $_[1]->{fname}\n<$_[1]->{email}> +\n$_[1]->{address}" }, doc => sub { join "\n\n", @{$_[1]->{person}} }, ); # $parser->parse() will return a single string containin the addresses + # in plain text format %rules = ( _default => 'content', # bogus => sub {}, # means "returns no value. The subtags ARE proce +ssed. bogus => undef, # means "ignore". The subtags ARE NOT processed. address => 'no content', person => 'no content array', doc => sub {$_[1]->{person}}, #'pass no content', foo => sub {print "FOOOOOOOO\n"}, ); # returns a simplified data structure kinda similar to XML::Simple my $parser = new XML::Rules ( rules => [ _default => sub {$_[0] => $_[1]->{_content}}, 'fname,lname' => sub {$_[0] => $_[1]->{_content}}, bogus => undef, address => sub {address => "$_[1]->{street}, $_[1]->{city} ($_ +[1]->{country})"}, phone => sub {$_[1]->{type} => $_[1]->{_content}}, # let's use the "type" attribute as the key and the conten +t as the value phones => sub {delete $_[1]->{_content}; %{$_[1]}}, # remove the text content and pass along the type => conte +nt from the child nodes person => sub { # lets print the values, all the data is readi +ly available in the attributes print "$_[1]->{lname}, $_[1]->{fname} <$_[1]->{email}>\n"; print "Home phone: $_[1]->{home}\n" if $_[1]->{home}; print "Office phone: $_[1]->{office}\n" if $_[1]->{office} +; print "Fax: $_[1]->{fax}\n" if $_[1]->{fax}; print "$_[1]->{address}\n\n"; return; # the <person> tag is processed, no need to rememb +er what it contained }, ] ); # prints the addresses, returns nothing
As you can see the rules applied to the parsed tags are basicaly of two types. Either they specify what data gets passed to the parent tag's rule and how or they do something with the attributes of the tag and the data returned by the rules of subtags. You can of course do both in your rules. For example if the XML looked like this:
<doc> <person> <fname>Jane</fname> <lname>Luser</lname> <email>JLuser@bogus.com</email> <address> <street>Washington st.</street> <city>Old Creek</city> <country>The US</country> <bogus>bleargh</bogus> </address> <phones> <phone type="home">123-456-7890</phone> <phone type="office">663-486-7890</phone> <phone type="fax">663-486-7000</phone> </phones> </person> <person> <fname>John</fname> <lname>Other</lname> <email>JOther@silly.com</email> <address id="12345"/> <phones> <phone type="office">663-486-7891</phone> </phones> </person> </doc>
You might use a subroutine like this for the <address> tag:
sub { if (exists $_[1]->{id} and $_[1]->{id}+0 > 0) { $get_addr->execute($_[1]->{id}); my $result = $get_addr->fetchall_arrayref(); my ($street, $sity, $country) = ( $result->[0][0], $result->[0][1], +$result->[0][2]); return address => "$_[1]->{street}, $_[1]->{city} ($_[1]->{country}) +" } else { return address => "$_[1]->{street}, $_[1]->{city} ($_[1]->{country}) +" } }
and proceed as if the data was directly in the XML in all cases.

Let me know please what you think. I'd also be grateful for any suggestions regarding the support for XML namespaces.

Update 2006-11-07: I just uploaded an updated version of the module, with more tests and "start tag" rules allowing you to skip branches of XML if you can decide based on the tag's attribute that you do not need them. I also uploaded the module to CPAN.


In reply to (RFC) XML::Rules - yet another XML parser by Jenda

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!
  • 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, 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, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • Outside of code tags, you may need to use entities for some characters:
            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.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others pondering the Monastery: (10)
    As of 2014-07-23 21:21 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      My favorite superfluous repetitious redundant duplicative phrase is:









      Results (152 votes), past polls