supprimer certaines balises d'un fichier xml

supprimer certaines balises d'un fichier xml - Perl - Programmation

Marsh Posté le 14-02-2018 à 16:34:52    

Bonjour
 
J'ai réussi à construire un code permettant d'extraire des infos d'un fichier xml et les écrire dans un fichier texte grâce à la librairie TWIG.
Aujourd'hui, je cherche à adapter ce code pour supprimer certaines lignes et générer un xml allégé. je me suis tournée vers la fonction "twig_print_outside_roots".  
Mon problème est que la sélection des lignes à supprimer ne fonctionne pas, càd que toutes les balises sont supprimées sans distinction.
 
Mon fichier xml d'entrée :

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <OUT>
  3. <IN>
  4.     <SIMULATION affectation="dynamique" pasdetemps="1" periode_affectation="10801" proc_deceleration="false" seed="1" titre="">
  5.     </SIMULATION>
  6. </IN>
  7.     <INSTANTS>
  8.         <INST nbVeh="0" val="1.00">
  9.             <CREATIONS/>
  10.             <TRAJS/>
  11.             <STREAMS/>
  12.             <SGTS/>
  13.         </INST>
  14.         <INST nbVeh="1" val="2.00">
  15.             <CREATIONS/>
  16.             <TRAJS>
  17.                 <TRAJ abs="650804.90" acc="-1.00" dst="14.41" id="0" ord="6861842.70" tron="TRONROUT0000000000193776" type="VL" vit="13.00" voie="3" z="0.00"/>
  18.             </TRAJS>
  19.         </INST>
  20.         <INST nbVeh="3" val="3.00">
  21.             <CREATIONS/>
  22.             <TRAJS>
  23.                 <TRAJ abs="650804.90" acc="-1.00" dst="14.41" id="0" ord="6861842.70" tron="TRONROUT0000000000193776" type="VL" vit="13.00" voie="3" z="0.00"/>
  24.                 <TRAJ abs="650631.10" acc="0.50" dst="10.42" id="5" ord="6861493.18" tron="TRONROUT0000000000195409INDIRECT" type="VL" vit="9.55" voie="2" z="0.00"/>
  25.                 <TRAJ abs="651309.38" acc="0.50" dst="36.52" id="6" ord="6861503.32" tron="TRONROUT0000000000195361_0" type="VL" vit="10.80" voie="1" z="0.00"/>
  26.             </TRAJS>
  27.         </INST>
  28.     </INSTANTS>
  29. </OUT>


 
 
Le code testé :

Code :
  1. #!/bin/perl -w
  2. use strict;
  3. use XML::Twig;
  4. my $FichierXML = 'test.xml';
  5. my $FichierOUT = 'traj_mini.xml';
  6. open( my $OUT, '>', $FichierOUT )
  7. or die("Impossible d'ouvrir le fichier $FichierOUT\n$!" );
  8. my $line ="TRONROUT0000000000193776";
  9.                                            
  10. my $twig = new XML::Twig(
  11. twig_roots    => { 'TRAJ' => 1 },
  12.  twig_print_outside_roots => $OUT,
  13.  Twig_handlers => {
  14.   'TRAJ' => \&Fonction,},
  15.   PrettyPrint =>'indented',
  16. );
  17.    
  18. $twig->parsefile( $FichierXML);             
  19. sub Fonction
  20. { my( $twig, $TRAJ)= @_; 
  21.     
  22.  if ($TRAJ->att('tron') ne "TRONROUT0000000000193776" )
  23.  {$TRAJ->cut;}
  24. }


 
 
Merci pour votre aide


Message édité par nafawana le 15-02-2018 à 14:40:12
Reply

Marsh Posté le 14-02-2018 à 16:34:52   

Reply

Marsh Posté le 15-02-2018 à 23:32:18    

Comme ça fait un bon exercice de XSLT, je vais y répondre dans ce langage:
 
A la base, en xslt c'est simple:

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">    
  3.    <xsl:mode on-no-match="shallow-copy"/>
  4.    <!-- On copie tout sauf les nœuds TRAJ qui n'ont pas la bonne valeur d'attribut -->
  5.    <xsl:template match="TRAJ[@tron ne 'TRONROUT0000000000193776']"/>
  6. </xsl:stylesheet>


 
