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

I need to sort following xml using XML::Twig ($VERSION = '3.42'). Sort order is parent attribute, Child, childinfo, Please advise. Thanks.
#I'm getting: Use of uninitialized value in string comparison (cmp) at + /usr/local/perl/lib/site_perl/5.12.2/XML/Twig.pm line 9313. my $t = XML::Twig::Elt->new(); ->parse( \*DATA); $t->sort_children(); #Child $t->print; __DATA__ <?xml version="1.0" encoding="utf-8"?> <root> <parent attribute="info1"> <Child4> <childinfo Number="1" attrib2="info3"/> </Child4> </parent> <parent attribute="info1"> <Child5> <childinfo Number="1" attrib2="info3"/> </Child5> </parent> <parent attribute="info3"> <Child1> <childinfo Number="3" attrib2="info3"/> </Child1> </parent> <parent attribute="info1"> <Child9> <childinfo Number="1" attrib2="info3"/> </Child9> </parent> <parent attribute="info5"> <Child7> <childinfo Number="5" attrib2="info3"/> </Child7> </parent> <parent attribute="info4"> <Child1> <childinfo Number="4" attrib2="info3"/> </Child1> </parent> <parent attribute="info1"> <Child12> <childinfo Number="1" attrib2="info3"/> </Child12> </parent> <parent attribute="info1"> <Child10> <childinfo Number="1" attrib2="info3"/> </Child10> </parent> <parent attribute="info2"> <Child8> <childinfo Number="2" attrib2="info3"/> </Child8> </parent> <parent attribute="info1"> <Child6> <childinfo Number="1" attrib2="info3"/> </Child6> </parent> <parent attribute="info1"> <Child3> <childinfo Number="1" attrib2="info3"/> </Child3> </parent> <parent attribute="info1"> <Child2> <childinfo Number="1" attrib2="info3"/> </Child2> </parent> </root>

Replies are listed 'Best First'.
Re: xml sorting using XML::Twig
by toolic (Bishop) on Jul 25, 2013 at 19:52 UTC
    A typo? You probably have:
    $t->parse( \*DATA);

    In the XML::Twig docs, sort_children seems to need arguments passed to it.

Re: xml sorting using XML::Twig
by Loops (Curate) on Jul 25, 2013 at 20:05 UTC
    You need to specify the criterion on which to sort, for example:
    my $t = parse XML::Twig::Elt(\*DATA); $t->sort_children_on_att('attribute'); $t->print;
      Loops,
      $t->sort_children_on_att('attribute');
      sorted parent attribute, how to get also sorted by Child then childinfo if parent attribute is same? Thanks.
      <root> <parent attribute="info1"> <Child4> <childinfo Number="1" attrib2="info3"/> </Child4> </parent> <parent attribute="info1"> <Child5> <childinfo Number="1" attrib2="info3"/> </Child5> </parent> <parent attribute="info1"> <Child9> <childinfo Number="1" attrib2="info3"/> </Child9> </parent> <parent attribute="info1"> <Child12> <childinfo Number="1" attrib2="info3"/> </Child12> </parent> <parent attribute="info1"> <Child10> <childinfo Number="1" attrib2="info3"/> </Child10> </parent> <parent attribute="info1"> <Child6> <childinfo Number="1" attrib2="info3"/> </Child6> </parent> <parent attribute="info1"> <Child3> <childinfo Number="1" attrib2="info3"/> </Child3> </parent> <parent attribute="info1"> <Child2> <childinfo Number="1" attrib2="info3"/> </Child2> </parent> <parent attribute="info2"> <Child8> <childinfo Number="2" attrib2="info3"/> </Child8> </parent> <parent attribute="info3"> <Child1> <childinfo Number="3" attrib2="info3"/> </Child1> </parent> <parent attribute="info4"> <Child1> <childinfo Number="4" attrib2="info3"/> </Child1> </parent> <parent attribute="info5"> <Child7> <childinfo Number="5" attrib2="info3"/> </Child7> </parent> </root>

        You'll need to supply a custom sorting sub that crafts an appropriate sort key:

        my $t= parse XML::Twig::Elt(\*DATA); $t->sort_children( sub { my $elt = shift; my $top = $elt->att('attribute'); my $kid = $elt->first_child->tag; return $top . $kid; }); $_->print for $t->children;
        Produces:
        <parent attribute="info1"><Child10><childinfo Number="1" attrib2="info +3"/></Child10></parent> <parent attribute="info1"><Child12><childinfo Number="1" attrib2="info +3"/></Child12></parent> <parent attribute="info1"><Child2><childinfo Number="1" attrib2="info3 +"/></Child2></parent> <parent attribute="info1"><Child3><childinfo Number="1" attrib2="info3 +"/></Child3></parent> <parent attribute="info1"><Child4><childinfo Number="1" attrib2="info3 +"/></Child4></parent> <parent attribute="info1"><Child5><childinfo Number="1" attrib2="info3 +"/></Child5></parent> <parent attribute="info1"><Child6><childinfo Number="1" attrib2="info3 +"/></Child6></parent> <parent attribute="info1"><Child9><childinfo Number="1" attrib2="info3 +"/></Child9></parent> <parent attribute="info2"><Child8><childinfo Number="2" attrib2="info3 +"/></Child8></parent> <parent attribute="info3"><Child1><childinfo Number="3" attrib2="info3 +"/></Child1></parent> <parent attribute="info4"><Child1><childinfo Number="4" attrib2="info3 +"/></Child1></parent> <parent attribute="info5"><Child7><childinfo Number="5" attrib2="info3 +"/></Child7></parent>

        But you'll notice that the unfortunate naming of the ChildNN tags makes Child 10 sort before Child2. You can hack at the naming to get the proper order, but it requires having a firm knowledge of your data invariants. Here would be a reasonable attempt though:

        my $t= parse XML::Twig::Elt(\*DATA); $t->sort_children( sub { my $elt = shift; my $top = $elt->att('attribute'); my $kid = sprintf '%04d', ($elt->first_child->tag =~ s/Child//r); return $top . $kid; }); $_->print for $t->children;
        Produces:
        <parent attribute="info1"><Child2><childinfo Number="1" attrib2="info3 +"/></Child2></parent> <parent attribute="info1"><Child3><childinfo Number="1" attrib2="info3 +"/></Child3></parent> <parent attribute="info1"><Child4><childinfo Number="1" attrib2="info3 +"/></Child4></parent> <parent attribute="info1"><Child5><childinfo Number="1" attrib2="info3 +"/></Child5></parent> <parent attribute="info1"><Child6><childinfo Number="1" attrib2="info3 +"/></Child6></parent> <parent attribute="info1"><Child9><childinfo Number="1" attrib2="info3 +"/></Child9></parent> <parent attribute="info1"><Child10><childinfo Number="1" attrib2="info +3"/></Child10></parent> <parent attribute="info1"><Child12><childinfo Number="1" attrib2="info +3"/></Child12></parent> <parent attribute="info2"><Child8><childinfo Number="2" attrib2="info3 +"/></Child8></parent> <parent attribute="info3"><Child1><childinfo Number="3" attrib2="info3 +"/></Child1></parent> <parent attribute="info4"><Child1><childinfo Number="4" attrib2="info3 +"/></Child1></parent> <parent attribute="info5"><Child7><childinfo Number="5" attrib2="info3 +"/></Child7></parent>