Sam & Max » xml http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 Importer des données, retour d’expérience 11 http://sametmax.com/importer-des-donnees-retour-dexperience/ http://sametmax.com/importer-des-donnees-retour-dexperience/#comments Thu, 16 Jan 2014 15:49:21 +0000 http://sametmax.com/?p=8772 Dédicaçons la chanson de notre article au plus barbu de mes amis poneys.

J’ai importé des données un très grand nombre de fois dans ma vie. Depuis des APIs, des XML, des CSV, du filesystem, des formats binaires, des formats batards, etc

Pour tous les jobs d’import, Python est probablement le meilleur langage au monde. Autant j’aime Python, autant je suis lucide sur le fait qu’en dev Web, Ruby et Javascript sont d’excellentes alternatives. En programmation concurrente, Go dépasse Python. En IA, Lisp est le top de la concurrence.

Mais pour l’import de données, Python est simplement le meilleur langage au monde. Sa capacité à lire énormément de formats facilement, sa force de manipulation de données numériques et texte, sa philosophie d’itération, ses nombreuses libs en font un outil incroyablement souple et puissant.

Malgré cela, on retrouve toujours des grosses difficultés dans l’import de données. Elles sont les mêmes pour tous les langages.

Voici mes 2 centimes.

N’accordez aucune confiance à la donnée

Partez du principe que tout champ peut manquer. Que toute donnée peut être mal formée ou corrompue. Ou fausse.

Même si le service en face est sérieux. J’ai bossé avec des données du service de santé américain, de France Télécom, de startups, d’outils Open Source, de scrapping de sites, de mon pote Maurice, et de mes propres scripts

Vous savez ce qu’ils ont tous en commun ? Aucun ne sont fiables. Aucun.

Ils ont tous des données merdiques à un moment où à un autre.

Ayez donc une approche défensive. Pour CHAQUE champ, posez vous la question : que doit faire le script d’import si il manque ? Si la donnée contenue est foireuse ?

Les outils d’abstraction sont vos amis

Un import, c’est typiquement le genre de taff où les surcouches vont énormément vous aider. ORM, DSL, XML objectify et toute lib qui peut vous éviter de travailler trop proche du format va vous faire gagner un temps fou.

Prenez un peu de temps pour les mettre en place. Même si ils vous font perdre un peu en perf, un gros script d’import devient TRÈS VITE un sac de nœud. Et vous voulez que les problèmes soient facilement identifiables.

Pour cette même raison, virez toute la logique de l’insertion des données en dehors du script. Votre script doit avoir une logique découpée en 3 parties :

  1. Le script d’acquisition, qui charge les données et les passe sous forme brute à un importeur.
  2. Un importeur, qui est capable d’extraire des données raffinées à partir d’un format de données brutes et appeler le bon code d’insertion.
  3. Du code d’insertion, qui attend en paramètre une donnée toujours propre (sans aucun check), et qui se charge uniquement de prendre cette donnée et la mettre dans votre système de stockage (généralement la base de données).

Le script d’acquisition doit rester très simple. Une suite d’instructions logiques pour récupérer la donnée, l’énumérer et la passer à l’importeur. C’est lui qui fait les appels API, qui se connecte au FTP, qui ouvre le CSV, qui parse le XML, etc. Ainsi vous pouvez facilement interchanger les importeurs ou voir si il y a une couille dans la récupération des données.

L’importeur est généralement le code le plus crade. C’est une série de try / except, de logique métier, d’assainissement des données. Vous ne voulez pas de code d’insertion là dedans, car vous voulez que ce code, qui est difficile à débugger et va être celui qui va être modifié toutes les 5 minutes au fur et à mesure que vous découvrez toute les merdes, soit dédié à une seule logique : obtenir de la donnée saine. Ce code sera spaghetti, vous n’y pouvez rien. Mais vous pouvez l’isoler et le commenter à mort.

Le code d’insertion est un code réutilisable. Il se fout de savoir d’où vient la donnée. Il attend un format, et un seul, et toujours de données correctes, et propres. C’est le but des importeurs de lui filer une entrée normalisée et pertinente. Ce code est propre, et doit être très bien testé via des tests unittaires. Il va vous servir plusieurs fois, car c’est le même code qui sera utilisé que vous importiez d’un service X ou un service Y. Il représente VOTRE logique métier. N’insérez pas ce code dans le code d’une autre abstraction (type ORM), ainsi, si vous changez d’outils, vous changez simplement ce code, et l’interface reste la même pour vos importeurs.

