Séquences bioinformatiques et XML
gilles.hunault "at" univ-angers.fr
Table des matières cliquable
1. Formats de séquences biologiques en bioinformatique
1. Formats de séquences biologiques en bioinformatique
Dans sa version minimale, une séquence bioinformatique se réduit à un couple (identifiant, séquence codée). L'idenfiant est parfois un numéro d'accession pour une banque de données bioinformatiques comme AAA18335, BN000065, 424143 ou P14602, ou un simple mot explicite, comme HSP_27. La séquence est soit nucléotidique (ADN) soit polypeptidique (protéines). Dans le premier cas, l'alphabet correspond aux 4 lettres A, C, G et T. Dans le second cas, il se compose des 20 lettres du code IUPAC. Les séquences sont obtenues de plus en plus rapidement et de plus en plus massivement (2014) pour des couts assez faibles. Voir page exemple la page séquençage de notre collègue Emmanuel JASPARD de l'université d'Angers.
Le format dit Fasta, qui est un "vieux" format texte non XML utilise une syntaxe particulière pour stocker une séquence. En voici un exemple nucléotique (fastaxmp_adn.txt) :
>Sangsue, ACE aatttaaaaatgaatttaataaatttttcatacttaaatttgctttttggtgccggtttatttagcgttttagaaagcgc tacaatattaaataccgaatcggatgctaaaaaatggctgacaacgtataacgatgaagccggaaaatatatttacgatg caactgaagcagaatggaattacaacaccaacctgactgatcacaatttaggaatttctattaaaaaatcaaatgatttg gctacttttacggaacaaaaggcaatcgaggccaataaaaaatttgtatggaaaaattttactgatccacttttgaaaag agaattttcaaaaataactgacattggtactgctagcctttcagatgaagactttcaaaagatgtcaggtttgaactctg atctaacaaaaatttacagcactgcaaaagtttgtaacaagcctaacgacccatctggaaaatgctatcctttagatcct gatttgtccgacataatctccaagtcaaacgatctcgaggaattgacctgggcatggaaaggttggagggatgcgtctgg caaacatatgcccgataaatatgatgaatttgttcaactgctcaacaaagctgctaagattcatggatatgaagacaacg gggattattggaggtcctggtacgagtcccccacgttcagaaaggattgtgaagatttgtggcaggagatcaaaccattc tacgaacaactgcatgcatacgtcagaaggaagctgcagaagaagtatccccaaattgcattccccaaggaggggcccat ccctgctcatctgctcggcaacatgtgggcccaatcgtgggagaacatagagtacttgttatgggcccaatcgtgggaga acatagagtacttgttaaggcccgctcctgaccttcctagcatggacatcactgaggaactcgtcaaacagaactacacg gcattgaaactcttccaactgtcggacacatttttcaaatccttgggtctcatccagatgcctcagccgttttgggaaaa gtcgatgatcgagaaaccagctgatcgggatgtgttcagaatcaaacaatgcgtttgccatgcgtcagcctgggacttct acaatcgcaaggatacggttgtggacatgcactggttcatgacgactcaccatgagatgggacacatcgaatactacctc cactacaaggaccaacccatcagtttcagatctggcgctaatccaggatttcatgaggccattgccgatattgcatcact gtcagtggccacacctgaatatatgcaatccgtcagcctgttgcctaatttcactgacgatccaaatggcgatttaaact tcttaatgaaccaagccttaacgaaggtggccttcctaccattcggttacctgatcgaccagtggagatgggacgtgttc tcgggagatacccctcgaccaaaatacaactccaagtggtggcacaacaggtgtaagtaccagggcatatatcctccagt gaaaaggtcagagcaagattttgatgccggttccaagttccatgtacccaacaacactccatacatcaggtactttgttg ctcacgtcatccaattccaattccatgaagccctgtgcaaggctgccaacaacagcagacctctacatagatgtaacatc gccaattccaaggaagctggagagaaactggctgaattgatgaaatctggatcttcaattccgtggcctaaagttctaga aaatcttactggatcggaaaaaatgtcagcgaaatctctcatggcctattacaaaccgttgatcgattggcctgaaaaaa gaaaaccaagggcagaaaattggatgggaggaaaaatgtcctcctggatcatttgaaccatgaaattatttatttgattt tatgtcatttcataatttttctaccacttttttaataaacttaggtgcctattgaatatgttcttgcaatttgaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >AB000263 acaagatgccattgtcccccggcctcctgctgctgctgctctccggggccacggccaccgctgccctgcc cctggagggtggccccaccggccgagacagcgagcatatgcaggaagcggcaggaataaggaaaagcagc ctcctgactttcctcgcttggtggtttgagtggacctcccaggccagtgccgggcccctcataggagagg aagctcgggaggtggccaggcggcaggaaggcgcacccccccagcaatccgcgcgccgggacagaatgcc ctgcaggaacttcttctggaagaccttctcctcctgcaaataaaacctcacccatgaatgctcacgcaag tttaattacagacctgaaet un exemple polypeptidique (fastaxmp_pro.txt) :
>A2ZDX4 MEYQGQHGGHASSRADEHGNPAVTTGNAPTGMGAGHIQEPAREDKKTDGVLRRSGSSSSSSSSEDDGMGGRRKKGIKEKI KEKLPGGNKGNNQQQQQEHTTTTTGGAYGPQGHDTKIATGAHGGTAATTADAGGEKKGIVDKIKEKLPGQH >A2ZDX6 MENYQGQHGYGADRVDVYGNPVAGQYGGGATAPGGGHGVMGMGGHHAGAGGQFQPVKEEHKTGGILHRSGSSSSSSSSED DGMGGRRKKGIKEKIKEKLPGGNKGNNHQQQQMMGNTGGAYGQQGHAGMTGAGTGTGVHGAEYGNTGEKKGFMDKIKEKL PGQH >AAA33480 MEDERNTQQHQGGEQAQDQENEVKDRGLLDSLLGRNKHDDQEKKNQQEEEELATGMEKVTVAEPDHKEEGHEAAEKKDSL LAKLHRTSSSSSSSSDDEEEEVIDENGEIVKRKKKGLKEKVKEKSAAHKAHDEGDHHQPGVPAPAPAPPVAVDTHAHHQE GEHKPHFPAPAPPPHVETHHPVVVHKIEDDDTKTQTPPQAPEEEKKGLLDKIKEKLPGGHKKPEDAAAAAAAPAVHAPPP PAPHAEVDVSSPDGKKGLLGKIMDKIPGYHKSSGEEDRKDAAGEHKTSS >AAB18201 MEDERSTQSYQGGEAAEQVEVTDRGLLGNLLGKKKAEEDKEKKEEELVTGMEKVSVEEPEVKKEEHVDGEKKETLFSKLH RSSSSSSSSSDEEEEEVIDDNGEVIKRKKKKGLKEKLQEKLPGHKDTEGEHVTALPAPAAPASVQTHHDTDVVVEKIDGD VKTEATPAVPEEEKKGFLEKIKEKLPGGHKKPEDAAAVPVTHAAPAPVTHAAPAPVHAPAPAAEEVSSPDAKEKKGLLGK IMDKLPGYHKTGEEDKAAAATGEHKPSA >AAB18202 MEDERSTQSYQGGEAAEQVEVTDRGLLGNLLGKKKAEEDKEKQEELVTGMEKVSVEEPEVKKEEHEDGEKKETLFSKLHR SSSSSSSSSDEEEEEVIDDNGEVIKRKKKKGLKEKLKEKLPGHKDTEGEHVTGLPAPAAPASVQTHHDTDVVVEKIDGDV KTEAAPAVPEEEKKGFLEKIKEKLPGGHKKPEDAAPVPVTHAAPAPVHAPAPAAEEVSSPDAKEKKGLLGKIMDKLPGYH KTGEEDKAAAAAGEHKPSALe plus gros reproche qu'on peut faire à ce format, très simple et encore très utilisé en 2012, est qu'il n'autorise pas l'écriture de commentaires.
Dans une version plus développée, une séquence peut comporter une longueur de séquence, une ou plusieurs indications de dates, une ou plusieurs indications d'auteurs et d'articles, les valeurs d'autres identifiants pour les autres base de données bioinformatiques, la taxonomie liée au gène, à la protéine ou à l'organisme hôte ou cible, des descriptions de portions codantes, etc.
A l'usage, il semble bien que les formats XML retenus par les grands organismes comme le NCBI, UNIPROT ou l'EBI et son ENA utilisent majoritairement des éléments et non pas des attributs pour la séquence. Ainsi on trouve presque exclusivement ce que nous nommerons le format fastaXmlElt dont voici un exemple (fastaxmpelt.xml) :
<?xml version="1.0" ?> <!-- Fichier fastaxmpelt.xml --> <proteins> <protein> <identifiant>A2ZDX4</identifiant> <sequence> MEYQGQHGGHASSRADEHGNPAVTTGNAPTGMGAGHIQEPAREDKKTDGVLRRSGSSSSSSSSEDDGMGGRRKKGIKEKI KEKLPGGNKGNNQQQQQEHTTTTTGGAYGPQGHDTKIATGAHGGTAATTADAGGEKKGIVDKIKEKLPGQH</sequence> </protein> <protein> <identifiant>A2ZDX6</identifiant> <sequence> MENYQGQHGYGADRVDVYGNPVAGQYGGGATAPGGGHGVMGMGGHHAGAGGQFQPVKEEHKTGGILHRSGSSSSSSSSED DGMGGRRKKGIKEKIKEKLPGGNKGNNHQQQQMMGNTGGAYGQQGHAGMTGAGTGTGVHGAEYGNTGEKKGFMDKIKEKL PGQH </sequence> </protein> <protein> <identifiant>AAA33480</identifiant> <sequence> MEDERNTQQHQGGEQAQDQENEVKDRGLLDSLLGRNKHDDQEKKNQQEEEELATGMEKVTVAEPDHKEEGHEAAEKKDSL LAKLHRTSSSSSSSSDDEEEEVIDENGEIVKRKKKGLKEKVKEKSAAHKAHDEGDHHQPGVPAPAPAPPVAVDTHAHHQE GEHKPHFPAPAPPPHVETHHPVVVHKIEDDDTKTQTPPQAPEEEKKGLLDKIKEKLPGGHKKPEDAAAAAAAPAVHAPPP PAPHAEVDVSSPDGKKGLLGKIMDKIPGYHKSSGEEDRKDAAGEHKTSS </sequence> </protein> <protein> <identifiant>AAB18201</identifiant> <sequence> MEDERSTQSYQGGEAAEQVEVTDRGLLGNLLGKKKAEEDKEKKEEELVTGMEKVSVEEPEVKKEEHVDGEKKETLFSKLH RSSSSSSSSSDEEEEEVIDDNGEVIKRKKKKGLKEKLQEKLPGHKDTEGEHVTALPAPAAPASVQTHHDTDVVVEKIDGD VKTEATPAVPEEEKKGFLEKIKEKLPGGHKKPEDAAAVPVTHAAPAPVTHAAPAPVHAPAPAAEEVSSPDAKEKKGLLGK IMDKLPGYHKTGEEDKAAAATGEHKPSA </sequence> </protein> <protein> <identifiant>AAB18202</identifiant> <sequence> MEDERSTQSYQGGEAAEQVEVTDRGLLGNLLGKKKAEEDKEKQEELVTGMEKVSVEEPEVKKEEHEDGEKKETLFSKLHR SSSSSSSSSDEEEEEVIDDNGEVIKRKKKKGLKEKLKEKLPGHKDTEGEHVTGLPAPAAPASVQTHHDTDVVVEKIDGDV KTEAAPAVPEEEKKGFLEKIKEKLPGGHKKPEDAAPVPVTHAAPAPVHAPAPAAEEVSSPDAKEKKGLLGKIMDKLPGYH KTGEEDKAAAAAGEHKPSA </sequence> </protein> </proteins>plutôt que le format que nous nommerons fastaXmlAtt comme ci-dessous (fastaxmpatt.xml) :
<?xml version="1.0" encoding="UTF-8" ?> <!-- Fichier fastaxmpatt.xml --> <proteins> <protein identifiant="A2ZDX4" sequence="MEYQGQHGGHASSRADEHGNPAVTTGNAPTGMGAGHIQEPAREDKKTDGVLRRSGSSSSSSSSEDDGMGGRRKKGIKEKI KEKLPGGNKGNNQQQQQEHTTTTTGGAYGPQGHDTKIATGAHGGTAATTADAGGEKKGIVDKIKEKLPGQH" /> <protein identifiant="A2ZDX6" sequence="MENYQGQHGYGADRVDVYGNPVAGQYGGGATAPGGGHGVMGMGGHHAGAGGQFQPVKEEHKTGGILHRSGSSSSSSSSED DGMGGRRKKGIKEKIKEKLPGGNKGNNHQQQQMMGNTGGAYGQQGHAGMTGAGTGTGVHGAEYGNTGEKKGFMDKIKEKL PGQH" /> <protein identifiant="AAA33480" sequence="MEDERNTQQHQGGEQAQDQENEVKDRGLLDSLLGRNKHDDQEKKNQQEEEELATGMEKVTVAEPDHKEEGHEAAEKKDSL LAKLHRTSSSSSSSSDDEEEEVIDENGEIVKRKKKGLKEKVKEKSAAHKAHDEGDHHQPGVPAPAPAPPVAVDTHAHHQE GEHKPHFPAPAPPPHVETHHPVVVHKIEDDDTKTQTPPQAPEEEKKGLLDKIKEKLPGGHKKPEDAAAAAAAPAVHAPPP PAPHAEVDVSSPDGKKGLLGKIMDKIPGYHKSSGEEDRKDAAGEHKTSS" /> <protein identifiant="AAB18201" sequence="MEDERSTQSYQGGEAAEQVEVTDRGLLGNLLGKKKAEEDKEKKEEELVTGMEKVSVEEPEVKKEEHVDGEKKETLFSKLH RSSSSSSSSSDEEEEEVIDDNGEVIKRKKKKGLKEKLQEKLPGHKDTEGEHVTALPAPAAPASVQTHHDTDVVVEKIDGD VKTEATPAVPEEEKKGFLEKIKEKLPGGHKKPEDAAAVPVTHAAPAPVTHAAPAPVHAPAPAAEEVSSPDAKEKKGLLGK IMDKLPGYHKTGEEDKAAAATGEHKPSA" /> <protein identifiant="AAB18202" sequence="MEDERSTQSYQGGEAAEQVEVTDRGLLGNLLGKKKAEEDKEKQEELVTGMEKVSVEEPEVKKEEHEDGEKKETLFSKLHR SSSSSSSSSDEEEEEVIDDNGEVIKRKKKKGLKEKLKEKLPGHKDTEGEHVTGLPAPAAPASVQTHHDTDVVVEKIDGDV KTEAAPAVPEEEKKGFLEKIKEKLPGGHKKPEDAAPVPVTHAAPAPVHAPAPAAEEVSSPDAKEKKGLLGKIMDKLPGYH KTGEEDKAAAAAGEHKPSA" /> </proteins>Afin de repérer facilement des séquences considéres considérées comme communes, il est d'usage d'ajouter une indication de classe (ou "groupe"). Par exemple, le fichier suivant, au format fasta traditionnel, permet de distinguer 2 protéines de classe 1 et 3 protéines de classe 2 via l'indication sur la ligne de l'identifiant après le symbole | (ou "pipe") (fastacl1.txt) :
>A2ZDX4 | Class 1 MEYQGQHGGHASSRADEHGNPAVTTGNAPTGMGAGHIQEPAREDKKTDGVLRRSGSSSSSSSSEDDGMGGRRKKGIKEKI KEKLPGGNKGNNQQQQQEHTTTTTGGAYGPQGHDTKIATGAHGGTAATTADAGGEKKGIVDKIKEKLPGQH >A2ZDX6 | Class 1 MENYQGQHGYGADRVDVYGNPVAGQYGGGATAPGGGHGVMGMGGHHAGAGGQFQPVKEEHKTGGILHRSGSSSSSSSSED DGMGGRRKKGIKEKIKEKLPGGNKGNNHQQQQMMGNTGGAYGQQGHAGMTGAGTGTGVHGAEYGNTGEKKGFMDKIKEKL PGQH >AAA33480 | Class 2 MEDERNTQQHQGGEQAQDQENEVKDRGLLDSLLGRNKHDDQEKKNQQEEEELATGMEKVTVAEPDHKEEGHEAAEKKDSL LAKLHRTSSSSSSSSDDEEEEVIDENGEIVKRKKKGLKEKVKEKSAAHKAHDEGDHHQPGVPAPAPAPPVAVDTHAHHQE GEHKPHFPAPAPPPHVETHHPVVVHKIEDDDTKTQTPPQAPEEEKKGLLDKIKEKLPGGHKKPEDAAAAAAAPAVHAPPP PAPHAEVDVSSPDGKKGLLGKIMDKIPGYHKSSGEEDRKDAAGEHKTSS >AAB18201 | Class 2 MEDERSTQSYQGGEAAEQVEVTDRGLLGNLLGKKKAEEDKEKKEEELVTGMEKVSVEEPEVKKEEHVDGEKKETLFSKLH RSSSSSSSSSDEEEEEVIDDNGEVIKRKKKKGLKEKLQEKLPGHKDTEGEHVTALPAPAAPASVQTHHDTDVVVEKIDGD VKTEATPAVPEEEKKGFLEKIKEKLPGGHKKPEDAAAVPVTHAAPAPVTHAAPAPVHAPAPAAEEVSSPDAKEKKGLLGK IMDKLPGYHKTGEEDKAAAATGEHKPSA >AAB18202 | Class 2 MEDERSTQSYQGGEAAEQVEVTDRGLLGNLLGKKKAEEDKEKQEELVTGMEKVSVEEPEVKKEEHEDGEKKETLFSKLHR SSSSSSSSSDEEEEEVIDDNGEVIKRKKKKGLKEKLKEKLPGHKDTEGEHVTGLPAPAAPASVQTHHDTDVVVEKIDGDV KTEAAPAVPEEEKKGFLEKIKEKLPGGHKKPEDAAPVPVTHAAPAPVHAPAPAAEEVSSPDAKEKKGLLGKIMDKLPGYH KTGEEDKAAAAAGEHKPSAToutefois, il peut être parfois plus simple de mettre le numéro de classe comme faisant partie de l'identifiant, comme suit (fastacl2.txt) :
>A2ZDX4_1 MEYQGQHGGHASSRADEHGNPAVTTGNAPTGMGAGHIQEPAREDKKTDGVLRRSGSSSSSSSSEDDGMGGRRKKGIKEKI KEKLPGGNKGNNQQQQQEHTTTTTGGAYGPQGHDTKIATGAHGGTAATTADAGGEKKGIVDKIKEKLPGQH >A2ZDX6_1 MENYQGQHGYGADRVDVYGNPVAGQYGGGATAPGGGHGVMGMGGHHAGAGGQFQPVKEEHKTGGILHRSGSSSSSSSSED DGMGGRRKKGIKEKIKEKLPGGNKGNNHQQQQMMGNTGGAYGQQGHAGMTGAGTGTGVHGAEYGNTGEKKGFMDKIKEKL PGQH >AAA33480_2 MEDERNTQQHQGGEQAQDQENEVKDRGLLDSLLGRNKHDDQEKKNQQEEEELATGMEKVTVAEPDHKEEGHEAAEKKDSL LAKLHRTSSSSSSSSDDEEEEVIDENGEIVKRKKKGLKEKVKEKSAAHKAHDEGDHHQPGVPAPAPAPPVAVDTHAHHQE GEHKPHFPAPAPPPHVETHHPVVVHKIEDDDTKTQTPPQAPEEEKKGLLDKIKEKLPGGHKKPEDAAAAAAAPAVHAPPP PAPHAEVDVSSPDGKKGLLGKIMDKIPGYHKSSGEEDRKDAAGEHKTSS >AAB18201_2 MEDERSTQSYQGGEAAEQVEVTDRGLLGNLLGKKKAEEDKEKKEEELVTGMEKVSVEEPEVKKEEHVDGEKKETLFSKLH RSSSSSSSSSDEEEEEVIDDNGEVIKRKKKKGLKEKLQEKLPGHKDTEGEHVTALPAPAAPASVQTHHDTDVVVEKIDGD VKTEATPAVPEEEKKGFLEKIKEKLPGGHKKPEDAAAVPVTHAAPAPVTHAAPAPVHAPAPAAEEVSSPDAKEKKGLLGK IMDKLPGYHKTGEEDKAAAATGEHKPSA >AAB18202_2 MEDERSTQSYQGGEAAEQVEVTDRGLLGNLLGKKKAEEDKEKQEELVTGMEKVSVEEPEVKKEEHEDGEKKETLFSKLHR SSSSSSSSSDEEEEEVIDDNGEVIKRKKKKGLKEKLKEKLPGHKDTEGEHVTGLPAPAAPASVQTHHDTDVVVEKIDGDV KTEAAPAVPEEEKKGFLEKIKEKLPGGHKKPEDAAPVPVTHAAPAPVHAPAPAAEEVSSPDAKEKKGLLGKIMDKLPGYH KTGEEDKAAAAAGEHKPSAPour XML, le choix se pose à nouveau de faire de la classe une partie de l'identifant (fastacl01.xml), de l'implémenter comme un attribut (fastacl02.xml), ou alors de soit d'utiliser un élément (fastacl03.xml), à moins d'utiliser un "panachage" (fastacl04.xml) :
fastacl01.xml fastacl02.xml fastacl03.xml fastacl04.xml
Enfin, les séquences étant en général de longueur variable, une indication de longueur, en élément ou attribut est souvent utile, à titre de vérification ou controle, d'où de nombreuses possibilités de stockage :
On pourra consulter ici le code-source PHP qui génère le fichier XML via l'API DOM de PHP 5.
2. Grammaires et vérifications
Le format Fasta est simple, et les éléments de base qu'il met en jeu (identifiant et séquence) sont peu nombreux. Toutefois, les vérifications qui s'imposent, notamment au niveau de l'alphabet pour la séquence fasta, ne peuvent pas s'écrire avec une DTD. Pour la longueur, nous essaierons de rajouter la contrainte qu'il faut au moins 20 acides aminés pour avoir une séquence polypeptitique "valide", même les grands centres autorisent le stockage de "segments" ou "portions" ou "fragments" avec moins de 20 caractères. Enfin, on admettra qu'une classe doit contenir au moins deux protéines et qu'un fichier XML de protéines doit toujours contenir au moins une protéine. Par contre, il n'est pas clair que deux identifiants distincts doivent correspondre à deux séquences distinctes et nous traiterons donc ce problème plus tard.
Afin que ce texte soit progressif et pédagogique, nous allons essayer d'exploiter au maximum les grammaires DTD avant d'utiliser les schémas XSD car les DTD sont plus simples à écrire que les XSD.
2.1 Grammaires DTD
Commençons par écrire une DTD pour le format fastaXmlElt dont fastaxmpelt.xml est un exemple. L'élément racine est proteins et il doit y avoir au moins un élément protein, d'où la déclaration 1 de fastaelt1.dtd. Chaque élément protein doit contenir dans cet ordre un élément identifiant et un élément sequence, d'où la déclaration 2. Un identifiant et une séquence sont a priori des chaines de caractères, donc une première grammaire de type DTD est simplement fastaelt1.dtd :
<!-- Grammaire fastaelt1.dtd --> <!ELEMENT proteins (protein)+ > <!ELEMENT protein (identifiant,sequence) > <!ELEMENT identifiant (#PCDATA) > <!ELEMENT sequence (#PCDATA) >Si l'identifiant est composé d'un seul mot et est bien unique (ce qui peut ne pas être le cas quand on fusionne des fichiers XML issus de diférentes bases de données), il serait bon de pouvoir vérifier l'unicité des identifiants. Malheureusement, ceci ne peut pas s'écrire dans une DTD, pas plus que le fait que les lettres de la séquence ne sont pas toutes les lettres possibles de l'alphabet. On se contentera donc de la grammaire précédente.
Si maintenant on décide d'utiliser le format fastaXmlAtt dont fastaxmpatt.xml est un exemple, il est possible de déclarer l'identifiant comme un id au sens des DTD, sous réserve bien sûr que l'identifiant corresponde à un seul mot, d'où la grammaire fastaatt1.dtd :
<!-- Grammaire fastaatt1.dtd --> <!ELEMENT proteins (protein)+ > <!ELEMENT protein EMPTY > <!ATTLIST protein identifiant ID #REQUIRED > <!ATTLIST protein sequence CDATA #REQUIRED >Avec un exemple un peu plus structuré où une séquence comporte un attribut class facultatif et un attribut longueur facultatif, comme pour fastamixte1.xml et fastamixte2.xml, on peut écrire une grammaire fastamixte1.dtd comme :
<!-- Grammaire fastamixte1.dtd --> <!ELEMENT proteins (protein)+ > <!ELEMENT protein (identifiant,sequence) > <!ELEMENT identifiant (#PCDATA) > <!ELEMENT sequence (#PCDATA) > <!ATTLIST protein class CDATA #IMPLIED > <!ATTLIST sequence longueur CDATA #IMPLIED >Pour en finir avec les DTD, on utilise désormais un élément classe vide facultatif avec un numéro, un nom et une longueur facultatifs, comme pour fastamixte3.xml et fastamixte4.xml, d'où la grammaire fastamixte2.dtd :
<!-- Grammaire fastamixte2.dtd --> <!ELEMENT proteins (protein)+ > <!ELEMENT protein (class?,identifiant,sequence) > <!ELEMENT class EMPTY > <!ELEMENT identifiant (#PCDATA) > <!ELEMENT sequence (#PCDATA) > <!ATTLIST class num CDATA #IMPLIED > <!ATTLIST class nom CDATA #IMPLIED > <!ATTLIST sequence longueur CDATA #IMPLIED >On ne confondra pas cette dernière grammaire avec celle qui impose un élément classe obligatoire avec des attributs facultatifs soit fastamixte3.dtd à tester avec fastamixte5.xml et fastamixte6.xml :
<!-- Grammaire fastamixte3.dtd --> <!ELEMENT proteins (protein+) > <!ELEMENT protein (class,identifiant,sequence) > <!ELEMENT class EMPTY > <!ELEMENT identifiant (#PCDATA) > <!ELEMENT sequence (#PCDATA) > <!ATTLIST class num CDATA #IMPLIED > <!ATTLIST class nom CDATA #IMPLIED > <!ATTLIST sequence longueur CDATA #IMPLIED >2.2 Grammaires XSD
Reprenons le format fastaXmlElt dont fastaxmpelt.xml est un exemple. L'élément racine est proteins et il doit y avoir au moins un élément protein, d'où le texte maxOccurs="unbounded" car minOccurs="1" est la valeur par défaut. Chaque élément protein doit contenir dans cet ordre un élément identifiant et un élément sequence, d'où la deuxième construction <xsd:complexType><xsd:sequence>. Un identifiant et une séquence sont a priori des chaines de caractères, donc une première grammaire de type XSD est simplement fastaelt1.xsd, copiée sur le modèle de fastaelt1.dtd :
<?xml version="1.0" ?><!-- Fichier fastaelt1.xsd --> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" > <xsd:element name="proteins"> <xsd:complexType> <xsd:sequence> <xsd:element name="protein" maxOccurs="unbounded"> <xsd:complexType> <xsd:sequence> <xsd:element name="identifiant" type="xsd:string" /> <xsd:element name="sequence" type="xsd:string" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema>Pour profiter pleinement de XSD, nous allons commencer par séparer la définition des éléments identifiant et sequence. Pour cela, on utilise <xsd:element ref="identifiant" /> au lieu de <xsd:element name="identifiant" /> en rajoutant en fin de fichier la définition de identifiant. On procède de façon identique pour sequence :
<?xml version="1.0" ?><!-- Fichier fastaelt2.xsd --> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" > <xsd:element name="proteins"> <xsd:complexType> <xsd:sequence> <xsd:element name="protein" maxOccurs="unbounded"> <xsd:complexType> <xsd:sequence> <xsd:element ref="identifiant" /> <xsd:element ref="sequence" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="identifiant" type="xsd:string" /> <xsd:element name="sequence" type="xsd:string" /> </xsd:schema>Rajoutons maintenant nos contraintes initiales, à savoir une séquence a une longueur de 20 caractères au moins, et ses lettres font partie de ACDEFGHIKLMNPQRSTVWY seulement. Une grammaire comme fastaelt3.xsd déclare invalide notre fichier initial fastaxmpelt.xml (astucieusement recopié en fastaxmpelt.txt) car il contient des espaces, des retours-charriot. Un "vrai" fichier XML valide pour cette grammaire est fastaxmpelt2.xml qui se voit bien une fois recopié en fastaxmpelt2.txt.
<?xml version="1.0" ?><!-- Fichier fastaelt3.xsd --> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" > <xsd:element name="proteins"> <xsd:complexType> <xsd:sequence> <xsd:element name="protein" maxOccurs="unbounded"> <xsd:complexType> <xsd:sequence> <xsd:element ref="identifiant" /> <xsd:element ref="sequence" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="identifiant" type="xsd:string" /> <xsd:simpleType name="sequenceAA"> <xsd:restriction base="xsd:string"> <xsd:minLength value="20" /> <xsd:pattern value="[ACDEFGHIKLMNPQRSTVWY]+" /> </xsd:restriction> </xsd:simpleType> <xsd:element name="sequence" type="sequenceAA" /> </xsd:schema>Reprenons l'exemple un peu plus structuré où une séquence comporte un attribut class facultatif et un attribut longueur facultatif, comme pour fastamixte7.xml et fastamixte8.xml. Une première grammaire possible est fastaelt4.xsd :
<?xml version="1.0" ?><!-- Fichier fastaelt4.xsd --> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" > <xsd:element name="proteins"> <xsd:complexType> <xsd:sequence> <xsd:element name="protein" maxOccurs="unbounded"> <xsd:complexType> <xsd:sequence> <xsd:element ref="identifiant" /> <xsd:element ref="sequence" /> </xsd:sequence> <xsd:attribute ref="class" use="optional" /> </xsd:complexType> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="identifiant" type="xsd:string" /> <xsd:element name="sequence" type="sequenceAvecLongueurFacultative" /> <xsd:simpleType name="sequenceAA"> <xsd:restriction base="xsd:string"> <xsd:minLength value="20" /> <xsd:pattern value="[ACDEFGHIKLMNPQRSTVWY]+" /> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="nosLongueurs"> <xsd:restriction base="xsd:nonNegativeInteger"> <xsd:minInclusive value="20" /> </xsd:restriction> </xsd:simpleType> <xsd:complexType name="sequenceAvecLongueurFacultative"> <xsd:simpleContent> <xsd:extension base="sequenceAA"> <xsd:attribute ref="longueur" use="optional" /> </xsd:extension> </xsd:simpleContent> </xsd:complexType> <xsd:attribute name="class" type="xsd:nonNegativeInteger" /> <xsd:attribute name="longueur" type="nosLongueurs" /> </xsd:schema>Toutefois, il est dommage d'avoir à copier/coller notre définition de sequenceAA. Il est beaucoup plus intéressant de mettre cette définition dans un autre fichier, disons bioinfdefs.xsd et d'inclure ce fichier dans la grammaire, soit le fichier fastaelt5.xsd :
<?xml version="1.0" ?><!-- Fichier Fastaelt5.xsd --> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" > <xsd:include schemaLocation="bioinfdefs.xsd" /> <xsd:element name="proteins"> <xsd:complexType> <xsd:sequence> <xsd:element name="protein" maxOccurs="unbounded"> <xsd:complexType> <xsd:sequence> <xsd:element ref="identifiant" /> <xsd:element ref="sequence" /> </xsd:sequence> <xsd:attribute ref="class" use="optional" /> </xsd:complexType> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="identifiant" type="xsd:string" /> <xsd:element name="sequence" type="sequenceAvecLongueurFacultative" /> <xsd:attribute name="class" type="xsd:nonNegativeInteger" /> <!-- inutile car incluse, mais utile pour rappeler qu'il existe un attribut longueur <xsd:attribute name="longueur" type="nosLongueurs" /> --> </xsd:schema>2.3 Exemple de script de vérification :
# Fichier cmdsval.txt ## Fichiers conformes xmlstarlet val -w fastaxmpelt.xml xmlstarlet val -w fastaxmpatt.xml xmlstarlet val -w fastacl01.xml xmlstarlet val -w fastacl02.xml xmlstarlet val -w fastacl03.xml xmlstarlet val -w fastamixte1.xml xmlstarlet val -w fastamixte2.xml xmlstarlet val -w fastamixte3.xml xmlstarlet val -w fastamixte4.xml xmlstarlet val -w fastamixte5.xml xmlstarlet val -w fastamixte6.xml xmlstarlet val -w fastamixte7.xml xmlstarlet val -w fastamixte8.xml xmlstarlet val -w fastaxmpelt2.xml ## DTDs xmllint --noout --dtdvalid fastaelt1.dtd fastaxmpelt.xml xmllint --noout --dtdvalid fastaatt1.dtd fastaxmpatt.xml xmllint --noout --dtdvalid fastamixte1.dtd fastamixte2.xml xmllint --noout --dtdvalid fastamixte2.dtd fastamixte3.xml xmllint --noout --dtdvalid fastamixte2.dtd fastamixte4.xml xmllint --noout --dtdvalid fastamixte3.dtd fastamixte5.xml xmllint --noout --dtdvalid fastamixte3.dtd fastamixte6.xml # la commande suivante provoque une erreur (volontairement) xmllint --noout --dtdvalid fastamixte3.dtd fastamixte4.xml ## XSDs xmllint --noout --schema fastaelt1.xsd fastaxmpelt.xml xmllint --noout --schema fastaelt2.xsd fastaxmpelt.xml # la commande suivante provoque une erreur (volontairement) xmllint --noout --schema fastaelt3.xsd fastaxmpelt.xml xmllint --noout --schema fastaelt3.xsd fastaxmpelt2.xml xmllint --noout --schema fastaelt4.xsd fastaxmpelt7.xml xmllint --noout --schema fastaelt4.xsd fastaxmpelt8.xml xmllint --noout --schema fastaelt5.xsd fastaxmpelt8.xml3. Transformations simples
Il n'est pas possible de passer de fastaxmp_pro.txt (format fasta texte traditionnel) à fastaxmpelt.xml (format fasta XML avec éléments) ou à fastaxmpatt.xml (format fasta XML avec attributs) puisque le fichier d'entrée n'est pas au format XML. Par contre les transformations inverses sont possibles et faciles à écrire. De même, passer du format élément au format attribut et réciproquement est très simple, au moins au départ, car obtenir des fichiers "proprement indentés" requiert plus de finesse :
Du format fastaXmlAtt au format fastaXmlElt Du format fastaXmlElt au format fastaXmlAtt <?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version="1.0"> <!-- Fichier att2elt.xsl --> <!-- conversion d'attributs en éléments version 1, voir aussi att2eltv2.xsl --> <xsl:output method="xml" encoding="UTF-8" /> <xsl:template match="/"> <xsl:element name="proteines"> <xsl:apply-templates match="protein" /> </xsl:element> </xsl:template> <xsl:template match="protein"> <xsl:element name="proteine"> <xsl:element name="identifiant"> <xsl:value-of select="@identifiant" /> </xsl:element> <xsl:element name="sequence"> <xsl:value-of select="@sequence" /> </xsl:element> </xsl:element> </xsl:template> </xsl:stylesheet> <?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version="1.0"> <!-- Fichier elt2att.xsl --> <!-- conversion d'éléments en attributs --> <xsl:output method="xml" encoding="UTF-8" /> <xsl:template match="/"> <xsl:element name="proteines"> <xsl:apply-templates match="protein" /> </xsl:element> </xsl:template> <xsl:template match="protein"> <xsl:element name="proteine"> <xsl:attribute name="identifiant"> <xsl:value-of select="identifiant" /> </xsl:attribute> <xsl:attribute name="sequence"> <xsl:value-of select="translate(sequence,' 	
','')" /> </xsl:attribute> </xsl:element> </xsl:template> </xsl:stylesheet>Du format fastaXmlAtt au format fasta texte traditionnel Du format fastaXmlElt au format fasta texte traditionnel <?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version="1.0"> <!-- Fichier att2txt.xsl --> <!-- conversion d'attributs au format texte version 1, voir aussi att2txtv2.xsl --> <xsl:output method="text" encoding="UTF-8" /> <xsl:template match="protein"> <xsl:text>></xsl:text> <xsl:value-of select="@identifiant" /> <xsl:text> </xsl:text> <xsl:value-of select="@sequence" /> </xsl:template> </xsl:stylesheet> <?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version="1.0"> <!-- Fichier elt2txt.xsl --> <!-- conversion d'éléments au format texte version 1, voir aussi elt2txtv2.xsl --> <xsl:output method="text" encoding="UTF-8" /> <xsl:template match="protein"> <xsl:text>></xsl:text> <xsl:value-of select="identifiant" /> <xsl:text> </xsl:text> <xsl:value-of select="sequence" /> </xsl:template> </xsl:stylesheet>On testera avec attention la progression des transformations att2eltv1.xsl, att2eltv2.xslet att2eltv3.xsl afin de comprendre comment on peut raffiner une solution, pour passer de att2eltv1_res.xml à att2eltv2_res.xml et att2eltv3_res.xml.
Pour vérifier la validité des fichiers produits, on pourra utiliser la grammaire fastaelte1.dtd pour laquelle l'élément racine proteines (donc avec un "e" en plus) contient des éléments proteines (donc avec un "e" en plus aussi).
D'autres transformations simples à effectuer sont celles qui consistent
à convertir et supprimer la classe en fin d'identifiant dans fastacl01.xml pour en faire un élément class via cu2ec.xsl
<?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version="1.0"> <!-- Fichier cu2ec.xsl --> <!-- on passe de <protein> <identifiant>A2ZDX4_1</identifiant> ... à <protein> <identifiant>A2ZDX4</identifiant> <class>1</class> ... --> <!-- le fichier inclus contient entre autres sdl (saut de ligne) --> <xsl:import href="stdWeb.xsl" /> <xsl:output method="xml" encoding="UTF-8" /> <xsl:template match="proteins"> <proteins> <xsl:call-template name="sdl" /> <xsl:for-each select="protein"> <protein> <xsl:call-template name="sdl" /> <identifiant> <xsl:value-of select="substring-before(identifiant,'_')" /> </identifiant> <xsl:call-template name="sdl" /> <class> <xsl:value-of select="substring-after(identifiant,'_')" /> </class> <xsl:call-template name="sdl" /> <sequence> <xsl:value-of select="sequence" /> </sequence> <xsl:call-template name="sdl" /> </protein> <xsl:call-template name="sdl" /> </xsl:for-each> </proteins> </xsl:template> </xsl:stylesheet>Fichier résultat de la transformation : cu2ec_res.xml.
à convertir et supprimer l'attribut classe dans fastacl02.xml pour l'ajouter à l'identifiant via attaddclass.xsl
<?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version="1.0"> <!-- Fichier attaddclass.xsl --> <!-- on passe de <protein class="1"> <identifiant>A2ZDX4</identifiant> <sequence> ... à <protein> <identifiant>A2ZDX4_1</identifiant> <sequence> ... --> <!-- le fichier inclus contient entre autres sdl (saut de ligne) --> <xsl:import href="stdWeb.xsl" /> <xsl:output method="xml" encoding="UTF-8" /> <xsl:template match="proteins"> <proteins> <xsl:call-template name="sdl" /> <xsl:for-each select="protein"> <protein> <xsl:call-template name="sdl" /> <identifiant> <xsl:value-of select="concat(identifiant,'_',@class)" /> </identifiant> <xsl:call-template name="sdl" /> <sequence> <xsl:value-of select="sequence" /> </sequence> <xsl:call-template name="sdl" /> </protein> <xsl:call-template name="sdl" /> </xsl:for-each> </proteins> </xsl:template> </xsl:stylesheet>Fichier résultat de la transformation : attaddclass_res.xml.
à calculer et ajouter la longueur de la séquence dans fastacl03.xml en tant qu'élément via eltaddlng.xsl
<?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version="1.0"> <!-- Fichier eltaddlng.xsl --> <!-- on passe de <protein> <identifiant>A2ZDX4</identifiant> <class>1</class> <sequence> ... à <protein> <identifiant>A2ZDX4</identifiant> <class>1</class> <longueur>171</longueur> <sequence> ... --> <!-- le fichier inclus contient entre autres sdl (saut de ligne) --> <xsl:import href="stdWeb.xsl" /> <xsl:output method="xml" encoding="UTF-8" /> <xsl:template match="proteins"> <proteins> <xsl:call-template name="sdl" /> <xsl:for-each select="protein"> <protein> <xsl:call-template name="sdl" /> <identifiant> <xsl:value-of select="identifiant" /> </identifiant> <xsl:call-template name="sdl" /> <class> <xsl:value-of select="class" /> </class> <xsl:call-template name="sdl" /> <longueur> <xsl:value-of select="string-length(sequence)" /> </longueur> <xsl:call-template name="sdl" /> <sequence> <xsl:value-of select="sequence" /> </sequence> <xsl:call-template name="sdl" /> </protein> <xsl:call-template name="sdl" /> </xsl:for-each> </proteins> </xsl:template> </xsl:stylesheet>Fichier résultat de la transformation : eltaddlng_res.xml.
à calculer et ajouter la longueur de la séquence dans fastacl04.xml en tant qu'attribut via attaddlng.xsl
<?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version="1.0"> <!-- Fichier attaddlngv2.xsl --> <!-- on passe de <protein> <identifiant>A2ZDX4</identifiant> <class num="1" /> <sequence> ... à <protein> <identifiant>A2ZDX4</identifiant> <class>1</class> <sequence longueur="171"> ... --> <!-- le fichier inclus contient entre autres sdl (saut de ligne) --> <xsl:import href="stdWeb.xsl" /> <xsl:output method="xml" encoding="UTF-8" /> <xsl:template match="proteins"> <proteins> <xsl:call-template name="sdl" /> <xsl:for-each select="protein"> <protein> <xsl:call-template name="sdl" /> <identifiant> <xsl:value-of select="identifiant" /> </identifiant> <xsl:call-template name="sdl" /> <class> <xsl:value-of select="class/@num" /> </class> <xsl:call-template name="sdl" /> <!-- ## ce qui suit n'est pas optimisé car on utilise deux fois translate() <xsl:element name="sequence"> <xsl:attribute name="longueur"> <xsl:value-of select="string-length(translate(sequence,' ',''))" /> </xsl:attribute> <xsl:value-of select="translate(sequence,' ','')" /> </xsl:element> ## il vaut mieux définir une variable qui contient le résultat de translate : --> <xsl:variable name="nouvelleSequence" select="translate(./sequence,' ','')" /> <xsl:element name="sequence"> <xsl:attribute name="longueur"> <xsl:value-of select="string-length($nouvelleSequence)" /> </xsl:attribute> <xsl:value-of select="$nouvelleSequence" /> </xsl:element> <xsl:call-template name="sdl" /> </protein> <xsl:call-template name="sdl" /> </xsl:for-each> </proteins> </xsl:template> </xsl:stylesheet>Fichier résultat de la transformation : eltaddlng_res.xml.
Enfin, Excel et Open Office Calc savent lire les fichiers-texte délimités par des points-virgules. On vérifiera que la transformation att2csv.xsl appliquée au fichier icls_att.xml produit le fichier convatt.csv et qu'il est directement utilisable sous Excel et OpenOffice (on a tronqué les séquences pour plus de lisibilité).
<?xml version="1.0" encoding="UTF-8"?> <!-- Fichier icls_att.xml --> <proteins> <protein identifiant="XA2ZDX4" classe="1" longueur="15" sequence="MEYQGQHGGHASSR" /> <protein identifiant="XA2ZDX6" classe="1" longueur="16" sequence="MENYQGQHGYGADRV" /> <protein identifiant="XAAA33480" classe="2" longueur="14" sequence="MEDERNTQQHQGG" /> <protein identifiant="XAAB18201" classe="2" longueur="12" sequence="MEDERSTQSYQ" /> <protein identifiant="XAAB18202" classe="2" longueur="15" sequence="MEDERSTQSYQGGE" /> </proteins><?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version="1.0"> <!-- Fichier att2csv.xsl --> <!-- conversion d'attributs au format CSV pour excel --> <!-- le fichier inclus contient entre autres sdl (saut de ligne) --> <xsl:import href="stdWeb.xsl" /> <xsl:output method="text" encoding="ISO8859-15" /> <xsl:template match="proteins"> <!-- une ligne d'en-tête pour Excel --> <xsl:text>identifiant ; classe ; longueur ; séquence AA </xsl:text> <xsl:call-template name="sdl" /> <!-- puis toutes les informations séparées par des points-virgules --> <xsl:for-each select="protein"> <xsl:value-of select="@identifiant" /> <xsl:text> ; </xsl:text> <xsl:value-of select="@classe" /> <xsl:text> ; </xsl:text> <xsl:value-of select="@longueur" /> <xsl:text> ; </xsl:text> <xsl:value-of select="translate(@sequence,' ','')" /> <xsl:call-template name="sdl" /> </xsl:for-each> </xsl:template> </xsl:stylesheet>identifiant ; classe ; longueur ; séquence AA XA2ZDX4 ; 1 ; 15 ; MEYQGQHGGHASSR XA2ZDX6 ; 1 ; 16 ; MENYQGQHGYGADRV XAAA33480 ; 2 ; 14 ; MEDERNTQQHQGG XAAB18201 ; 2 ; 12 ; MEDERSTQSYQ XAAB18202 ; 2 ; 15 ; MEDERSTQSYQGGEOn remarquera que la sortie est en ISO8859-15 pour Excel.
4. Fichiers XML des grands centres de bioinformatique
Chaque grand centre (NCBI, UNIPROT, EMBL...) a son propre format, sa propre stratégie de gestion, ses outils et ses API.
Quelques exemples
Voir également la question 7 de WEBrd01.
Retour à la page principale de (gH)