J'ai le XML suivant et j'espère obtenir l'élément enfant du même parent si une expression régulière correspond à un autre élément enfant. Le problème est que le XML a des balises de dénomination en double partout, il est donc difficile de simplement faire Movie-> Year car il y a de nombreux éléments de film.
Par exemple
Données:
XXX
Perl
my $simple = XML::Simple->new( ); my $tree = $simple->XMLin($_); my $movie = $tree->{movie}{title}; if($movie =~ /Titanic/) { # $movie -> year ??? # desired output = 1997 }
Quelle est la manière la plus simple de faire cela avec XML :: Simple?
4 Réponses :
Il n'y a pas de moyen facile avec XML :: Simple car c'est l'analyseur XML le plus difficile à utiliser . Sa propre documentation met en garde contre son utilisation. ("L'utilisation de ce module dans un nouveau code est fortement déconseillée .")
Ce que vous avez là n'est pas du XML valide, nous devons donc d'abord le rendre XML valide p>
use XML::LibXML qw( ); my $parser = XML::LibXML->new(); my $doc = $parser->parse_string("<movies>$not_quite_xml</movies>"); my ($movie_node) = $doc->findnodes('/movies/movie[title/text()="Titanic"]') or die("Titanic not found\n"); my $year = $movie_node->findvalue('year/text()'); ...
Vous pouvez également appeler un outil de ligne de commande comme xmlstarlet
à partir de Perl pour extraire rapidement uniquement les informations dont vous avez besoin.
Par exemple, si votre fragment de document XML a été stocké dans /tmp/foo.xml
, puis le script shell suivant le convertira en une forme tabulaire qui est plus facile à traiter en Perl en lisant une ligne à la fois.
Titanic|1997 Moneyball|2011 Fight Club|1999
prints
{ echo '<movies>' ; cat /tmp/foo.xml ; echo '</movies>'; } \ | xmlstarlet sel -T -t -m '//movie' -v "concat(title, '|', year)" -n
Cette façon particulière de convertir le document xml en une forme plus pratique n'est pas robuste contre les retours à la ligne ou les |
dans les titres de films et nécessite un outil externe, mais c'est facile.
J'espère qu'il a été dit que XML :: Simple
ne devrait pas être utilisé, étant remplacé par de bien meilleurs modules il y a longtemps et " fortement déconseillé " contre par son propre auteur, il y a également des années.
Cet exemple montre un moyen d'utiliser le nœud du parent afin d'interroger les frères et sœurs, comme cela a été spécifiquement demandé. (Je remplis votre échantillon avec le nœud racine
afin d'avoir un XML bien formé.) La réponse d'ikegami montre comment vous pouvez faire plus directement ce dont vous semblez avoir besoin.
Si vous avez une raison de parcourir les nœuds
(peut-être à la recherche d'une variété de titres), alors leurs nœuds frères
peuvent être trouvés par
foreach my $node ($doc->findnodes($xpath)) { if ($node->to_literal =~ /(Titanic)/) { say "Title: $1"; say "\tyear: ", $node->parentNode->findvalue('./year'); } }
S'il y a toujours un seul nœud
sous un nœud
, cela peut être simplifié par le raccourci findvalue
, en remplaçant la boucle sur $ node-> parentNode-> findnodes
, pour
use strict; use warnings; use feature 'say'; use XML::LibXML; my $file = shift || die "Usage: $0 filename\n"; my $doc = XML::LibXML->load_xml(location => $file, no_blanks => 1); my $xpath = '/document/movie/title'; foreach my $node ($doc->findnodes($xpath)) { if ($node->to_literal =~ /(Titanic)/) { say "Title: $1"; foreach my $yr ($node->parentNode->findnodes('./year')) { say "\tyear: ", $yr->to_literal; } } }
Ici nous obtenons le texte directement et il n'y a donc pas besoin de -> to_literal
non plus.
Il existe de nombreuses autres méthodes dans XML :: LibXML :: Node , la classe de base des nœuds utilisée pour dériver d'autres classes particulières. L'un des intérêts ici peut être nextSibling
, comme moyen de parcourir d'autres informations sur le titre dans un
.
Notez que cette bibliothèque complète et riche en fonctionnalités fournit de nombreux autres outils pour travailler avec XML. D'une part, ajouter des détails à votre fichier source, comme des attributs, permettrait d'utiliser les autres atouts de la bibliothèque.
La documentation est répartie sur plusieurs pages. Consultez ce post pour un résumé des liens vers les documents pertinents. Il existe également un tutoriel pour XML :: LibXML , par l'auteur de XML::Simple
.
Encore une autre façon de le faire, avec Mojo :: DOM cette fois. Il n'y a rien à recommander cela par rapport à d'autres solutions (à part XML :: Simple).
Cela ajoute un élément racine puis utilise un sélecteur CSS pour récupérer les titres:
use utf8; use strict; use warnings; my $xml = <<'HERE'; <movies> <movie> <title>Titanic</title> <year>1997</year> <genre>Drama</genre> </movie> <movie> <title>Moneyball</title> <year>2011</year> <genre>Sport/Drama</genre> </movie> <movie> <title>Fight Club</title> <year>1999</year> <genre>Drama/Action</genre> </movie> </movies> HERE use Mojo::DOM; my @movies = Mojo::DOM ->new( $xml ) ->find( 'movies title' ) ->map( 'text' ) ->each; say join "\n", @movies;
p>
Mojo :: DOM est en effet une façon tellement amusante de gérer le HTML ou le XML. Aimer.
Veuillez pas pas non avec
XML :: Simple
. Bien que ce module ait certainement sa place, il est obsolète depuis longtemps, et son propre auteur a "fortement déconseillé" son utilisation depuis des années, et a écrit un tutoriel pour un autre. Choisissez XML :: LibXML ou XML :: Twig