Debugging

Votre script va planter. Beaucoup. Souvent.

Un champ absolument indispensable – que la spec papier notait comme toujours présent – va manquer. Un autre champ noté de type int dans le xld contient une lettre. L’encoding n’est pas le bon, alors qu’il l’a toujours été pendant 5 mois.

Ce n’est pas une question de si, c’est une question de quand.

Donc déjà, blindez votre script de log. Quand je dis blindez, je veux dire que chaque if, chaque résultat de check, doit être accompagné d’une ligne affichant l’action en cours, et son contexte (la donnée traitée, de préférence avec un truc pour l’identifier, genre un ID). Quand il plantera à 3 heure du matin sur un truc hubuesque et que le relancer pour obtenir le même état prendra une demi-journée, le log sera votre seul chance de réparer la panne sans engager un psy.

Mettez aussi un gros try / except générique qui loggue toute exception, pour pouvoir faire un debug post mortem. Idéalement, faites le dumper locals() et envoyez-vous un mail d’alerte. Vous ne voulez pas que le script ne tourne pas pendant une journée sans que vous le sachiez.

Mettez des options dans votre scripts pour pouvoir débugger plus facilement. Par exemple, si vous avez de code d’insertion qui est sous forme de tâche asynchrone (type celery), mettez une option --synchronous qui insère le code inline afin de pouvoir utiliser pdb sur tout le script en cas de besoin. Ou alors, si vous avez une grosse archive à décompresser, mettez une option --nozip pour pouvoir sauter cette étape.

Et si vous insérez un break point, mettez le dans une condition du genre :

if id_du_champ_ou_autre_moyen_identifiant == "valeur":
    send_mail('Alerte, on est peut être au bug. Bouge ton fion.')
    import ipdb; ipdp.set_trace()

Comme ça vous pouvez retourner à vos moutons le temps que le breakpoint s’active, ce qui, sur des gros jeux de données, peut prendre énormément de temps.

Enfin, je sais qu’on a tendance à être fainéant et vouloir toujours débugger en direct. Mais faites des mocks. Faites un faux XML, une API bidon, bref, un truc qui vous permet d’insérer des cas d’import avec une données dans le format attendu, et testez votre code avec ça. Pour les petits imports, c’est une perte de temps, mais pour les gros imports, ça va vous faire gagner des jours. Ainsi vous pouvez tester des cas isolé, rajouter des bugs rencontrés, etc. Et en plus ça sert de documentation.

Problèmes courants

Il y a mille et une manière d’avoir un import qui plante, mais il y a généralement 6 grosses foirades qu’on retrouve tout le temps.

Service défaillant

Le service (FTP, API, humain en face, NAS, etc) qui doit vous fournir les données est indisponible. Vous n’y pouvez rien. Envoyez-vous un SMS pour vous prévenir, aujourd’hui c’est facile et ça coûte presque rien. Ainsi vous pourrez dialoguer rapidement avec les personnes responsables de problème.

Champs manquants

Grand classique. Tout champ peut manquer. Tout. Même un ID unique sans lequel la donnée n’a aucune sens. Faites vous un wrapper du genre :

def get_data(champ):
    try:
        # extraire le champ
    except ChampAbsent, ChampMalFormé:
        return None

Et utilisez le partout. Et décidez ce que doit faire votre programme si il rencontre None. Pour TOUS les champs. Si None est une valeur possible, utilisez Ellipsis. Si Ellipsis est une valeur possible, faites vous une classe InvalidData.

Mauvais encoding

Super vicieux. Je vous renvoie à l’article sur l’encoding pour cela.

Donnée aberrante

Impossible à prévoir, très difficile à identifier. Donnée de mauvais type, date ou nombre hors limites, texte dans la mauvaise langue, etc. Vous ne pouvez pas tout prévoir. Pour ça, il faudra faire au fur et à mesure des plantages.

Données mal formatée et malicieuses

En plus de get_data, il vous faut un clean_data. Qui check si on peut processer la donnée sereinement. Pour tous les champs également. C’est con, mais si LEUR système n’escape pas les entrées utilisateurs, c’est VOTRE système dans lequel se retrouve les injections de code.

Performances

La vitesse de votre script sera généralement limitée par 3 facteurs :

  • Vitesse de lecture.
  • Vitesse d’écriture.
  • Vos plantages.

Ces 3 facteurs sont en général très liés.

