2010-11-12 16 views
5

Ya he visto algunas respuestas en varios lugares, con respecto a establecer el orden de los elementos XML que se devuelven por XMLout. Sin embargo, no puedo resolver un problema usando esas respuestas/ejemplos.XML :: Orden de elemento de salida simple de hash complejo

Tengo un script que necesita generar algunos datos XML, y ciertos elementos deben imprimirse en cierto orden. Hash es bastante complejo y no pude lograr ningún resultado anulando sorted_keys en el objeto XML::Simple. Bueno, lo hice, pero no de la manera que quería.

El código de ejemplo está debajo, los detalles sobre el problema están debajo del código.

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

Por lo tanto, la salida de este script será la siguiente:

<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 elementos se imprimen, y el orden de salida es por orden alfabético, por clasificación en name atributo. El orden de salida es AAA, GGG, ZZZ.

Sin embargo, lo que necesitaría es tener la salida mientras se ordena (numéricamente, de menor a mayor) en el elemento i. Entonces esa salida estará en orden ZZZ, AAA, GGG.

No tengo control sobre el orden en el hash (no sin usar el módulo Tie::...), así que no puedo hacerlo de esa manera. Si uso NoSort => 1, la salida no será ordenada por nada en particular, así que terminaré obteniendo resultados aleatorios.

Por lo tanto, estoy bastante seguro de que debe haber una manera de resolver esto de la manera que yo lo deseo al anular la subrutina sorted_keys. Sin embargo, no pude obtener los resultados que quería, porque sorted_keys se invoca para cada instancia de item_list. Cuando se invoca sorted_keys para el elemento opt, entonces simplemente tengo acceso a la referencia completa de hash, pero de nuevo no hay forma de garantizar el pedido de salida sin confiar en el módulo Tie::.

Ahora, he conseguido que esto funcione como yo quiero, mediante el uso de Tie::IxHash módulo, a continuación, anulando sorted_keys y (re) creación de un subhash item_list, volviendo a insertar los valores de hash de original en la nueva (ordenada), entonces eliminando subhash en hash original y sustituyéndolo por hash ordenado nuevo.

Algo como esto:

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

Aunque esto funciona (y hasta ahora parece funcionar de forma fiable), yo creo que tiene que haber una manera de lograr esto sin usar Tie::IxHash módulo y hacer todo lo que la recreación de hash/reordenando, y solo de alguna manera clasificando/devolviendo cierta información desde sorted_keys.

No puedo entenderlo, y realmente no entiendo cómo se supone que sorted_keys funciona (especialmente cuando se obtienen resultados diferentes con conjuntos de datos de entrada diferentes o complejos;), pero espero que haya alguien por ahí que sabe esto.

Es decir, han intentado modificar XML/Simple.pm sí mismo y cambiar el orden de clasificación en la última línea de retorno de subrutina sorted_keys, pero todavía era conseguir de forma alfanumérica ordenados de salida. Me temo que no puedo entender cómo lo modificaría para que no se ordene en name, sino en i.

Cualquier ayuda es muy apreciada :)

+0

TLDR, bit +1 para la minuciosidad del esfuerzo (en mi humilde opinión), habría hecho +2 si pudiera :) – DVK

Respuesta

1

creo que en este punto que ya no caben en XML :: Simple. Si le importa el orden de los elementos secundarios de un elemento, entonces es hora de usar un módulo XML-ish más. Para el estilo de creación XML que desee, tal vez XML::TreeBuilder, mire el método new_from_lol. O XML :: LibXML, XML :: Twig, XML :: Writer ...

También intenté mezclar Tie :: IxHash y XML :: Simple en el pasado, y no era bonito. en realidad llegaste bastante lejos aquí. Pero creo que de esta manera yace la locura

0

¿Tal vez eche un vistazo a la eliminación de hash_to_array? Funcionó para mí http://perlmaven.com/xml-simple-sorting

Quería realizar una clasificación natural contra una clave de etiqueta y logré sobrescribir XML :: Simple hash_to_array. Yo, básicamente copiado el método de XML :: Simple.pm e hice una pequeña edición para el tipo natural, - como esto:

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

Claro, es feo, centinela habría que configurar 'i' como KeyAttr y su 5 años demasiado tarde, pero funciona y ahora puedo ir a hacer otra cosa :)

+0

Mientras que este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Las respuestas solo de enlace pueden dejar de ser válidas si la página vinculada cambia – abarisone