2009-02-12 4 views
4

J'utilise Perl XML::Simple pour analyser XML profondément imbriqué et voudrait extraire une petite liste d'éléments sur les 4 niveaux bas:Existe-t-il un moyen simple d'extraire des valeurs profondément imbriquées avec XML :: Simple?

A 
    B 
    C 
     D1 
     D2 
     D3 

Idéalement je veux faire sur l'étape d'entrée, si possible. Comme ceci:

my @list = XMLin($xml, { SomeAttribute => 'ButWhat?' }); 

se retrouver avec la même chose que si je l'ai fait:

@list = ('D1', 'D2', 'D3') 

Est-il possible? Ou juste pas si simple?

Répondre

0

Merci pour toutes les suggestions. En fin de compte, j'ai esquivé le problème de la traversée de la structure de données en utilisant un bloc d'évaluation.

my $xml_tree; 
my @list; 

eval { 

    # just go for it 
    my @list = @{ $xml_tree->{A}->{B}->{C} }; 

}; 

if ([email protected]) { 
    say "oops - xml is not in expected format - and that happens sometimes"; 
} 
+0

Je ne pense pas que vous ayez besoin de beaucoup -> 's - $ xml_tree -> {A} {B} {C} devrait fonctionner correctement. –

3

En supposant que vos données en mémoire ressemble à:

my $parsed = { 
    A => { 
     B => { 
      C => [ qw/here is your list/ ], 
     }, 
    }, 
}; 

Ensuite, vous pouvez obtenir votre liste avec my @list = @{ $parsed->{A}{B}{C} }.

Est-ce ce que vous essayez de faire? Edit: en tenant compte de certains commentaires, peut-être vous voulez Data::Visitor::Callback. Vous pouvez ensuite extraire tous les tableaux comme:

my @arrays; 
my $v = Data::Visitor::Callback->new(
    array => sub { push @arrays, $_ }, 
); 
$v->visit($parsed_xml); 

Après cela court, \ @arrays sera une liste de références à tableaux imbriqués arbitrairement profondément.

Enfin, si vous avez juste un nom d'attribut et que vous voulez rechercher des noeuds XML correspondants, vous voulez vraiment XPath:

use XML::LibXML; 
my $parser = XML::LibXML->new; 
my $doc = $parser->parse_string($xml_string); 

# yeah, I am naming the variable data. so there. 
my @data = map { $_->textContent } $doc->findnodes('//p[@id="foo"]'); 

Quoi qu'il en soit, TMTOWTDI. Si vous travaillez avec XML, et voulez faire quelque chose de compliqué, XML :: Simple est rarement la bonne réponse. J'utilise XML :: LibXML pour tout, puisque c'est presque toujours plus facile. Une autre chose, vous pouvez vouloir Data::DPath. Il laisse vous "XPath" une structure de données Perl en mémoire:

+0

Merci pour la réponse. Oui, je sais que je peux le faire - mais j'espérais ne pas avoir à tester l'existence de tous les niveaux dans le hachage pour accéder à la liste. –

+0

L'astuce, c'est que vous devez savoir combien de niveaux de profondeur vous allez aller avant de commencer. –

0

Le fait que vous utilisez XML :: Simple n'est pas pertinent; vous essayez de rechercher une structure de refs et de refs array. Savez-vous ce que vous cherchez? Sera-t-il toujours au même endroit? Si oui, alors quelque chose comme ce que jrockway a écrit fera l'affaire facilement. Si non, alors vous devrez marcher chaque morceau de la structure jusqu'à ce que vous trouviez ce que vous cherchez. Une chose que je fais souvent est de vider la structure que XML :: Simple retourne en utilisant Data::Dumper, pour voir à quoi ça ressemble (si elle "regarde" toujours la même chose, sinon, vous pouvez déterminer dynamiquement comment marcher c'est en testant que quelque chose est un ref et quel type de ref c'est). La vraie question est: que cherchez-vous?

0

Data::Diver fournit une interface agréable pour creuser dans des structures profondes.

1

En m'appuyant sur Jon's answer, voici le code de base que j'utilise lorsque j'ai besoin de faire ce genre de chose. Si j'ai besoin de quelque chose de colombophile, j'attrape habituellement un module si je suis autorisé à le faire.

L'astuce dans get_values commence par la référence de niveau supérieur, obtient le niveau inférieur suivant et le place dans la même variable. Ça continue jusqu'à ce que j'arrive là où je veux être. La plupart du code est juste des assertions pour s'assurer que les choses fonctionnent bien. Dans la plupart des cas, je trouve que ce sont les données qui sont foirées, pas la traversée (mais je fais beaucoup de travail de nettoyage des données). Ajustez la vérification d'erreur pour votre situation.

 
use Carp qw(croak); 

my $parsed = { 
    A => { 
    B => { 
     C => [ qw/here is your list/ ], 
     D => { 
     E => [ qw/this is a deeper list/ ], 
     }, 
    }, 
    }, 
}; 

my @keys = qw(A B C D); 

my @values = eval { get_values($parsed, @keys) } or die; 

$" = " ][ "; 
print "Values are [ @values ]\n"; 

sub get_values 
    { 
    my($hash, @keys) = @_; 

    my $v = $hash; # starting reference 

    foreach my $key (@keys) 
     { 
     croak "Value is not a hash ref [at $key!]\n" unless ref $v eq ref {}; 
     croak "Key $key does not exist!\n" unless exists $v->{$key}; 
     $v = $v->{$key}; # replace with ref down one level 
     } 

    croak "Value is not an array ref!" unless ref $v eq ref []; 
    @$v; 
    } 
Questions connexes