La meilleur stratégie, c’est d’extrapoler un max de données, et de cacher tout ce qui est cacheable. Par exemple, copiez les données brutes sur vos serveurs (genre si c’est un fichier sur le leur). Copiez les références externes, même si vous n’en avez pas besoin afin d’éviter une query de plus, vous les supprimerez plus tard. Pré-calculez les champs, par exemple age, si vous avez la date de naissance, etc.

Si vous avez beaucoup de checks à faire pour l’assainissement des données, mettez vos données en cache (par exemple dans redis), pour que les looks up soient rapides, ou au moins, ajoutez les index qui vont bien dans la DB (on peut avoir des perfs X10 rien qu’avec ça).

Ensuite, partez du principe que ça va planter souvent, donc :

  • Faites des opérations idempotentes, c’est à dire qu’on peut les relancer autant de fois qu’on veut sans risque. Typiquement, vérifiez si une donnée existe avant l’insertion, et si oui, faites une mise à jour complète.
  • Mettez un historique. Sauvegardez quelque part l’avancement de votre import, afin de ne pas tout recommencer depuis le début après le plantage.

Ah oui, et faites des copies de sauvegarde des données brutes ET des données importées. Pour les premières, parce qu’on est pas à l’abri un “rm -fr” malencontreux. Ne rigolez pas, ça m’est arrivé la semaine dernière. Une semaine à tout DL à nouveau. Pour les secondes, parce que tant que le dernier import n’est pas terminé, on peut toujours corrompre toute sa base avec une couille de dernière minute. Comme un encoding qui change aléatoirement sur une donnée non datée.

Bon sens

Évidement, je parle ici d’un synthèse des problématiques rencontrées. Vous ne pouvez pas appliquer TOUT ça, ou en tout cas, pas au début, ou pas sur des petits scripts, etc. Selon le sérieux de votre source de données, il faudra plus ou moins être défensif. L’expérience et la douleur vous permettra de trouver la juste dose de morphine.

]]>
http://sametmax.com/importer-des-donnees-retour-dexperience/feed/ 11
Qu’est-ce qu’un namespace ? 10 http://sametmax.com/quest-ce-quun-namespace/ http://sametmax.com/quest-ce-quun-namespace/#comments Thu, 28 Nov 2013 06:42:31 +0000 http://sametmax.com/?p=8103 Un mot qui vient et qui revient comme on en a des tas en informatique, et qui est utilisé par tout le monde comme si chacun était supposé savoir.

Très énervant quand on ne sait pas ce que ça veut dire.

Un namespace, ou espace de noms, est simplement un conteneur de noms, un groupe.

On s’en sert pour pouvoir distinguer deux choses qui ont le même nom.

Par exemple, un trou du cul peut être un anus ou un connard. Si quelqu’un fait la mention d’un “trou du cul”, j’ai besoin de contexte pour savoir si il parle d’un orifice ou d’un fils d’horreur. Ce contexte peut être donné par un namespace.

Comment est représenté un namespace dépend de l’outil utilisé.

Ainsi, en XML, c’est un préfixe puis deux points :

<xsd:element></xsd:element>
<taoisme:element></taoisme:element>

Ici j’ai deux balises “element”, mais elles sont préfixées par deux namespaces : xsd et taoisme.

C’est tout. Il n’y a rien d’autre caché derrière le namespace, c’est simplement un moyen de s’assurer qu’on sait bien de quoi on parle.

Certains namespaces sont standardisés. Par exemple, xsd est un namespace réservé aux balises qu’on retrouve dans un fichier .xsd. D’autres sont créés par les utilisateurs pour des raisons d’organisation.

En effet, un des avantages du namespace, c’est qu’il évite que deux références homonymes ne s’écrasent l’une l’autre.

Par exemple en Python, le nom du module qui contient une fonction peut servir de namespace.

Si je fais :

import os
import shelve

Alors j’ai accès à trois fonctions open():

  • os.open(): la fonction du open() du module os dont le nom est préfixé par le namespace os.
  • shelve.open(): la fonction du open() du module shelve dont le nom est prefixé par le namespace shelve.
  • open(): la fonction du open() dont le nom n’est PAS préfixé est qui est donc dans le namespace en cours.

Et aucune fonction ne s’écrase l’une l’autre. Le nom du module, étant utilisé comme namespace, permet la distinction pour l’humain ET la machine.

Dans ce langage, cependant, on peut copier une référence d’un namespace à l’autre. Par exemple :

from datetime import date

