2010-11-12 5 views
5

Ich habe bereits einige Antworten auf verschiedene Orte gesehen, in Bezug auf die Reihenfolge der XML-Elemente, die von XMLout zurückgegeben werden. Ich bin jedoch nicht in der Lage, ein Problem mit diesen Antworten/Beispielen zu lösen.XML :: Einfache Ausgabe-Element-Reihenfolge von komplexen Hash

Ich habe ein Skript, das einige XML-Daten ausgeben muss, und bestimmte Elemente müssen in bestimmter Reihenfolge gedruckt werden. Hash ist ziemlich komplex, und ich konnte keine Ergebnisse erzielen, indem ich sorted_keys in XML::Simple Objekt überschreiben. Nun, ich tat es, aber nicht so, wie ich es wollte.

Beispielcode ist unten, Details zu dem Problem sind unterhalb des Codes.

#!/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"; 

also die Ausgabe dieses Skripts wird dies:

<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 Elemente werden ausgedruckt und Ausgabereihenfolge ist alphabetisch, indem Sie auf name Attribut zu sortieren. Ausgangsreihenfolge ist AAA, GGG, ZZZ.

Was ich jedoch benötigen würde, ist die Ausgabe während der Sortierung (numerisch, von niedrigsten bis höchsten) auf i Element. Also wird die Ausgabe in der Reihenfolge ZZZ, AAA, GGG sein.

Ich habe keine Kontrolle über die Reihenfolge im Hash (nicht ohne Tie::... Modul zu verwenden), so kann ich es nicht so machen. Wenn ich NoSort => 1 verwende, wird die Ausgabe nicht nach irgendetwas sortiert, so dass ich am Ende zufällige Ausgabe bekomme.

Also bin ich mir ziemlich sicher, dass es eine Möglichkeit geben muss, dies so zu lösen, wie ich es will, indem ich die Unterroutine sorted_keys überschreibe. Ich konnte jedoch keine gewünschten Ergebnisse erzielen, da sorted_keys für jede Instanz von item_list aufgerufen wird. Wenn sorted_keys für opt Element aufgerufen wird, dann habe ich einfach Zugriff auf die gesamte Hash-Referenz, aber wiederum kein Mittel, um die Ausgabe zu gewährleisten, ohne auf Tie:: Modul zu verlassen.

Jetzt habe ich es geschafft, dies zu erhalten, die Art und Weise zu arbeiten, ich will, durch Tie::IxHash Modul verwenden, dann sorted_keys überschrieben und (Wieder-) die Schaffung eines subhash item_list, durch Werte von ursprünglichen Hash in neue (geordnet) ein wieder einsetzen, dann Löschen von Subhash im ursprünglichen Hash und Ersetzen durch neuen geordneten Hash.

Etwas wie folgt aus:

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); 
} 

Obwohl dies funktioniert (und so weit scheint zuverlässig zu arbeiten), ich glaube, dass es einen Weg, dies zu erreichen, ohne Tie::IxHash-Modul und alle, die Hash-Erholung zu tun/Neuordnung und nur durch Sortieren/Zurückgeben bestimmter Daten aus sorted_keys.

Ich kann es einfach nicht herausfinden, und ich verstehe nicht wirklich, wie sorted_keys soll funktionieren (vor allem, wenn Sie unterschiedliche Ergebnisse mit verschiedenen/komplexen Mengen von Eingabedaten erhalten;), aber ich hoffe, es ist jemand da draußen, wer weiß das.

Ich meine, ich habe versucht, XML/Simple.pm selbst zu ändern und Sortierreihenfolge in der letzten Rückleitung von sorted_keys Unterprogramm ändern, aber ich bekam immer noch alphanumerisch sortierte Ausgabe. Ich fürchte, ich kann nicht herausfinden, wie ich es ändern würde, so dass es nicht auf name, sondern auf i sortieren.

Jede Hilfe sehr geschätzt :)

+0

TLDR, Bit +1 für die Gründlichkeit von (IMHO, vergeblich) Aufwand - hätte +2 getan, wenn ich könnte :) – DVK

Antwort

1

ich an dieser Stelle glauben Sie XML entwachsen :: Simple. Wenn Ihnen die Reihenfolge der untergeordneten Elemente in einem Element wichtig ist, sollten Sie ein XML-ish-Modul verwenden. Für den Stil der XML-Erstellung, die Sie wollen, vielleicht XML::TreeBuilder, schauen Sie sich die new_from_lol Methode an. Oder XML :: LibXML, XML :: Zweig, XML :: Schreiber ...

Ich habe auch versucht, Tie :: IxHash und XML :: Simple in der Vergangenheit zu mischen, und es war nicht schön. du bist wirklich ziemlich weit hergekommen. Aber ich glaube auf diese Weise liegt Wahnsinn

0

Vielleicht werfen Sie einen Blick auf überschreiben Hash_to_array? Arbeitete für mich. http://perlmaven.com/xml-simple-sorting

Ich wollte eine natürliche Sortierung gegen einen Tag-Schlüssel durchführen und erreichte dies durch Überschreiben von XML :: Simple hash_to_array. Ich kopierte grundsätzlich die Methode von XML :: Simple.pm und machte eine kleine Bearbeitungs für die natürliche Art - wie folgt aus:

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 => { ... 

Sicher, es ist hässlich, Sentinel würde festlegen müssen ‚i‘ als KeyAttr und seine 5 Jahre zu spät, aber es funktioniert und jetzt kann ich etwas anderes gehen :)

+0

Während dieser Link beantworten kann die Frage, es ist besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz zur Verfügung zu stellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert – abarisone

Verwandte Themen