Si tu veux exactement le même formatage en sortie qu'en entrée, comme tu n'as pas de modèle (dtd, rng...) associé, le parseur xsl ne peut savoir si ton formatage entre les TRAJ consiste en vrai nœuds texte significatifs pour le modèle ou pas. Il faut alors faire a la main le boulot pour ces nœuds texte.
Idem pour le formatage après la déclaration xml.
Ça donne une feuille de style un poil plus complexe, mais à peine:

Code :
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">    
  3.    <xsl:mode on-no-match="shallow-copy"/>
  4.    <xsl:param name="filter-value" select="'TRONROUT0000000000193776'"/>
  5.    <!-- Pour conserver le formatage après la déclaration -->
  6.    <xsl:template match="/">
  7.        <xsl:text>&#10;</xsl:text>
  8.        <xsl:copy>
  9.            <xsl:apply-templates select="*"/>
  10.        </xsl:copy>
  11.    </xsl:template>
  12.    <!-- copie des nœuds avec la bonne valeur d'attribut et des nœuds texte ad-hoc -->
  13.    <!-- ie ceux avant une ligne non effacée et le dernier au cas ou toutes les lignes avec TRAJ seraient effacées -->
  14.    <xsl:template match="TRAJS">
  15.        <xsl:copy>
  16.            <xsl:apply-templates select="TRAJ[@tron=$filter-value]
  17.                |text()[following-sibling::*[1][self::TRAJ[@tron=$filter-value]]]
  18.                |text()[position()=last()]"/>
  19.        </xsl:copy>
  20.    </xsl:template>
  21. </xsl:stylesheet>


Et tant que j'y étais, j'ai passé la valeur d'attribut filtrante comme un paramètre de la feuille de style (donc pouvant être positionné à l'appel) de valeur par défaut celle que tu donnes dans ton exemple.
 
Et sinon, pour ton programme perl, c'est parce que dans ton handler, tu cut les elements avec la mauvaise valeur d'attribut, mais tu ne dis pas ce que tu fais avec ceux qui ont la bonne: $TRAJ->print.
 
Ce qui donne en mettant tout ça au propre:

Code :
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;
  4. use autodie;
  5. use XML::Twig;
  6.  
  7. sub parse_xml {
  8.    my($infile, $outfile, $filter) = @_;
  9.    
  10.    open(my $fhout, '>', $outfile);
  11.    my $twig = XML::Twig->new(
  12.         twig_roots => { 'TRAJ' => 1 },
  13.         twig_print_outside_roots => $fhout,
  14.         twig_handlers => {'TRAJ' => sub{handler(@_, $filter);},},
  15.         PrettyPrint =>'indented',
  16.     )->parsefile($infile);
  17. }
  18.  
  19. sub handler {
  20.    my ($twig, $TRAJ, $filter)= @_;
  21.    ($TRAJ->att('tron') ne $filter)?$TRAJ->cut:$TRAJ->print;
  22. }
  23.  
  24. parse_xml('test.xml', 'traj_mini.xml', 'TRONROUT0000000000193776');


En sortie, c'est identique a ce que donnent mes 5 lignes de xsl du début de mon post.
 
A+,


Message édité par gilou le 16-02-2018 à 14:03:54

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 16-02-2018 à 23:38:09    

Bonjour Gilou et merci!
 
 :pt1cable: effectivement il ne manquait pas grand chose même avec un code pas très propre!
 
Alors en réalité, il me faut filtrer une liste d'attributs issue d'un fichier texte, par exemple :

Code :
  1. TRONROUT0000000000193776
  2. TRONROUT0000000000195361_0


 
J'avais pensé utiliser la fonction index sur une longue chaine concaténée... et puis j'ai choisi de tester un hash couplé à la fonction exists, une première pour moi! Pas déçue, ça fonctionne bien! :bounce:  
J'ai adapté le fichier d'entrée:

Code :
  1. TRONROUT0000000000193776:mini
  2. TRONROUT0000000000195361_0:mini


 
Et voici le code de Gilou que j'ai adapté :