Ceci va importer date et le rendre disponible dans le namespace en cours. Du coup on aura pas besoin de préfixer date pour l’utiliser dans le namespace en cours, mais en contrepartie, on ne pourra pas créer de variable date car cela écraserait la référence précédente.

Si vous prenez par exemple PHP 5 qui n’avait pas encore de mécanisme de namespace à l’époque, les dev étaient obligés de créer artificiellement un namespace en allongeant le nom de toutes leurs fonctions avec ‘sujet_’. Ce qui donnait des noms à rallonge du genre : mysql_real_escape_string().

]]>
http://sametmax.com/quest-ce-quun-namespace/feed/ 10
YAML, XML, JSON, CSV, INI… Qu’est-ce que c’est et à quoi ça sert ? 28 http://sametmax.com/yaml-xml-json-csv-ini-quest-ce-que-cest-et-a-quoi-ca-sert/ http://sametmax.com/yaml-xml-json-csv-ini-quest-ce-que-cest-et-a-quoi-ca-sert/#comments Sat, 06 Jul 2013 05:23:09 +0000 http://sametmax.com/?p=6576 évident pour tout le monde...]]> Que voilà de jolis acronymes !

Quand j’ai débuté la programmation, je les rencontrais partout sur le net. On en parlait comme si on parlait d’acheter du pain. Apparemment c’était évident pour tout le monde.

Ça m’a énervé, mais ça m’a énervé !

Et puis j’ai oublié. C’est devenu tellement le quotidien pour moi, tellement banal… Jusqu’à ce que je reçoive ce mail :

Je viens vous quémander un article

Il y a une question que je me pose assez souvent avant de coder quelque chose et bien que j’ai pu me faire quelques opinions au fil du temps, je n’ai jamais trouvé un article ou une conf ou que sais-je qui explique ça clairement.

Ma question concerne le format d’écriture des données.
Entre les fichiers textes genre ini, json, xml, yaml (que j’aime bien), les formats binaires (pickle, voir hdf5 pour les scientifiques et j’en ignore peut être d’autre…), les bases de données mysql, postgre, sqlite.

Je n’ai pas les idées claires sur :
* cas typique d’utilisation
* les plus et les moins
* la corruptibilité
* la sécurité (point fourre-tout)

En gros, mon raisonnement de béotien dit : binaire plus performant que texte mais texte lisible par l’éditeur et ça, ça rassure.

A prendre ou à jeter

Aujourd’hui, je vais donc parler des formats texte, et je ferai un article sur les formats binaires plus tard.

Formats

YAML, XML, JSON, CSV, INI sont des noms de formats de données texte. Il y a plusieurs choses à comprendre ici:

Donnés :
Ca peut être n’importe quoi que vous vouliez sauvegarder ou transmettre : carnet d’adresses, configuration d’un logiciel, contenu d’une base de données, nom/prenom/age/mensurations, etc. Bref, tout groupe d’informations que vous souhaitez pourvoir communiquer, ou relire plus tard.
Format :
Comment sont organisées ces données. Comme on manipule les données avec un ordinateur, il est nécessaire de les ranger données d’une certaine façons. En les rangeant de cette façon, l’ordinateur, si il connait le “format”, est capable d’analyser les données qu’il reçoit. Sinon pour lui elles ne veulent rien dire.
Texte :
En opposition à “binaire” (ce qui est un abus de langage, puisque du texte en informatique, c’est du binaire). Cela signifie que votre format est organisé autour d’un texte lisible par les humains. C’est ce texte qui va dire “ceci est l’age”, “ceci est le nom”, “ceci est la taille de ses ganglions”, etc.

En résumé, un format de données texte, c’est une convention textuelle pour que des ordinateurs puissent échanger des données entre eux et que celui qui reçoit puisse retrouver la même chose que ce que l’on lui a envoyé.

Exemples

Le format XML est une convention qui dit qu’on va mettre les données (les informations qu’on transmet) entre des balises.

Les balises ont la forme suivante : < nomDeBalise >.

Une balise peut contenir une données, ou d’autres balises.

Le choix des balises est laissé à la personne qui créer le XML. Celui qui reçoit le XML doit connaître ces balises pour récupérer les données.

Imaginez un carnet d’adresses avec chaque personne ayant un nom et un numéro de téléphone. Il existe de nombreux moyens de représenter ce carnet d’adresses. L’UN des moyens de possibles, est d’écrire un fichier XML. Par exemple :

<?xml version="1.0" encoding="utf8"?>
<personnes>
    <personne>
        <nom>Sam</nom>
        <numero>555-555-555</numero>
    </personne>
    <personne>
        <nom>Max</nom>
        <numero>1234567890</numero>
    </personne>
    <personne>
        <nom>Bob</nom>
        <numero>666</numero>
    </personne>
</personnes>

Pour vous en tant qu’humain, ça n’apporte rien.

Mais quand vous avez besoin de créer un programme qui peut sauvegarder / lire ces données ou les transmettre à un autre programme (car oui, les programmes doivent pourvoir se parler entre eux, comment vous croyez que cet article arrive sur votre ordinateur ?), il va falloir choisir un format pour ces données.

Chaque format et différent, et possède des avantages et des inconvénients. On peut très bien représenter les mêmes données avec deux formats différents. Ainsi, voici le même carnet d’adresses, mais au format CSV :

"nom";"numero"
"Sam";"555-555-555"
"Max";"1234567890"
"Bob";"666"

Quel format est le meilleur ?

Il n’existe pas de “meilleur” format.

Chaque format a ses caractéristiques, et il existe de nombreux formats. En fait, il existe même des formats dans les formats, des XML avec des balises qui ont des noms standardisés par exemple. Pire, vous pouvez inventer vos propres formats. Mais à moins d’être très bon et de combler un besoin qui ne l’est pas encore, je ne vous le recommande pas.

Aussi il va vous falloir choisir un format selon votre situation. En général, on choisira parmi ces caractéristiques :

  • Facilité de manipulation : votre code va traiter ce format, donc si c’est galère à manipuler, vous allez vous faire du travail en plus.
  • Expressivité : certains formats permettent de “dire” plus de de choses que d’autres. Selon la complexité de vos données, certains seront trop simples ou trop complexes.
  • Pérennité : combien de temps ce format doit être lisible ? Et-il un standard reconnu ? Est-il beaucoup utilisé ? Est-il ouvert ? Avez-vous le droit d’utiliser ce format (car oui, il y a des questions légales) ?
  • Performance : le temps de traitement de ce format convient-il à votre usage ? La place qu’il prend sur le disque ? En RAM ? Le temps de transfert par un réseau ?

Dans notre cas, nous allons étudier des formats textes ouverts qui sont tous des standards, et lisibles par un humain. Aussi la pérennité de vos données est le moindre de vos soucis, si tant est que le créateur du format est compétent et bienveillant.

Dans cet article, nous allons en effet voir uniquement les formats texte. Je garderai les formats binaires pou un article suivant, et un dernier pour les bases de données.

Au passage, les formats textes ont tous ces caractéristiques en commun:

  • Ils sont lisibles et modifiables facilement et avec peu de moyen par les humains.
  • On peut debugger, et même bidouiller un format texte sans trop de souci, même si on a pas de lib faite pour ça.
  • Ils sont plus lents et prennent plus de place que les formats binaires.
  • On les utilisent souvent pour les exports / imports de données, les fichiers de configurations, et dans la transmission d’informations sur le Web entre deux machines.

A noter qu’au passage la sécurité et la corruptibilité ne sont pas des questions liées au format, mais à la méthode de manipulation de ces formats. Donc le choix du format ne prend pas en compte ces questions.

Le format CSV

L’acronyme CSV signifie Coma Separated Values, littéralement valeurs séparées par des virgules. C’est un des formats les plus simples que l’on puisse trouver.

Dans sa version la plus basique, c’est un format ligne à ligne, et chaque ligne représente une entrée (par exemple une personne dans un carnet d’adresses, un objet dans un catalogue, des groupes d’ingrédients dans une liste de recettes, etc).

Pour chaque entrée, il y a une série de valeur, chaque valeur est séparée par des virgules. Reprenons l’exemple du carnet d’adresse :

sam,555-555-555,5 rue des lilas
max,1234567890,thailande
bob,666,7eme cercle

Mais il peut se complexifier dès qu’on veut rajouter des valeurs plus complexes. Par exemple si elles contiennent des virgules, alors il est d’usage d’entourer les valeurs de quotes :

"sam","555-555-555","5, rue des lilas"
"max,"1234567890","Patong, thailande"
"bob","666","7eme cercle"

Le séparateur (la virgule) et les quotes peuvent être aussi un point-virgule et un quote simple, et on peut avoir n’importe quelle combinaison :

'sam','555-555-555','5, rue des lilas'
'max,'1234567890','Patong, thailande'
'bob','666','7eme cercle'
'sam';'555-555-555';'5, rue des lilas'
'max;'1234567890';'Patong, thailande'
'bob';'666';'7eme cercle'
sam;555-555-555;5, rue des lilas
max;1234567890;Patong, thailande
bob;666;7eme cercle

Il existe aussi des caractères d’échappement, des exceptions de retour à la ligne, et des programmes qui produisent des lignes avec un séparateur, puis des lignes avec un autre. Autant dire que d’un format simple, on arrive parfois à quelque chose de complexe. C’est d’ailleurs pour ça que je recommande de ne pas traiter le CSV à la main, mais d’utiliser des modules spécialisés comme csv en Python.

Sachez néanmoins que le format le plus courant est celui supporté par les tableurs (Excel, LibreOffice Calc…) utilisant des guillemets doubles et des point-virgules :

"Sam";"555-555-555";"5, rue des lilas"
"Max";"1234567890";"Patong, thailande"
"Bob";"666";"7eme cercle"

C’est donc ce format que je recommande car il est du coup très facile à lire et à modifier. Il est aussi possible de donner un nom à chaque colonne sur la première ligne :

"nom";"numero";"adresse"
"Sam";"555-555-555";"5, rue des lilas"
"Max";"1234567890";"Patong, thailande"
"Bob";"666";"7eme cercle"

Vous voudrez utiliser ce format quand :

  • Vos données sont simples et facile à représentées sous forme de tableau.
  • Vous avez pas envie de vous faire chier (TRES bonne raison).
  • Il n’y a pas d’imbrication dans vos données.
  • Vos données sont statiques (pas de vérification, pas de calculs, pas de transtypage). Bref, on les lis tel quel.
  • Vous voulez que ce soit lisible dans un tableur. Pratique pour les néophytes.

Le format INI

Le format INI, utilisé pour l’INItialisation, est surtout un format pour stocker des configurations de logiciel. On le trouve souvent dans des fichiers avec des paramètres, portant l’extension .ini, .cfg, .conf ou .txt.

Il est constitué de deux types d’élément :

  • Les en-têtes, ou sections, qui s’écrivent [nom_de_l_en_tete]. Ils servent à nommer et délimiter des groupes de valeurs.
  • Les valeurs, qui s’écrivent nom=valeur.

Les fichiers INI ne sont pas fait pour stocker des données répétitives comme des personnes d’un carnet d’adresses. Généralement, chaque entrée est unique, et représente un paramètre du programme :

[utilisateur]
nom=Sam
dossier=/home/sam
 
[dernier_acces]
jour=2013-07-06
fichier='le_plus_dur_est_derriere_toi.avi'

C’est un format simple à manipuler (et il existe un module Python pour ça) et généralement on le lit, on modifie une valeur, et on la sauvegarde.

Vous voudrez utiliser ce format quand :

  • Vous voulez sauvegarder l’état ou la configuration de votre programme.
  • Vos données sont très simples.
  • Vous êtes sous une vieille machine Windows.
  • Vous voulez tricher à Baldur’s Gate.

Le format JSON

A la base JSON, qui signifie JavaScript Object Notation, n’était pas un format destiné à être échangé, mais seulement la représentation textuelle des objets Javascript.

Il se trouve qu’avec l’age d’or du Web, et l’utilisation massive d’AJAX, il a été utilisé pour communiquer entre le navigateur et le serveur, et les gens se sont apperçu qu’il était en fait très très très pratique.

Aujourd’hui, JSON est utilisé un peu pour tout, et si vous ne savez pas trop quoi choisir comme format, choisissez JSON, il y a peu de chance de se planter.

Voilà à quoi ressemble le carnet d’adresses en JSON:

[
    {
        'nom': 'Sam',
        'numero': '555-555-555',
        'adresse': '5, rue des lilas'
    },
    {
        'nom': 'Max',
        'numero': '1234567890',
        'adresse': 'Patong, thailande'
    },
    {
        'nom': 'Bob',
        'numero': '666',
        'adresse': '7eme cercle'
    }
]

JSON est extrêmement facile à manipuler de nos jours, et l’immense majorité des langages ont un module pour ça. Python n’échappe pas à la règle.

Il est rare que JSON soit une mauvaise idée, donc je vais plutôt faire une liste de quand vous ne voulez PAS utiliser JSON :

  • Vos données vont être lues souvent pas des humains sans connaissance techniques (le CSV est plus adapté).
  • La plupart des systèmes qui vont lire ces données ont de mauvaises bibliothèques pour lire le JSON, mais de grosses facilités pour d’autres formats.
  • Le reste du système communique avec un autre format (pas la peine de cumuler 40000 formats).
  • Vous avez besoin de performances extrêmes (dans ce cas cherchez du côté des formats binaires).
  • Vous voulez stocker de grosses quantités de données ou faire des analyses complexes dessus (dans ces cas cherchez du côté des bases de données).
  • Il existe un standard qui correspond à votre usage qui n’est pas en JSON (ex: notifications : Utilisez RSS, qui est du XML, ou les emails, carnet d’adresses : utilisez ldif ou vcard, etc.)
  • Vos données sont très très complexes et demande un format plus riche, des vérifications automatiques, un typage avancé, etc. Préférez XML.

Sinon, allez-y, prenez du JSON. On peut l’utiliser pour les fichiers de configuration (comme le fait Sublime Text), comme API pour son service (ce que font presque tous les grands services du monde), pour communiquer entre plusieurs sites Web (JSONP), pour exporter / importer ses données (fixtures Django)…

Le format YAML

YAML, l’humoristique “Yet Another Markup Language” a des buts et qualités similaires au JSON, mais avec un format différent.

Voici le fichier INI, traduit en YAML (les espaces sont significatifs) :

---
utilisateur:
    nom: Sam
    dossier: /home/sam

dernier_acces:
    jour: 2013-07-06
    fichier: 'le_plus_dur_est_derriere_toi.avi'
...

Le format YAML est néanmoins plus riche que le JSON:

  • Il permet d’inclure plusieurs document dans un seul fichier en les séparant par ---
  • Il contient des types avancés comme les dates.
  • Il possède plusieurs manières d’écrire les textes multi-lignes

Globalement le YAML a été créé pour être facilement lisible et éditable par un humain, et pour cette raison la communauté Ruby l’a adopté pour ses fichiers de configuration. On retrouve donc YAML dans RubyOnRail.

Utilisez YAML quand :

  • Votre fichier est destiné à être aussi souvent lu par une machine qu’un humain et contient des types complexes.
  • Vous êtes dans un environnement Ruby (à Rome…).

Contrairement à JSON, on utilisera donc plus YAML pour la configuration que l’envoie de données.

Personnellement je ne suis pas un aficionado de YAML. Mon expérience est que sa syntaxe complexe (j’admet que l’exemple ne le laisse pas paraitre) amène souvent des grattements de tête suite à une édition malheureuse. Frustrant pour un simple fichier de config.. De plus, il faut souvent installer une lib additionnelle pour le lire. Par ailleurs, JSON – qui est en fait un subset de YAML – fait généralement très bien le boulot.

Le format XML

La fameux eXtensible Markup Language. Je ne vais pas vous faire un cours complet sur XML, car on pourrait y passer des mois, au sens propre. Ce format, aux apparences simples, a été utilisé pour des choses extrêmement complexes.

Revenons à notre exemple :

<?xml version="1.0" encoding="utf8"?>
<personnes>
    <personne>
        <nom>Sam</nom>
        <numero>555-555-555</numero>
    </personne>
    <personne>
        <nom>Max</nom>
        <numero>1234567890</numero>
    </personne>
    <personne>
        <nom>Bob</nom>
        <numero>666</numero>
    </personne>
</personnes>

< ?xml version="1.0" encoding="utf8"? > est l’en-tête du fichier, il annonce quel format de XML on va utiliser, le reste est le contenu.

Ici nous n’avons que quelques balises, mais bien entendu, les balises peuvent contenir des balises qui peuvent contenir des balises… En prime, XML autorise des attributs (nom=”valeur”), c’est à dire des valeurs sur les balises qui modifient la signification de celle-ci. Par exemple :

...
<personne>
    <nom>Bob</nom>
    <numero type="shortcode">666</numero>
</personne>
...

Enfin, XML permet ce qu’on appelle des namespaces, afin de dire que les balises correspondent à un dialecte et pas un autre.

Exemple, j’ai deux fois nom dans ce XML :

<?xml version="1.0" encoding="utf8"?>
<personnes>
    <personne>
        <nom>Sam</nom>
        <numero>555-555-555</numero>
        <ville>
            <nom>Metro peau lisse</nom>
            <coord>3.14,6.56</coord>
        </ville>
    </personne>
    <personne>
        <nom>Max</nom>
        <numero>1234567890</numero>
        <ville>
            <nom>Patong</nom>
            <coord>4.2,6.9</coord>
        </ville>
    </personne>
    <personne>
        <nom>Bob</nom>
        <numero>666</numero>
        <ville>
            <nom>Sodome</nom>
            <coord>-12,-13</coord>
        </ville>
    </personne>
