2010-11-12 11 views
5

Już widziałem kilka odpowiedzi w różnych miejscach, w odniesieniu do ustawiania kolejności elementów XML zwracanych przez XMLout. Jednak nie jestem w stanie rozwiązać problemu za pomocą tych odpowiedzi/przykładów.XML :: Prosta kolejność elementów wyjściowych ze złożonego skrótu

Mam skrypt, który wymaga wyprowadzenia niektórych danych XML, a niektóre elementy muszą zostać wydrukowane w określonej kolejności. Hash jest dość skomplikowany i nie udało mi się uzyskać żadnych wyników przez przesłonięcie obiektu sorted_keys w obiekcie XML::Simple. Cóż, zrobiłem, ale nie tak, jak chciałem.

Przykładowy kod znajduje się poniżej, szczegóły problemu znajdują się poniżej kodu.

#!/usr/bin/perl 

use strict; 
use warnings; 
use XML::Simple; 

package MyXMLSimple; 
use base 'XML::Simple'; 

sub sorted_keys 
{ 
my ($self, $name, $hashref) = @_; 
# ... 
return $self->SUPER::sorted_keys($name, $hashref); 
} 

package main; 

my $xmlParser = MyXMLSimple->new; 

my $items = { 
'status' => 'OK', 
'fields' => { 
    'i1' => { 
    'header' => 'Header 1', 
    'max_size' => '3' 
    }, 
    'i2' => { 
    'header' => 'Header 2', 
    'max_size' => '8' 
    } 
}, 
'item_list' => { 
    'GGG' => { 
    'index' => '3', 
    'i' => 3, 
    'points' => { 
    'p5' => { 
    'data' => '10', 
    } 
    }, 
    }, 
    'AAA' => { 
    'index' => '1', 
    'i' => 2, 
    'points' => { 
    'p7' => { 
    'data' => '22', 
    } 
    }, 
    }, 
    'ZZZ' => { 
    'index' => '2', 
    'i' => 1, 
    'points' => { 
    'p6' => { 
    'data' => '15', 
    } 
    }, 
    } 
} 
}; 

my $xml = $xmlParser->XMLout($items); 
print "$xml"; 

więc wyjście z tego skryptu będzie to:

<opt status="OK"> 
    <fields name="i1" header="Header 1" max_size="3" /> 
    <fields name="i2" header="Header 2" max_size="8" /> 
    <item_list name="AAA" i="2" index="1"> 
    <points name="p7" data="22" /> 
    </item_list> 
    <item_list name="GGG" i="3" index="3"> 
    <points name="p5" data="10" /> 
    </item_list> 
    <item_list name="ZZZ" i="1" index="2"> 
    <points name="p6" data="15" /> 
    </item_list> 
</opt> 

item_list elementy są drukowane, a kolejność wyjścia jest alfabetycznie, sortując na atrybucie name. Kolejność wyjściowa to AAA, GGG, ZZZ.

Jednak potrzebowałbym mieć wyjście podczas sortowania (numerycznie, od najniższego do najwyższego) elementu i. Tak więc wyjście będzie w kolejności ZZZ, AAA, GGG.

Nie mam żadnej kontroli nad zamówieniem w haśle (nie bez użycia modułu Tie::...), więc nie mogę tego zrobić w ten sposób. Jeśli użyję NoSort => 1, dane wyjściowe nie zostaną posortowane według niczego, więc otrzymam losowe dane wyjściowe.

Jestem prawie pewny, że musi istnieć sposób, aby rozwiązać problem tak, jak tego chcę, przesuwając podprogram sorted_keys. Jednak nie mogłem uzyskać pożądanych wyników, ponieważ wywoływana jest sorted_keys dla każdego wystąpienia item_list. Kiedy sorted_keys jest wywoływany dla elementu opt, to po prostu mam dostęp do całego hashu, ale znowu nie oznacza to zagwarantowania kolejności wyjściowej bez polegania na module Tie::.

Teraz udało mi się uzyskać to działa tak, jak chcesz, za pomocą modułu Tie::IxHash, następnie przesłanianie sorted_keys i (re) tworząc SUBHASH item_list poprzez wsunięcie wartości z oryginalnego hash do nowego (zamówione) jeden, a następnie usuwanie subhash w oryginalnym haszu i zastępowanie go nowym uporządkowanym hashem.

coś takiego:

sub sorted_keys 
{ 
my ($self, $name, $hashref) = @_; 
if ($name eq "opt") 
{ 
    my $clist = { }; 
    tie %{$clist}, "Tie::IxHash"; 

    my @sorted_keys = sort { $hashref->{item_list}->{$a}->{i} <=> $hashref->{item_list}->{$b}->{i} } keys %{$hashref->{item_list}}; 
    foreach my $sorted_key (@sorted_keys) 
    { 
    $clist->{$sorted_key} = $hashref->{item_list}->{$sorted_key}; 
    } 

    delete $hashref->{item_list}; 
    $hashref->{item_list} = $clist; 
} 
return $self->SUPER::sorted_keys($name, $hashref); 
} 

Chociaż to działa (i jak dotąd wydaje się działać niezawodnie), wierzę, że musi być jakiś sposób, aby to osiągnąć bez użycia modułu Tie::IxHash i robi wszystko, hash rekreacji/zmiana kolejności i tylko w jakiś sposób sortowanie/zwracanie pewnych danych z poziomu sorted_keys.

Po prostu nie mogę tego rozgryźć i naprawdę nie rozumiem, jak powinien działać sorted_keys (szczególnie, gdy uzyskuje się różne wyniki z różnymi/złożonymi zestawami danych wejściowych;), ale mam nadzieję, że jest ktoś tam, kto to wie.

Mam na myśli, próbowałem modyfikować sam XML/Simple.pm i zmieniając kolejność sortowania w ostatniej linii zwrotnej z podprogramu sorted_keys, ale wciąż otrzymywałem alfanumerycznie posortowane dane wyjściowe. Obawiam się, że nie mogę wymyślić, jak to zmodyfikować, aby nie sortować na name, ale na i.

Każda pomoc jest mile widziane :)

+0

TLDR, bit +1 za dokładność (IMHO, bezowocny) wysiłek - zrobiłbym +2, gdybym mógł :) – DVK

Odpowiedz

1

Wierzę w tym momencie przerosły XML :: Simple. Jeśli zależy ci na kolejności dzieci w elemencie, to nadszedł czas, aby użyć więcej modułu XML-ish. Aby uzyskać styl tworzenia XML, być może XML::TreeBuilder, spójrz na metodę new_from_lol. Lub XML :: LibXML, XML :: Twig, XML :: Writer ...

Próbowałem również miksowania Tie :: IxHash i XML :: Simple w przeszłości, i nie było to przyjemne. naprawdę dotarłeś tutaj dość daleko. Ale wierzę, że w ten sposób leży szaleństwo

0

Może spojrzeć na przesłanianie hash_to_array? Wypracował dla mnie. http://perlmaven.com/xml-simple-sorting

Chciałem wykonać sortowanie naturalne z kluczem kluczowym i udało się to osiągnąć przez przesłonięcie XML :: Simple hash_to_array. I w zasadzie skopiowana metodę z XML :: Simple.pm i popełniłem mały edycji do naturalnej sort - tak:

package MyXMLSimple;  # my XML::Simple subclass 
use base 'XML::Simple'; 

sub hash_to_array { 
    my $self = shift; 
    my $parent = shift; 
    my $hashref = shift; 

    my $arrayref = []; 

    my($key, $value); 

    if ($parent eq "mytag") { 
    my @keys = $self->{opt}->{nosort} ? keys %$hashref : sort {$a<=>$b} keys %$hashref; 
    foreach $key (@keys) { 
    $value = $hashref->{$key}; 
    return($hashref) unless(UNIVERSAL::isa($value, 'HASH')); 

    if(ref($self->{opt}->{keyattr}) eq 'HASH') { 
     return($hashref) unless(defined($self->{opt}->{keyattr}->{$parent})); 
     push @$arrayref, $self->copy_hash(
     $value, $self->{opt}->{keyattr}->{$parent}->[0] => $key 
    ); 
    } 
    else { 
     push(@$arrayref, { $self->{opt}->{keyattr}->[0] => $key, %$value }); 
    } 
    } 

    } else { 
    my @keys = $self->{opt}->{nosort} ? keys %$hashref : sort keys %$hashref; 
    foreach $key (@keys) { 
    $value = $hashref->{$key}; 
    return($hashref) unless(UNIVERSAL::isa($value, 'HASH')); 

    if(ref($self->{opt}->{keyattr}) eq 'HASH') { 
     return($hashref) unless(defined($self->{opt}->{keyattr}->{$parent})); 
     push @$arrayref, $self->copy_hash(
     $value, $self->{opt}->{keyattr}->{$parent}->[0] => $key 
    ); 
    } 
    else { 
     push(@$arrayref, { $self->{opt}->{keyattr}->[0] => $key, %$value }); 
    } 
    } 
    } 

    return($arrayref); 
} 

my $xmlParser = MyXMLSimple->new(KeepRoot => 1); 
$xmlParser->XMLout($self->{_xml}, KeyAttr => { ... 

Pewnie, że jest brzydki, wartownik będzie trzeba ustawić „i” jak i jego 5 KeyAttr lat za późno, ale działa i teraz mogę iść zrobić coś innego :)

+0

Podczas gdy ten link może odpowiadać pytanie, lepiej w tym miejscu umieścić istotne części odpowiedzi i podać odsyłacz do odniesienia. Odpowiedzi dotyczące linków mogą stać się nieprawidłowe, jeśli strona z linkami się zmieni – abarisone

Powiązane problemy