2
votes

Perl XML obtient les détails de l'enfant du même parent

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?


1 commentaires

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


4 Réponses :


4
votes

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()');
...


0 commentaires

1
votes

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.


0 commentaires

3
votes

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.


0 commentaires

2
votes

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>


1 commentaires

Mojo :: DOM est en effet une façon tellement amusante de gérer le HTML ou le XML. Aimer.