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

Okay...I need help !

I have a XML file named "abc.xml" as follows.

#########################################

<?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?> <system> <header></header> <class> <name>Eight</name> <strength>8</strength> </class> <class> <name>Four</name> <strength>4</strength> </class> <class> <name>Ten</name> <strength>10</strength> </class> <class> <name>One</name> <strength>1</strength> </class> <footer></footer> </system>

##############################################

Now I want to create another XML file named "xyz.xml" which is sorted on the basis of "strength count". And the new XML file should look like this.

##############################################

<?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?> <system> <header></header> <class> <name>One</name> <strength>1</strength> </class> <class> <name>Four</name> <strength>4</strength> </class> <class> <name>Eight</name> <strength>8</strength> </class> <class> <name>Ten</name> <strength>10</strength> </class> <footer></footer> </system>

##############################################

Any idea how can it can be done.

Thanks in advance!

Regards,

Cherry

Code tags added by GrandFather

Replies are listed 'Best First'.
Re: Sort n Create XML
by b4swine (Pilgrim) on Aug 20, 2007 at 08:05 UTC
    Look up the package XML::Simple;.

    This allows you to read the entire XML file in a variable using XMLin

    Now the easiest way to understand what has happened is to use Data::Dumper and print Dumper($xmlcontents) where $xmlcontents is what was read by XMLin.

    Now just sort, and then use XMLout to print it.

Re: Sort n Create XML
by holli (Abbot) on Aug 20, 2007 at 09:12 UTC
    Purists would say, this is a job for XSLT. But for me that language is terribly mindboggling. So if I face XML-transformations I normally settle on this approach:
    • read the xml using on of the many xml-parsers into a datastructure
    • do the transformation using grep, map and sort.
    • write the xml using a templating engine.
    This has the charme that it nicely separates presentation from logic.

    Example:
    use strict; use warnings; use Template; use XML::Simple; my $data = XMLin( $ARGV[0], ForceArray => 'class' ); $data->{class} = [ sort { $a->{strength}->[0] <=> $b->{strength}->[0] } @{$data->{class}} ]; #use Data::Dumper; #print Dumper ( $data ); my $tt = Template->new(); $tt->process( \*DATA, {data => $data}, $ARGV[1] ) || die $tt->error(), "\n"; __DATA__ <?xml version="1.0" encoding="UTF-8"?> <system> <header></header> [% FOR class = data.class %] <class> <name>[% class.name.0 %]</name> <strength>[% class.strength.0 %]</strength> </class> [% END %] <footer></footer> </system>
    You can call this as with two arguments. The first is the file to be read, the second is the file to be written. If you omit the second argument, the output is written to STDOUT.


    holli, /regexed monk/
Re: Sort n Create XML
by misc (Friar) on Aug 20, 2007 at 09:41 UTC
    TIMTOWTDI :-)
    Here is my version, utilizing XML::Twig

    #!/usr/bin/perl -w use strict; use warnings; use XML::Twig; my $t= XML::Twig->new(pretty_print=>'indented'); $t->parse( join('',<DATA>) ); $t->root->sort_children( sub{ my $e = shift; return 0 if $e->tag eq 'header'; return 10000000 if $e->tag eq 'footer'; my @strength = $e->getElementsByTagName('strength') or + die "Malformed element:\n".$e->sprint; return $strength[0]->text; }, type=>'numeric' ); $t->print; __DATA__ <?xml version="1.0" encoding="UTF-8"?> <system> <header></header> <class> <name>Eight</name> <strength>8</strength> </class> <class> <name>Four</name> <strength>4</strength> </class> <class> <name>Ten</name> <strength>10</strength> </class> <class> <name>One</name> <strength>1</strength> </class> <footer></footer> </system>
    The sort sub function is quite ugly and buggy,
    if you are going to use Twig, you should either print the header, footer and the sorted classes in separate prints or surround all classes with a e.g. <allclasses> tag.

    I just wanted to give an idea how you could use Twig for the job.
Re: Sort n Create XML
by roman (Monk) on Aug 20, 2007 at 11:00 UTC
    I'm used to use XML::LibXML and XPath
    use strict; use warnings; use XML::LibXML; my $doc = XML::LibXML->new->parse_file('abc.xml'); my @class_elems = $doc->findnodes('/*/class'); my @sorted_class_elems = sort { $a->findvalue('strength') <=> $b->findvalue('strength') } @class_elems; for my $node (@class_elems) { $node->replaceNode( shift(@sorted_class_elems)->cloneNode(1) ); } open( my $fh, '>', 'xyz.xml' ); $fh->print( $doc->toString(1) );
    Roman
Re: Sort n Create XML
by Samy_rio (Vicar) on Aug 20, 2007 at 09:12 UTC

    TIMTOWTDI using split and RegExp

    use strict; use warnings; my $data = do{local $/; <DATA>}; my ($header, $class, $footer) = $data =~ m/^(.*<\/header>)(.*)(<footer +>.*)$/is; my @data = split/(?=<class>)/, $class; my %hash; for my $data (@data){ if ($data =~ m/<strength>((?:(?!<\/strength>).)*)<\/strength>/si){ my $str = $1; if (defined($hash{$str})) { $hash{$str} .= $data }else{ $hash{ +$str} = $data} } } my @sort = sort{$a <=> $b} (keys %hash); print "$header\n"; print $hash{$_} for (@sort); print "$footer"; __DATA__ <?xml version="1.0" encoding="UTF-8"?> <system> <header></header> <class> <name>Eight</name> <strength>8</strength> </class> <class> <name>Four</name> <strength>4</strength> </class> <class> <name>Ten</name> <strength>10</strength> </class> <class> <name>One</name> <strength>1</strength> </class> <footer></footer> </system>

    Regards,
    Velusamy R.


    eval"print uc\"\\c$_\""for split'','j)@,/6%@0%2,`e@3!-9v2)/@|6%,53!-9@2~j';