</personnes>

Comment savoir pour ma machine ce que signifie, “nom” ?

Et bien je peux les namespacer, c’est à dire les lier à une URL qui pointe vers la documentation qui dit ce que signifie chaque balise, ou au moins le site de l’auteur du XML.

<?xml version="1.0" encoding="utf8"?>
<personnes xmlns:sm="http://sametmax.com" xmlns:osm="http://openstreetmap.org">
    <sm:personne>
        <sm:nom>Sam</sm:nom>
        <sm:numero>555-555-555</sm:numero>
        <sm:ville>
            <osm:nom>Metro peau lisse</osm:nom>
            <osm:coord>3.14,6.56</osm:coord>
        </sm:ville>
    </sm:personne>
    <sm:personne>
        <sm:nom>Max</sm:nom>
        <sm:numero>1234567890</sm:numero>
        <sm:ville>
            <osm:nom>Patong</osm:nom>
            <osm:coord>4.2,6.9</osm:coord>
        </sm:ville>
    </sm:personne>
    <sm:personne>
        <sm:nom>Bob</sm:nom>
        <sm:numero>666</sm:numero>
        <sm:ville>
            <osm:nom>Sodome</osm:nom>
            <osm:coord>-12,-13</osm:coord>
        </sm:ville>
    </sm:personne>
</personnes>

Et si on veut se marrer un peu plus, on peut utiliser ce qu’on appelle des DTD (ou le format plus moderne XSD) : un XML, qui définit comment doit être formé un autre XML et qui permet de vérifier cela automatiquement. Il existe également un format appelé XSLT, qui permet de définir, en XML, comment transformer un autre document XML, en un document dans un troisième format de votre choix.

Bref, le XML peut devenir très très compliqué. Si compliqué en fait, que des formats en XML, même avec leurs spécifications, deviennent difficiles à lire.

XML a été historiquement le premier format texte à être souple, extensible et interopérable. Aussi a-t-il été longtemps un format de choix pour communiquer entre machines et pour sauvegarder les configurations complexes. Aujourd’hui, sa verbosité et sa complexité ont amener les gens à se tourner massivement vers JSON, et XML n’est maintenant plus utilisé que pour les données très riches ou des raisons historiques (RSS, SOAP, etc).

Il reste un terrain où XML brille, c’est la validation de données. En effet, grâce au DTD / XSD, on peut publier un document qui permet à tout langage avec la bibliothèque appropriée, de vérifier si un XML est valide, comme un formulaire. On publie les besoins de validation une fois, et plein de langages peuvent en faire usage. C’est un vrai plus.

Je recommande rarement d’utiliser XML, c’est lourd, difficile à manipuler (malgré un très bon support général de la plupart des langages), verbeux… Difficile à un débutant de mettre les mains dans votre système quand la courbe d’apprentissage est aussi hardos.

Utilisez XML si :

  • Un des dialectes XML correspond à un standard pour votre usage (RSS / Atom).
  • Vous visez des systèmes qui utilisent historiquement XML : serveurs SOAP, environnement Java…
  • La validation des données est importante et elles seront lues par de multiples langages.

Maintenance, versioning, documentation

Ce n’est pas le tout de choisir un format, il faut maintenant s’assurer qu’on puisse l’utiliser.

Car mettre des labels à ses données, c’est bien beau, mais si le mec qui reçoit vos données ne sait pas ce que signifie ces labels, il ne peut rien en faire.

Il va donc falloir écrire une documentation pour cela. Et oui, on ne documente pas seulement son code, mais aussi ses formats !

Je vous invite aussi fortement à versionner vos formats, c’est à dire à toujours accompagner vos données (dans le fichier, via le protocole d’échange, ou dans le changelog de votre application) d’un numéro de version, ceci afin que les utilisateurs / développeurs / admin système ne se retrouvent pas couillonnés quand vous décider de modifier un peu votre format.

Sur le long terme, comme les commentaires de code, cela va vous aider vous. Mais cela rendra aussi votre projet plus accessible et attirant pour les gens de l’extérieur, ou tout simplement vos collègues.

En écrivant cela je viens de m’apercevoir que l’on a rien fait de tout cela pour 0bin. La honte…

Prochainement, les formats binaires.

]]>
http://sametmax.com/yaml-xml-json-csv-ini-quest-ce-que-cest-et-a-quoi-ca-sert/feed/ 28