Code :
  1. #!/usr/bin/env perl
  2.     use strict;
  3.     use warnings;
  4.     use autodie;
  5.     use XML::Twig;
  6.    
  7.    my $FichierTXT = "ListTron.txt";
  8. open( my $TXT, '<', $FichierTXT )
  9. or die "Ouverture impossible de $FichierTXT\n$!";
  10. my %ListTron;
  11. while (<$TXT> ) {
  12.  chomp $_;
  13.  (my $link,my $zone) = split /:/, $_;
  14.  $ListTron{$link} = $zone;
  15. }
  16.  
  17.     sub parse_xml {
  18.         my($infile, $outfile, %ListTron) = @_;
  19.        
  20.         open(my $fhout, '>', $outfile);
  21.         my $twig = XML::Twig->new(
  22.             twig_roots => { 'TRAJ' => 1 },
  23.             twig_print_outside_roots => $fhout,
  24.             twig_handlers => {'TRAJ' => sub{handler(@_, %ListTron);},},
  25.             PrettyPrint =>'indented',
  26.         )->parsefile($infile);
  27.     }
  28.    
  29.     sub handler {
  30.         my ($twig, $TRAJ, %ListTron)= @_;
  31.        
  32.         my $tron = $TRAJ->att('tron'); 
  33.         if (exists ($ListTron{$tron})){$TRAJ->print;}
  34.         else{$TRAJ->cut;}
  35.     }
  36.    
  37.     parse_xml('test.xml', 'traj_mini.xml', %ListTron);


 
Point de détail : mon fichier de sortie comprend des lignes vides, est-ce qu'on peut les retirer?

Code :
  1. <INSTANTS>
  2.         <INST nbVeh="0" val="1.00">
  3.             <CREATIONS/>
  4.             <TRAJS/>
  5.             <STREAMS/>
  6.             <SGTS/>
  7.         </INST>
  8.         <INST nbVeh="1" val="2.00">
  9.             <CREATIONS/>
  10.             <TRAJS>
  11.                
  12.   <TRAJ abs="650804.90" acc="-1.00" dst="14.41" id="0" ord="6861842.70" tron="TRONROUT0000000000193776" type="VL" vit="13.00" voie="3" z="0.00"/>
  13.             </TRAJS>
  14.         </INST>
  15.         <INST nbVeh="3" val="3.00">
  16.             <CREATIONS/>
  17.             <TRAJS>
  18.                
  19.   <TRAJ abs="650804.90" acc="-1.00" dst="14.41" id="0" ord="6861842.70" tron="TRONROUT0000000000193776" type="VL" vit="13.00" voie="3" z="0.00"/>
  20.                
  21.                
  22.   <TRAJ abs="651309.38" acc="0.50" dst="36.52" id="6" ord="6861503.32" tron="TRONROUT0000000000195361_0" type="VL" vit="10.80" voie="1" z="0.00"/>
  23.             </TRAJS>
  24.         </INST>
  25.     </INSTANTS>


Message édité par nafawana le 16-02-2018 à 23:41:29
Reply

Marsh Posté le 17-02-2018 à 16:33:52    

Dans ce cas la on peut faire bien plus simple:
 

Code :
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;
  4. use autodie;
  5. use XML::Twig;
  6.  
  7. sub parse_xml {
  8.    my($infile, $outfile, @filter) = @_;
  9.    
  10.    my $twig = XML::Twig->new(
  11.         twig_handlers => {'TRAJ' => sub{handler(@_, @filter);},},
  12.         PrettyPrint => 'indented',
  13.    )->parsefile($infile);
  14.    open(my $fhout, '>', $outfile);
  15.    $twig->print($fhout);
  16. }
  17.  
  18. sub handler {
  19.    my ($twig, $TRAJ, @filter)= @_;
  20.    my $value = $TRAJ->att('tron');
  21.    $TRAJ->cut unless (grep(/^$value$/, @filter));
  22. }
  23.  
  24. parse_xml('nafawana.xml', 'awanafan.xml', 'TRONROUT0000000000193776', 'TRONROUT0000000000195361_0');


Et je te laisse décider de la meilleure manière de passer ta liste à la fonction qui dorénavant accepte une liste en dernier argument.
 
Les lignes vides, c'est parce que tu avais déclaré TRAJ en root + twig_print_outside_roots.  
twig_print_outside_roots fait qu'il copie jusqu'a une balise TRAJ, saut de ligne inclus, avant d'appeler le handler.
Comme tu veux la même racine, on laisse tomber la déclaration de TRAJ en racine. Un va utiliser twig pour construire un nouvel arbre, sans les éléments TRAJ avec un attribut a une mauvaise valeur (donc plus de print dans le handler) et on imprime tout l'arbre construit, ce qui n'a aucune raison de générer des lignes vides.
 
A+,


Message édité par gilou le 17-02-2018 à 16:46:26

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed