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

Hello,

I am using Perl 5.14.2 and XML::Simple (2.18) to create an XML file from a hash, and I'm having trouble. I can't seem to find the right options to pass to XMLout to create the XML file how I want it. I can modify the way I am creating the hash in the first place, if that is best.

Here's the code:
#!/usr/bin/perl use strict; use warnings; use XML::Simple qw(:strict); my $userref = (); $userref->{0}{'hash'}{'name'} = 'joe'; $userref->{0}{'hash'}{'type'} = 'user'; $userref->{1}{'hash'}{'name'} = 'mary'; $userref->{1}{'hash'}{'type'} = 'user'; my $XmlRef = (); # loop thru all keys in userref for my $key(sort keys %$userref){ # the key name my $keyname = 'key'.$key; my $cnt = scalar keys %{$XmlRef->{$keyname}{'devices'}}; my $i=0; for my $foo(keys %{$userref->{$key}{'hash'}}){ $XmlRef->{$keyname}{'devices'}{'dev'.$cnt}{'param'}{$foo}{'content +'} = [ $userref->{$key}{'hash'}{$foo} ]; $i+=1; } } # write XML to file my $tmpfile = '/tmp/testfile.xml'; my $fh; open $fh, '>', $tmpfile or die "open($tmpfile): $!\n"; XMLout($XmlRef, XMLDecl => '<?xml version="1.0" encoding="utf-8"?>', ContentKey => '-content', NoAttr => 0, OutputFile => $fh, KeyAttr => { 'param' => 'id' }, AttrIndent => 1, ); close $fh;

and here's the contents of the generated XML file:

<?xml version="1.0" encoding="utf-8"?> <opt> <key0> <devices> <dev0> <param id="name"> <content>joe</content> </param> <param id="type"> <content>user</content> </param> </dev0> </devices> </key0> <key1> <devices> <dev0> <param id="name"> <content>mary</content> </param> <param id="type"> <content>user</content> </param> </dev0> </devices> </key1> </opt>

however, what i would like is for the "content" key to be removed entirely, e.g.:

<opt> <key0> <devices> <dev0> <param id="name">joe</param> <param id="type">user</param> </dev0> </devices> </key0>

if i simply leave off the "content" key when generating the hash, then I lose the ability to make the "id" an attribute of the "param" key. grrr. can anyone help?

Replies are listed 'Best First'.
Re: XMLout and keys/attributes
by sauoq (Abbot) on May 09, 2012 at 15:02 UTC

    Remove the ContentKey option from your call to XMLout and change where you set $XmlRef to the following:

    $XmlRef->{$keyname}{'devices'}{'dev'.$cnt}{'param'} = [ {$foo => $userref->{$key}{'hash'}{$foo}} ];
    Update: No, that's not right... you have two tags, so you have to push them on... Try this:
    $XmlRef->{$keyname}{'devices'}{'dev'.$cnt}{'param'} = []; for my $foo(keys %{$userref->{$key}{'hash'}}) { push @{ $XmlRef->{$keyname}{'devices'}{'dev'.$cnt}{'param'} } , {id => $foo, content => $userref->{$key}{'hash'}{$foo}} ; $i+=1; }

    Edit: struck out incorrect code.

    -sauoq
    "My two cents aren't worth a dime.";
      Arggg! I just looked at the original code again, and I realized I screwed up the description of the XML. here is the correct $userref example:
      $userref->{0}{'hash'}{'Name'} = 'joe'; $userref->{0}{'hash'}{'Type'} = 'user'; $userref->{0}{'hash'}{'1Param'} = 'foo';
      and here is what I need the XML to look like:
      <key0> <devices> <dev0> <param id="Name">joe</param> <param id="Type">user</param> <param id="1Param">foo</param> </dev0> </devices> </key0>
      btw, your code suggestion was very close sauoq, care to try again?!
        btw, your code suggestion was very close sauoq, care to try again?!

        I already did. See my update? ;-)

        P.S. It should work with three params as well as two, so your "arrrggg" isn't needed. :-)

        -sauoq
        "My two cents aren't worth a dime.";
Re: XMLout and keys/attributes
by tobyink (Canon) on May 09, 2012 at 15:02 UTC

    You are probably best off using something like XML::LibXML. XML::Simple is mostly for people who just want to treat XML as if it were a big hash structure, and don't care about the XML elements, attributes, text nodes, etc.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
      This looks interesting...but I have XML::Simple code everywhere else and I'd like to solve it that way. If I can't though, I'll try your suggestion. Thanks for your response.
Re: XMLout and keys/attributes
by Anonymous Monk on May 09, 2012 at 14:48 UTC
    What are you using for XMLin?
      I am not using XMLin for anything. What I am trying to do (generate an XML file from a hash/hashref of my own making) has been done elsewhere with no problems. It is just that I am being picky about what is an attribute and what is not that is tripping me up.
        I forgot to mention an important point: in the $userref hashref, there will be many values for 'name', and they could start with a number, which is why I want to use them as attributes, and not as keys. example:
        $userref->{2}{'hash'}{'name'} = '12345'; $userref->{3}{'hash'}{'name'} = '10foo';

        I am not using XMLin for anything.

        Then I would ditch XMLout ASAP :) seriously

        The way I use XML::Simple, is start with XML I want, then use XMLin to get data I want, then XMLout to get back xml I started with

        If XMLin can't be made into data structure you want, then XMLout can't be made into xml you want

        All that massaging of $userref you do is busywork :)

        So with XML::Rules ( XML::Simple on steriods :) steroids) I might write

        #!/usr/bin/perl -- use strict; use warnings; use XML::Rules; use Data::Dump qw/ dd /; my $wanted = <<'__XML__'; <?xml version="1.0" encoding="utf-8"?> <opt> <key0> <devices> <dev0> <param id="name">joe</param> <param id="type">user</param> </dev0> </devices> </key0> <key1> <devices> <dev0> <param id="name">mary</param> <param id="type">user</param> </dev0> </devices> </key1> </opt> __XML__ use XML::Rules; my $t = XML::Rules->new( qw/ stripspaces 8 /, rules => { 'dev0,devices,key0,key1,opt' => 'no content', 'param' => 'as array', }, ); my $XmlRef = $t->parse( $wanted ); dd $XmlRef; print $t->toXML( opt => $XmlRef->{opt}, undef, " ", ); __END__ { opt => { key0 => { devices => { dev0 => { param => [ { _content => "joe", id => "name" }, { _content => "user", id => "type" }, ], }, }, }, key1 => { devices => { dev0 => { param => [ { _content => "mary", id => "name" }, { _content => "user", id => "type" }, ], }, }, }, }, } <opt> <key0> <devices> <dev0> <param id="name">joe</param> <param id="type">user</param> </dev0> </devices> </key0> <key1> <devices> <dev0> <param id="name">mary</param> <param id="type">user</param> </dev0> </devices> </key1> </opt>

        You still get to have a hairy hash, you still get your xml, and you don't spend days trying to make it "Simple"