лучший способ отсортировать узлы xml по значению дочернего узла с помощью XML::DOM

Я пытался отсортировать группу узлов в XML-файле, и это оказалось намного сложнее, чем я думал. Я использую XML::DOM, и я хочу взять узел и отсортировать группу дочерних узлов на основе значения, в этом случае отсортировать узлы-члены по моему значению member/num

вот xml файл:

<?xml version="1.0"?>
<family>
<member><num>1A</num><name>isashi</name></member>
<member><num>1</num><name>felix</name></member>
<member><num>3</num><name>brandon</name></member>
<member><num>5</num><name>jeremy</name></member>
<member><num>4B</num><name>aaron</name></member>
</family>

и соответствующий код Perl:

my $instance = 'C:\my\path\perlNodeSortTest.xml'; 
$instance =~ s#\\#/#g;

# create parser, open file
my $parser = XML::DOM::Parser->new();
my $doc = $parser->parsefile( $instance );



sub readMembers(){

my $members = $doc->getElementsByTagName( 'member' );

# basic idea here is to loop thru nodes, swapping the old sort order node for the new,
# but getting error
my $i = 0;
foreach my $nodeMem(sort mySort @{$members} ){
    my $nodeNum = $nodeMem->getElementsByTagName('num')->item(0);
    my $numVal = &getTagValue( $nodeNum );

    my $parentNode = $nodeMem->getParentNode();
    print $parentNode->getNodeName(), "\n";

    my $oldNode = $members->item($i);

    $parentNode->replaceChild($nodeMem, $oldNode);
    print "reading " . $nodeMem->getNodeName() . " num is $numVal\n";

    $i++
}
}


# this sort could be a lot more sophisticated, but this is the basic idea
sub mySort(){

my $nodeNumA = $a->getFirstChild();
my $nodeNumB = $b->getFirstChild();

    # getTagValue() sub not shown, but it just grabs the value of the node, assuming
    # it's a text node and has no child element nodes
my $numA = &getTagValue( $nodeNumA );
my $numB = &getTagValue( $nodeNumB );

if( $numA =~ m/[a-zA-Z]/ || $numB =~ m/[a-zA-Z]/){
    return $numA cmp $numB;
} else {
    return $numA cmp $numB;
}
}

этот код приводит к ошибке, например:

Can't call method "getNodeName" on an undefined value at sort-nodes-test.pl line 47.

Я пробовал еще пару вещей, например определение узла вне цикла foreach, но при этом некоторые элементы в выводе пропускались, хотя весь вывод консоли был правильным.

когда я поменял эту строку:

$parentNode->replaceChild($nodeMem, $oldNode);

для этого ($ root определен вне foreach):

$root->appendChild($nodeMem);

Я получил правильный вывод, но это кажется странным. Возможно, я ответил на свой собственный вопрос, написав это (я полагаю, неплохо .. :)), но будет ли это решение работать все время? Есть ли понимание, правильно ли второе решение и почему оно работает? Я бы подумал, что это добавит копию отсортированного узла ... .

Кроме того, какие-нибудь любимые пакеты, методы сортировки xml-узлов с помощью perl?


person badperson    schedule 22.07.2013    source источник
comment
XSLT может сортировать. Вы захотите использовать XML::LibXSLT.   -  person runrig    schedule 23.07.2013


Ответы (1)


Неудивительно, что я бы использовал XML::Twig:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

my $t= XML::Twig->new( pretty_print => 'record_c')->parsefile( $ARGV[0]);
$t->root->sort_children_on_field( 'num');
$t->print;

В любом случае я бы старался избегать XML::DOM. XML::LibXML очень похож, но быстрее, имеет больше возможностей и лучше поддерживается.

person mirod    schedule 23.07.2013