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

I've searched manuals and the web and this site till I can no longer think straight. I think what I want to do is simple, but I must be missing something. I have some XML:
<tabs> <tab name="foo"> <flag name="one" params="123">First</flag> <list name="two" params="456">Second</list> <list name="three" params="abc">Third</list> <other>stuff</other> </tab> <tab name="bar"> ... </tab> </tabs>
I can change the flag and list items to look like <item type="flag"... etc.

I want to be able to parse this XML along the lines of XML::Simple, but I need to know the order of "one", "two" and "three". Since XML::Simple puts them in a hash, the order is lost. I would not mind an array of hashes with the flag and list items, but I haven't been able to force that. I'd prefer not to have the user have to put in order="n" for each of these.

Any help with a library or even an XML redesign would be appreciated. --traveler

Replies are listed 'Best First'.
(jeffa) Re: Ordered XML items (XML::Simple?)
by jeffa (Bishop) on Jan 17, 2003 at 16:43 UTC
    At first i thought that simply adding forcearray => 1 would do the trick, but it does not. The problem seems to be in your design. Try something like
    
    <lists>
    <list params="123">First</list>
    <list params="456">Second</list>
    </lists>
    
    in combination with forcearray => 1. Here is some code to play with:
    use strict; use warnings; use XML::Simple; use Data::Dumper; $Data::Dumper::Indent = 1; my $xml = XMLin(\*DATA, forcearray => 1); print Dumper $xml; __DATA__ <xml> <tabs> <tab name="foo"> <flag name="one" params="123">First</flag> <list name="two" params="456">Second</list> <list name="three" params="abc">Third</list> </tab> </tabs> <lists> <list params="123">First</list> <list params="456">Second</list> </lists> </xml>
    The important output:
    
      'lists' => [
        {
          'list' => [
            {
              'params' => '123',
              'content' => 'First'
            },
            {
              'params' => '456',
              'content' => 'Second'
            }
          ]
        }
    
    looks like it preserved the order. :)

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      Thanks jeffa. I had tried something similar, but instead of forcearray=>1 I used forecarray['lists']. I could not get it to work. Unfortunately this solution fails on
      <lists> <list name="one" params="456">First</list> <flag name="two" params="123">Second</flag> <list name="three" params="abc">Third</list> <lists>
      When I tried it like this, it separated the lists and the flag into separate hashes in the array. I want the user to be able to specify any order (and there will be other tags, e.g. entry, title.

      --traveler

        Looks like the problem is in the design and not the tool. Question: are lists, entries, and titles types of tags, or are they something else? If you want them to be tags, then make them common tags that can have different attributes:
        <?xml version="1.0" encoding="UTF-8"?> <tags> <tag type="title" params="blah">Zero</tag> <tag type="list" params="123">First</tag> <tag type="entry" params="456">Second</tag> <tag type="flag" params="123">Third</tag> </tags>
        When run through the same code, this output is generated:
        $VAR1 = { 'tags' => [ { 'tag' => [ { 'params' => 'blah', 'content' => 'Zero', 'type' => 'title' }, { 'params' => '123', 'content' => 'First', 'type' => 'list' }, { 'params' => '456', 'content' => 'Second', 'type' => 'entry' }, { 'params' => '123', 'content' => 'Third', 'type' => 'flag' } ] } ] };
        There are advantages to both approaches. This approach allows you to preserve the order of your 'tags', but the other approach allows you to drill-down to a specific list of tags in O(1).

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)
        
Re: Ordered XML items (XML::Simple?)
by grantm (Parson) on Jan 17, 2003 at 18:43 UTC

    XML::Simple cannot give you one array containing both <flag> and <list> elements - that's the price you pay for simplicity.

    You could change your XML so that the elements had the same name and the type was determined through an attribute. You should set keyattr => [] to ensure an array of hashes doesn't get 'folded' into a hash of hashes. That will be happening at the moment since 'name' is one of the default values in keyattr.

    Of course changing your XML because the tool is not powerful enough to work with it might suggest it's time to change the tool. XML::Twig would be worth a look.

      Thanks. Fortunately it is early enough to change the XML.

      --traveler

Re: Ordered XML items (XML::Simple?)
by runrig (Abbot) on Jan 17, 2003 at 19:21 UTC
    I mentioned a different 'simple' interface to XML which retains order in my journal, and I hope those guys get it on CPAN soon. Maybe if you email them they'd send a copy (see the OCpm site). The library depends on libXML I believe, so that may be a concern.