perlxml-twig

XML::Twig sort children on attribute (perl)


Hi,

I need to sort the children of the "Entries" element using the "a" attribute with XML::Twig but cannot find a method to do so. The structure is as follows:

<Entries>
    <datatag1 a="A1">
        <t>A</t>
    </datatag1>
    <datatag1 a="ABA1">
        <t>D</t>
    </datatag1>
    <datatag1 a="C1">
        <t>1</t>
    </datatag1>
    <datatag1 a="F1">
        <t>14</t>
    </datatag1>
    <datatag1 a="AB1">
        <t>1</t>
    </datatag1>
</Entries>

The desired order after sorting needs to be:

<Entries>
    <datatag1 a="A1">
        <t>A</t>
    </datatag1>
    <datatag1 a="C1">
        <t>1</t>
    </datatag1>
    <datatag1 a="F1">
        <t>14</t>
    </datatag1>
    <datatag1 a="AB1">
        <t>1</t>
    </datatag1>
    <datatag1 a="ABA1">
        <t>D</t>
    </datatag1>
</Entries>

However the alphabetical sorting using:

$root->sort_children_on_att('a');

does not give the desired results. This is expected as alphabetically AB follows A. Is there any other way to do the sorting with XML::Twig to get the desired result?

Any help much appreciated.


Solution

  • Twig's sorting methods don't accepts a custom sort method, but you can always use Perl's sort and cut/paste the nodes:

    #!/usr/bin/perl
    use warnings;
    use strict;
    
    use XML::Twig;
    
    my $xml = '<Entries>...</Entries>';
    
    sub sort_children {
        my $parent = $_;
        my @children = sort {
            length $a->att('a') <=> length $b->att('a')
            or $a->att('a') cmp $b->att('a')
        } $parent->cut_children;
        $_->paste(last_child => $parent) for @children;
    }
    
    my $twig = 'XML::Twig'->new(twig_handlers => { Entries => \&sort_children });
    $twig->parse($xml);
    $twig->print;