http://qs1969.pair.com?node_id=658918


in reply to XML::Simple design decisions
in thread XML::Simple

I have a problem with array folding that I could only solve by subclassing XML::Simple and hardcoding a return in the array_to_hash method.
sub array_to_hash { . . . # Or assume keyattr => [ .... ] else { ELEMENT: for($i = 0; $i < @$arrayref; $i++) { return ($arrayref) if $arrayref->[$i]{name} eq 'e_im_dev_io_entry'; + #this line was added to jump out return($arrayref) unless(UNIVERSAL::isa($arrayref->[$i], 'HASH')); . . . }
If an attribute called "name" has the same value in multiple nested elements, then only one attribute will remain after the array folding. This example is only a part of a larger xml file. I don't want to use KeyAttr=>[] which does prevent the folding, since in other parts of the file, the array folding is desirable. I only want to prevent array folding if the attribute value is equal to "something". I have tried many options with no success. Am I missing something, or is subclassing the only way?
<?xml version="1.0" encoding="UTF-8" standalone="no" ?> <Report address="Address" name="IM Report" productID="INTRFC-MGR01"> <Entry detail="4" name="e_im_dev_io_entry"> <Text>Device Handle: 2</Text> </Entry> <Entry detail="4" name="e_im_dev_io_entry"> <Text>Device Handle: 5</Text> </Entry> </Report> _______________________________________________ Options used were: none No good, I lost one element $VAR1 = { 'Entry' => { 'e_im_dev_io_entry' => { 'detail' => '4', 'Text' => 'Device Handle: +5' } }, 'name' => 'IM Report', 'address' => 'Address', 'productID' => 'INTRFC-MGR01' }; _______________________________________________ Options used were: KeyAttr=>[] This is what I want. $VAR1 = { 'Entry' => [ { 'detail' => '4', 'name' => 'e_im_dev_io_entry', 'Text' => 'Device Handle: 2' }, { 'detail' => '4', 'name' => 'e_im_dev_io_entry', 'Text' => 'Device Handle: 5' } ], 'name' => 'IM Report', 'address' => 'Address', 'productID' => 'INTRFC-MGR01' };
I would like the output to be identical to the last example. I don't want to lose any elements. Again, this is just a small piece of a much larger xml document. There are lots of Entry elements so I can't use KeyAttr {...}

Replies are listed 'Best First'.
Re^2: XML::Simple design decisions
by Jenda (Abbot) on Dec 29, 2007 at 00:53 UTC

    You could also use XML::Rules instead of XML::Simple as it gives you more detailed control over what data structure gets generated.

    Something like:

    use XML::Rules; # at least 0.22 (for the stripspaces) # see http://www.perlmonks.org/?node_id=658971 my $parser = XML::Rules->new( rules => [ Text => 'content', Entry => 'as array', Report => 'pass', Other => sub {return delete($_[1]->{name}) => $_[1]}, ], stripspaces => 3, ); my $data = $parser->parse(\*DATA); use Data::Dumper; print Dumper($data); __DATA__ <?xml version="1.0" encoding="UTF-8" standalone="no" ?> <Report address="Address" name="IM Report" productID="INTRFC-MGR01"> <Entry detail="4" name="e_im_dev_io_entry"> <Text>Device Handle: 2</Text> </Entry> <Entry detail="4" name="e_im_dev_io_entry"> <Text>Device Handle: 5</Text> </Entry> <Other detail="4" name="first"> <Text>Device Handle: 5</Text> </Other> <Other detail="4" name="second"> <Text>Device Handle: 5</Text> </Other> </Report>

    It doesn't try to guess as XML::Simple does so it's more work though. (In not yet released 0.23 the rule for the Other tag will be just Other => 'by name',.)

      Thanks Jenda, that module works very well for me. One more point I would like to make. With XML::Simple and my xml file that I am working with, I can't use ForceArray => 'Entry' either for some reason; it hangs the script. I do not have that problem with XML::Rules.
Re^2: XML::Simple design decisions
by ysth (Canon) on Dec 25, 2007 at 10:06 UTC