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

Thank You for your replies monks, I was getting the hash as an input from a file with incorrect structuring. Anyways, the structure was corrected and I am now getting the Hash structure as
$FIELDS = { 'AA' => { 'Description' => '', 'Type' => 'Module', 'File' => 'Sample1.pl', }, 'BB' => { 'Description' => 'Initiator', 'Type' => 'Methods', 'File' => 'Sample1.pl', }, 'CC' => { 'Description' => 'Destructor', 'Type' => 'Methods', 'File' => 'Sample2.pl', 'Values' => { '1' => 'Ignore', '2' => 'Retry', '3' => 'Abort' } } },
Using XML::Simple, I was able to convert it to basic XML as follows
<?xml version = "1.0"?> <data> <AA> <Description></Description> <Type>Module</Type> <File>Sample1.pl</File> </AA> <BB> <Description>Initiator</Description> <Type>Methods</Type> <File>Sample1.pl</File> </BB> <CC> <Description>Destructor</Description> <Type>Methods</Type> <File>Sample2.pl</File> <Value> <1>Ignore</1> <2>Retry</2> <3>Abort</3> </Value> </C> </data>
As you can see the XML tags <1> <2> <3> would be illegal, so I want to convert the data to something like following:
<?xml version = "1.0"?> <data> <Node No='1' Label='AA'> <Description></Description> <Type>Module</Type> <File>Sample1.pl</File> </Node> <Node No= '2' Label='BB'> <Description>Initiator</Description> <Type>Methods</Type> <File>Sample1.pl</File> </Node> <Node No='3' Label='CC'> <Description>Destructor</Description> <Type>Methods</Type> <File>Sample2.pl</File> <Value No='1'>Ignore</Value> <Value No='2'>Retry</Value> <Value No='3'>Abort</Value> </Node> </data>
The idea to introduce numbers with nodes, was to ensure, two nodes with same name can exist. I am unsure of how XML::Writer or XML::Twig could be able to parse the Perl Hash into the XML structure I need. Any Suggestions please?

Replies are listed 'Best First'.
Re: Perl and XML
by shmem (Chancellor) on Apr 29, 2008 at 17:26 UTC

    Reading the XML::Simple man page helps. Write your expected xml to a file, say, foo.xml, then read and dump it with a script named foo:

    use XML::Simple; use Data::Dumper; $Data::Dumper::Indent = 1; my $config = XMLin(); print Dumper($config);

    Voilà:

    $VAR1 = { 'Node' => [ { 'Type' => 'Module', 'No' => '1', 'File' => 'Sample1.pl', 'Description' => {}, 'Label' => 'AA' }, { 'Type' => 'Methods', 'No' => '2', 'File' => 'Sample1.pl', 'Description' => 'Initiator', 'Label' => 'BB' }, { 'Value' => [ { 'No' => '1', 'content' => 'Ignore' }, { 'No' => '2', 'content' => 'Retry' }, { 'No' => '3', 'content' => 'Abort' } ], 'Type' => 'Methods', 'No' => '3', 'File' => 'Sample2.pl', 'Description' => 'Destructor', 'Label' => 'CC' } ] };

    Lather, rinse, repeat.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      Hey,
      The Data I provided as Perl Hash is a sample of what I have. And I need to convert it the other way round, from Hash to XML and XML::Simple will just convert it to simple XML but then the problem of illegal tags <1>,<2>,<3> crops up so i can't use XML::Simple unless it allows me to make that changes.
      Bugz

        Did you lather, rinse, repeat? You would have found out that the data structure you want with XML::Simple is as follows:

        $FIELDS = { data => { Node => [ { No => '1', Label => 'AA', Type => { content => 'Module' }, File => { content => 'Sample1.pl'}, Description => { }, }, { No => '2', Label => 'BB', Type => { content => 'Methods' }, File => { content => 'Sample1.pl' }, Description => { content => 'Initiator' }, }, { No => '3', Label => 'CC', Type => { content => 'Methods' }, File => { content => 'Sample2.pl' }, Description => { content => 'Destructor' }, Value => [ { No => '1', content => 'Ignore', }, { No => '2', content => 'Retry', }, { No => '3', content => 'Abort', } ], } ] } };

        Output:

        <opt> <data> <Node Label="AA" No="1"> <Description></Description> <File>Sample1.pl</File> <Type>Module</Type> </Node> <Node Label="BB" No="2"> <Description>Initiator</Description> <File>Sample1.pl</File> <Type>Methods</Type> </Node> <Node Label="CC" No="3"> <Description>Destructor</Description> <File>Sample2.pl</File> <Type>Methods</Type> <Value No="1">Ignore</Value> <Value No="2">Retry</Value> <Value No="3">Abort</Value> </Node> </data> </opt>

        Getting rid of the outer <opt> tag is left as an exercise to the reader.

        As dragonchild wrote above, you need to transform your hash so as to fit the module you are using.

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Perl and XML
by dragonchild (Archbishop) on Apr 29, 2008 at 17:13 UTC
    I don't remember anything in the XML spec that would preclude a tag named <1>, but this was several years ago.

    As for your problem, just transform the hash first before passing it to the XML writer. It's just a simple data structure manipulation.


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
      Hey dragonchild,
      I am unsure what you meant by manipulating the data structure... Could you elaborate it please?
      Also, the Hash that I have can't be changed. It's the input from another source.
      Bugz
        I didn't say change the source - I said to change your hash. Just cause you change an in-memory data structure doesn't mean anything to the place you got it from.

        As for transforming it, what would happen if you were to take the Values section and make it look more like what you want it to look like? That would be transforming it.


        My criteria for good software:
        1. Does it work?
        2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Perl and XML
