Bugz has asked for the wisdom of the Perl Monks concerning the following question:

Hi all, I am interested in writing a method that takes input as a hash and creates/appends to an xml file. The idea is everytime a hash reference is passed to the method when the method is called, it must add a child node to the xml node. ie. for the first time, it must create a xml file with the the following nodes
<a> <b> <c> <d> <e> </b> </a>

The next time the method's called. If the file's existent, it must append the new hash to the existing xml file. It must have the following structure:
<a> <b> <c> <d> <e> </b> <b> <c> <d> <e> </b> . . </a>

I understand XML::WRITER can help you create such a tree, but I think I would also have to parse the file if existent and then append to it. Could someone tell me how so do i use it to create the file and then just parse it and append child nodes to existing XML file whenever the method's called.
Thanks

Replies are listed 'Best First'.
Re: Perl Hash to XML
by dHarry (Abbot) on Aug 18, 2008 at 19:21 UTC

    Recently, my attention was drawn to XML::Twig by other monks. I have been using it a lot since. I am pretty sure it can solve your problem. All you need to do is write a little logic around it. But there are more/many ways leading to Rome of course. I have no experience with XML:Writer. From a quick look at CPAN I think it is well possible to write XML files, I am not sure about the updating part though.

      you said XML twig allows it, but how?
      The idea is to check if there's a root and if there's a root, then add the child node to it and then the data nodes as child nodes to that node.

      How would i achieve this using Twig, i checked it's tutorial and my head's spinning faster than a top.

      I dun mind using XML::SIMPLE, XML::WRITER, XML::TWIG, XML::SMART, XML::DUMPER...anything that lets me create a XML file and then open it at a later instance to add another node.
Re: Perl Hash to XML
by grantm (Parson) on Aug 18, 2008 at 21:17 UTC

    A handy trick that mirod pointed out is to use two files. The first contains your enclosing <a> elements and uses a DTD to define a named entity that refers to the second file. The second file contains just the series of <b> elements with their contents, and no enclosing top level element.. E.g.:

    <!DOCTYPE doc [<!ENTITY real_doc SYSTEM "/path/to/real/file.xml">]><a> +&real_doc;</a>

    This allows you to append to the second file without having to worry about the encolsing element tags.

Re: Perl Hash to XML
by Your Mother (Archbishop) on Aug 18, 2008 at 22:21 UTC

    I don't know if I'd like to solve it this way in production but this works. I prefer XML::LibXML for XML stuff but it doesn't do marshalling with Perl data so we bring in XML::Simple which I don't generally recommend but for simple, predictable data it can be nice.

    use strict; use warnings; use XML::LibXML; use XML::Simple; use constant XML_STORAGE => "./whatever.xml"; my %data = ( d20 => int(rand(20) + 1), time => time(), ); save_data_as_xml(\%data); # Take a look. open my $xml_fh, "<", XML_STORAGE or die $!; print while <$xml_fh>; close $xml_fh; sub save_data_as_xml { my $data = shift || return; my $doc = _get_or_initialize(); my $new_raw_xml = XML::Simple::XMLout($data); my $parser = XML::LibXML->new(); $parser->recover(1); my $new_node = $parser->parse_string($new_raw_xml); my $root = $doc->documentElement(); $root->appendChild( $new_node->documentElement() ); $root->appendChild( $doc->createTextNode("\n") ); _save($doc); } sub _get_or_initialize { my $parser = XML::LibXML->new(); my $doc = eval { $parser->parse_file(XML_STORAGE) }; warn $@ if $@; return $doc if $doc; return $parser->parse_string("<container/>"); } sub _save { my $doc = shift; $doc->toFile(XML_STORAGE); }
Re: Perl Hash to XML
by Perlbotics (Archbishop) on Aug 18, 2008 at 19:44 UTC
    If you can sacrifice your requirement to append to an existing file, than XML::Simple can do it. Might be even saver to work with two files for smaller sets of data (XMLin file / modify data in memory / XMLout new file)?
      so if i got it right, you are suggesting that i copy contents from an existing file, and push it into a new file(new by name of just replacing the old one) along with the additional data i am pushing in this time. :)

      sure XML::SIMPLE would do that, but there's ought to be a simpler way. right?
        It depends... Do you mean simple in the sense of performance? Well, who says that your existing XML file has to be treated as such (parsed and validated)? Seek to the end of the file, before the </a>, add another <b>...</b></a> sequence and done. But I guess, you want a little bit more than that...and a little bit more robustness?
        The previous suggestion is simple to program, but surely not the fastest way to append a node to a (potentially large) file.
Re: Perl Hash to XML
by Anonymous Monk on Aug 19, 2008 at 18:30 UTC