PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Mise à jour

Mise à jour de la base de données, veuillez patienter...

Faire manger du datetime à JSON en Python

dimanche 9 mars 2014 à 21:32

La guerre de la sérialisation a plus ou moins été gagnée par JSON. Le XML est relégué aux documents très riches et aux systèmes legacy. Le YML est cantonné a des niches (et en plus souvent des niches en Ruby, c’est dire !). Et les formats binaires, sont gardés pour les besoins de perf. Le reste, c’est du JSON, du JSON et du JSON.

Seulement le JSON ne permet pas de sauvegarder des dates, seulement des chaînes, des entiers, des booléens et null. Heureusement on peut créer son propre dialecte au dessus de JSON pour y remédier, mais il faut avoir un un parseur qui le gère.

En Python on peut créer sa propre classe d’encodeur et décodeur de JSON et donc techniquement ajouter n’importe quel type.

Voici une recette pour en créer un qui gère le type datetime de manière transparente :

import re
import json
 
from datetime import datetime
 
# On hérite simplement de l'encodeur de base pour faire son propre encodeur
class JSONEncoder(json.JSONEncoder):
 
    # Cette méthode est appelée pour serialiser les objets en JSON
    def default(self, obj):
        # Si l'objet est de type datetime, on retourne une chaîne formatée
        # représentant l'instant de manière classique
        # ex: "2014-03-09 19:51:32.7689"
        if isinstance(obj, datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
        return json.JSONEncoder.default(self, obj)
 
 
# On fait l'opération exactement inverse pour le décodeur
class JSONDecoder(json.JSONDecoder):
 
 
    # On écrase la méthode qui permet de décoder les paires clé / valeur
    # du format JSON afin que chaque valeur passe par notre moulinette
    def object_pairs_hook(self, obj):
        return dict((k, self.decode_on_match(v)) for k, v in obj)
 
 
    # notre moulinette
    def decode_on_match(self, obj):
 
        # une petite regex permet de savoir si la chaine est une date
        # sérialisée selon notre format précédent
        match = re.search(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6}', unicode(obj))
        # si oui, on parse et on retourne le datetime
        if match:
            return datetime.strptime(match.string, self.datetime_format)
 
        # sinon on retourne l'objet tel quel
        return obj
 
# On se fait des raccourcis pour loader et dumper le json
 
def json_dumps(data):
    return JSONEncoder().encode(data)
 
 
def json_loads(string):
    return JSONDecoder().decode(string)

Usage :

>>> res = json_dumps({'test': datetime(2000, 1, 1, 1, 1, 1), 'autre': [True, 1]})
>>> print(type(res), res)
(<type 'str'>, '{"test": "2000-01-01 01:01:01.000000", "autre": [true, 1]}')
>>> res = json_loads(res)
>>> print(type(res), res)
(<type 'dict'>, {u'test': u'2000-01-01 01:01:01.000000', u'autre': [True, 1]})

Minibelt contient une version un peu plus élaborée de ce code qui prend en compte les types date, time et timedelta ainsi que pas mal d’options de configuration.

flattr this!

Error happened! 0 - count(): Argument #1 ($value) must be of type Countable|array, null given In: /var/www/ecirtam.net/autoblogs/autoblogs/autoblog.php:428 http://ecirtam.net/autoblogs/autoblogs/sametmaxcom_a844ada43a979e3b1395ab9acb6afafb84340999/?Faire-manger-du-datetime-%C3%A0-JSON-en-Python #0 /var/www/ecirtam.net/autoblogs/autoblogs/autoblog.php(999): VroumVroum_Blog->update() #1 /var/www/ecirtam.net/autoblogs/autoblogs/sametmaxcom_a844ada43a979e3b1395ab9acb6afafb84340999/index.php(1): require_once('...') #2 {main}