by GrandFather (Saint) on Apr 29, 2008 at 22:05 UTC

    It's not clear to me why the xx tags need to be transformed. If that is not an issue then the following which uses XML::Twig to generate an XML fragment given a hash as described may be of help:

    use strict; use warnings; use XML::Twig; my $FIELDS = { 'AA' => { 'Description' => '', 'Type' => 'Module', 'File' => 'Sample1.pl', }, 'BB' => { 'Description' => 'Initiator', 'Type' => 'Methods', 'File' => 'Sample1.pl', }, 'CC' => { 'Description' => 'Destructor', 'Type' => 'Methods', 'File' => 'Sample2.pl', 'Values' => { '1' => 'Ignore', '2' => 'Retry', '3' => 'Abort' } } }; my $twig = XML::Twig->new (pretty_print => 'indented'); $twig->set_root (my $root = XML::Twig::Elt->new ('Fields')); AddElements ($root, $FIELDS); $twig->print (); sub AddElements { my ($parent, $hash) = @_; for my $node (sort keys %$hash) { my $child; if ($node =~ /^[0-9]/) { $child = $parent->insert_new_elt (last_child => 'Value', {No => $node}); } else { $child = $parent->insert_new_elt (last_child => $node); } if (! ref $hash->{$node}) { $child->set_text ($hash->{$node}); next; } AddElements ($child, $hash->{$node}); } }

    Prints:

    <Fields> <AA> <Description></Description> <File>Sample1.pl</File> <Type>Module</Type> </AA> <BB> <Description>Initiator</Description> <File>Sample1.pl</File> <Type>Methods</Type> </BB> <CC> <Description>Destructor</Description> <File>Sample2.pl</File> <Type>Methods</Type> <Values> <Value No="1">Ignore</Value> <Value No="2">Retry</Value> <Value No="3">Abort</Value> </Values> </CC> </Fields>

    Perl is environmentally friendly - it saves trees
      Hey GrandFather,
      Thank you for the response. It does solve one of the problems and provides me exactly what i was looking to do. However, the reason changing the xx tag was to take care of the cases where multiple tags of the same xx value existing.
      The perl hash i provided was a sample of the data i have and it has multiple xx values that repeat unfortunately, making it an illegal hash as it has multiple keys of same value, but that's how data is stored in it.
      Thus I look forward to changing it to something like:

      Option 1
      <Function> <Name>XX</Name> ... ... </Function>

      or

      Option 2
      <Function Name=""> ... ... </Function>

      This way, I could ensure I was pulling in all of these instances.
      Could you suggest something for the same?
      Thanks,
      Bugz

        Please put some effort into understanding the solutions provided in this thread. For further help provide us with the real data and the output you want to get, along with the script you use to achieve that goal. That would cut down guessing and annoyance. Tendyeberrymud.

        XY problem is always a good read.

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

        In that case you really need a different data structure. Perhaps you ought give us a chunk of the bigger picture?


        Perl is environmentally friendly - it saves trees
Re: Perl and XML
by apl (Monsignor) on Apr 29, 2008 at 17:26 UTC
    Off-topic, and anal-retentive on my part. Rather than
    $FIELDS = { 'AA' => { 'Description' => '', 'Type' => 'Module', 'File' => 'Sample1.pl', }, 'BB' => { 'Description' => 'Initiator', 'Type' => 'Methods', 'File' => 'Sample1.pl', }, 'CC' => { 'Description' => 'Destructor', 'Type' => 'Methods', 'File' => 'Sample2.pl', 'Values' => { '1' => 'Ignore', '2' => 'Retry', '3' => 'Abort' } },
    please consider
    $FIELDS = { 'AA' => { 'Description' => '', 'Type' => 'Module', 'File' => 'Sample1.pl', }, 'BB' => { 'Description' => 'Initiator', 'Type' => 'Methods', 'File' => 'Sample1.pl', }, 'CC' => { 'Description' => 'Destructor', 'Type' => 'Methods', 'File' => 'Sample2.pl', 'Values' => { '1' => 'Ignore', '2' => 'Retry', '3' => 'Abort' } } },
    This shows you the closing curley-brace you forgot after the 'Values' block.

    It doesn't really matter what convention you choose, but pick a standard and keep to it.

      Hi apl,
      Thank you for pointing out the err on my part. I have updated the same and it was here that I made the mistake. so the problem's still open.
      Bugz