Je veux envoyer un objet datetime.datetime sous forme sérialisée à partir de Python en utilisant JSON et désérialiser en JavaScript en utilisant JSON. Quelle est la meilleure façon de procéder?
Je veux envoyer un objet datetime.datetime sous forme sérialisée à partir de Python en utilisant JSON et désérialiser en JavaScript en utilisant JSON. Quelle est la meilleure façon de procéder?
Réponses:
Vous pouvez ajouter le paramètre 'default' à json.dumps pour gérer ceci:
date_handler = lambda obj: (
obj.isoformat()
if isinstance(obj, (datetime.datetime, datetime.date))
else None
)
json.dumps(datetime.datetime.now(), default=date_handler)
'"2010-04-20T20:08:21.634121"'
Qui est ISO 8601 format .
Une fonction de gestionnaire par défaut plus complète:
def handler(obj):
if hasattr(obj, 'isoformat'):
return obj.isoformat()
elif isinstance(obj, ...):
return ...
else:
raise TypeError, 'Object of type %s with value of %s is not JSON serializable' % (type(obj), repr(obj))
Mise à jour: sortie ajoutée de type ainsi que de valeur.
Mise à jour: gérer également la date
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Pour les projets multilingues , j'ai découvert que les chaînes contenant des dates RfC 3339 sont la meilleure solution. Une date RfC 3339 ressemble à ceci:
1985-04-12T23:20:50.52Z
Je pense que la plupart du format est évident. La seule chose quelque peu inhabituelle peut être le "Z" à la fin. Il signifie GMT / UTC. Vous pouvez également ajouter un décalage de fuseau horaire comme +02: 00 pour CEST (Allemagne en été). Personnellement, je préfère tout garder en UTC jusqu'à ce qu'il s'affiche.
Pour l'affichage, les comparaisons et le stockage, vous pouvez le laisser sous forme de chaîne dans toutes les langues. Si vous avez besoin de la date pour les calculs, convertissez-la facilement en un objet de date natif dans la plupart des langues.
Générez donc le JSON comme ceci:
json.dump(datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))
Malheureusement, le constructeur Date de Javascript n'accepte pas les chaînes RfC 3339 mais il existe de nombreux analyseurs disponibles sur Internet.
huTools.hujson essaie de gérer les problèmes d'encodage les plus courants que vous pourriez rencontrer dans le code Python, y compris les objets date / datetime tout en gérant correctement les fuseaux horaires.
datetime
: datetime.isoformat () et par simplejson
, qui videront les datetime
objets sous forme de isoformat
chaînes par défaut. Pas besoin de strftime
piratage manuel .
datetime
objets en isoformat
chaîne. Pour moi, les simplejson.dumps(datetime.now())
rendementsTypeError: datetime.datetime(...) is not JSON serializable
json.dumps(datetime.datetime.now().isoformat())
c'est là que la magie opère.
Je l'ai résolu.
Disons que vous avez un objet datetime Python, d , créé avec datetime.now (). Sa valeur est:
datetime.datetime(2011, 5, 25, 13, 34, 5, 787000)
Vous pouvez le sérialiser en JSON en tant que chaîne de date / heure ISO 8601:
import json
json.dumps(d.isoformat())
L'exemple d'objet datetime serait sérialisé comme suit:
'"2011-05-25T13:34:05.787000"'
Cette valeur, une fois reçue dans la couche Javascript, peut construire un objet Date:
var d = new Date("2011-05-25T13:34:05.787000");
Depuis Javascript 1.8.5, les objets Date ont une méthode toJSON, qui retourne une chaîne dans un format standard. Pour sérialiser l'objet Javascript ci-dessus en JSON, la commande serait donc:
d.toJSON()
Ce qui vous donnerait:
'2011-05-25T20:34:05.787Z'
Cette chaîne, une fois reçue en Python, pourrait être désérialisée en un objet datetime:
datetime.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')
Cela se traduit par l'objet datetime suivant, qui est le même que celui avec lequel vous avez commencé et donc correct:
datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)
À l'aide de json
, vous pouvez sous-classer JSONEncoder et remplacer la méthode default () pour fournir vos propres sérialiseurs personnalisés:
import json
import datetime
class DateTimeJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
else:
return super(DateTimeJSONEncoder, self).default(obj)
Ensuite, vous pouvez l'appeler comme ceci:
>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'
obj.isoformat()
. Vous pouvez également utiliser l'appel le plus courant dumps()
, qui prend d'autres arguments utiles (comme indent
): simplejson.dumps (myobj, cls = JSONEncoder, ...)
Voici une solution assez complète pour l'encodage et le décodage récursifs des objets datetime.datetime et datetime.date à l'aide du json
module de bibliothèque standard . Cela nécessite Python> = 2.6 car le %f
code de format dans la chaîne de format datetime.datetime.strptime () n'est pris en charge que depuis lors. Pour la prise en charge de Python 2.5, supprimez le %f
et supprimez les microsecondes de la chaîne de date ISO avant d'essayer de le convertir, mais vous perdrez la précision des microsecondes, bien sûr. Pour l'interopérabilité avec les chaînes de date ISO d'autres sources, qui peuvent inclure un nom de fuseau horaire ou un décalage UTC, vous devrez peut-être également supprimer certaines parties de la chaîne de date avant la conversion. Pour un analyseur complet des chaînes de date ISO (et de nombreux autres formats de date), consultez le module de dateutil tiers .
Le décodage ne fonctionne que lorsque les chaînes de date ISO sont des valeurs dans une notation d'objet littéral JavaScript ou dans des structures imbriquées dans un objet. Les chaînes de date ISO, qui sont des éléments d'un tableau de niveau supérieur, ne seront pas décodées.
C'est à dire que cela fonctionne:
date = datetime.datetime.now()
>>> json = dumps(dict(foo='bar', innerdict=dict(date=date)))
>>> json
'{"innerdict": {"date": "2010-07-15T13:16:38.365579"}, "foo": "bar"}'
>>> loads(json)
{u'innerdict': {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)},
u'foo': u'bar'}
Et cela aussi:
>>> json = dumps(['foo', 'bar', dict(date=date)])
>>> json
'["foo", "bar", {"date": "2010-07-15T13:16:38.365579"}]'
>>> loads(json)
[u'foo', u'bar', {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)}]
Mais cela ne fonctionne pas comme prévu:
>>> json = dumps(['foo', 'bar', date])
>>> json
'["foo", "bar", "2010-07-15T13:16:38.365579"]'
>>> loads(json)
[u'foo', u'bar', u'2010-07-15T13:16:38.365579']
Voici le code:
__all__ = ['dumps', 'loads']
import datetime
try:
import json
except ImportError:
import simplejson as json
class JSONDateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime.date, datetime.datetime)):
return obj.isoformat()
else:
return json.JSONEncoder.default(self, obj)
def datetime_decoder(d):
if isinstance(d, list):
pairs = enumerate(d)
elif isinstance(d, dict):
pairs = d.items()
result = []
for k,v in pairs:
if isinstance(v, basestring):
try:
# The %f format code is only supported in Python >= 2.6.
# For Python <= 2.5 strip off microseconds
# v = datetime.datetime.strptime(v.rsplit('.', 1)[0],
# '%Y-%m-%dT%H:%M:%S')
v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S.%f')
except ValueError:
try:
v = datetime.datetime.strptime(v, '%Y-%m-%d').date()
except ValueError:
pass
elif isinstance(v, (dict, list)):
v = datetime_decoder(v)
result.append((k, v))
if isinstance(d, list):
return [x[1] for x in result]
elif isinstance(d, dict):
return dict(result)
def dumps(obj):
return json.dumps(obj, cls=JSONDateTimeEncoder)
def loads(obj):
return json.loads(obj, object_hook=datetime_decoder)
if __name__ == '__main__':
mytimestamp = datetime.datetime.utcnow()
mydate = datetime.date.today()
data = dict(
foo = 42,
bar = [mytimestamp, mydate],
date = mydate,
timestamp = mytimestamp,
struct = dict(
date2 = mydate,
timestamp2 = mytimestamp
)
)
print repr(data)
jsonstring = dumps(data)
print jsonstring
print repr(loads(jsonstring))
datetime.datetime.utcnow().isoformat()[:-3]+"Z"
elle sera exactement comme ce que JSON.stringify () produit en javascript
Si vous êtes certain que seul Javascript consommera le JSON, je préfère passer Date
directement les objets Javascript .
La ctime()
méthode sur les datetime
objets renverra une chaîne que l'objet Date Javascript peut comprendre.
import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()
Javascript sera heureux de l'utiliser comme un littéral d'objet, et vous avez votre objet Date intégré.
.ctime()
est un TRÈS mauvais moyen de transmettre des informations sur le temps, .isoformat()
c'est beaucoup mieux. Ce .ctime()
qui est de jeter le fuseau horaire et l'heure d'été comme s'ils n'existaient pas. Cette fonction devrait être supprimée.
Tard dans le jeu ... :)
Une solution très simple consiste à patcher le module json par défaut. Par exemple:
import json
import datetime
json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)
Maintenant, vous pouvez utiliser json.dumps () comme s'il avait toujours pris en charge datetime ...
json.dumps({'created':datetime.datetime.now()})
Cela a du sens si vous avez besoin que cette extension du module json démarre toujours et que vous ne vouliez pas changer la façon dont vous ou d'autres utilisez la sérialisation json (dans le code existant ou non).
Notez que certains peuvent considérer les correctifs de bibliothèques de cette manière comme une mauvaise pratique. Une attention particulière doit être portée au cas où vous souhaiteriez étendre votre application de plusieurs manières - dans ce cas, je suggère d'utiliser la solution par ramen ou JT et de choisir l'extension json appropriée dans chaque cas.
None
. Vous voudrez peut-être lever une exception à la place.
Pas grand chose à ajouter à la réponse du wiki communautaire, à l'exception de l' horodatage !
Javascript utilise le format suivant:
new Date().toJSON() // "2016-01-08T19:00:00.123Z"
Côté Python (pour le json.dumps
gestionnaire, voir les autres réponses):
>>> from datetime import datetime
>>> d = datetime.strptime('2016-01-08T19:00:00.123Z', '%Y-%m-%dT%H:%M:%S.%fZ')
>>> d
datetime.datetime(2016, 1, 8, 19, 0, 0, 123000)
>>> d.isoformat() + 'Z'
'2016-01-08T19:00:00.123000Z'
Si vous omettez ce Z, les frameworks frontaux comme angular ne peuvent pas afficher la date dans le fuseau horaire local du navigateur:
> $filter('date')('2016-01-08T19:00:00.123000Z', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 20:00:00"
> $filter('date')('2016-01-08T19:00:00.123000', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 19:00:00"
Mon conseil est d'utiliser une bibliothèque. Il y en a plusieurs disponibles sur pypi.org.
J'utilise celui-ci, ça marche bien: https://pypi.python.org/pypi/asjson
Apparemment, le «bon» format de date JSON (bien JavaScript) est 2012-04-23T18: 25: 43.511Z - UTC et «Z». Sans cela, JavaScript utilisera le fuseau horaire local du navigateur Web lors de la création d'un objet Date () à partir de la chaîne.
Pour un temps "naïf" (ce que Python appelle un temps sans fuseau horaire et cela suppose qu'il est local), le ci-dessous forcera le fuseau horaire local afin qu'il puisse ensuite être correctement converti en UTC:
def default(obj):
if hasattr(obj, "json") and callable(getattr(obj, "json")):
return obj.json()
if hasattr(obj, "isoformat") and callable(getattr(obj, "isoformat")):
# date/time objects
if not obj.utcoffset():
# add local timezone to "naive" local time
# /programming/2720319/python-figure-out-local-timezone
tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
obj = obj.replace(tzinfo=tzinfo)
# convert to UTC
obj = obj.astimezone(timezone.utc)
# strip the UTC offset
obj = obj.replace(tzinfo=None)
return obj.isoformat() + "Z"
elif hasattr(obj, "__str__") and callable(getattr(obj, "__str__")):
return str(obj)
else:
print("obj:", obj)
raise TypeError(obj)
def dump(j, io):
json.dump(j, io, indent=2, default=default)
Pourquoi est-ce si difficile.
Pour la conversion de date Python en JavaScript, l'objet date doit être au format ISO spécifique, c'est-à-dire au format ISO ou au numéro UNIX. Si le format ISO manque d'informations, vous pouvez d'abord convertir au numéro Unix avec Date.parse. De plus, Date.parse fonctionne également avec React tandis que la nouvelle Date peut déclencher une exception.
Si vous disposez d'un objet DateTime sans millisecondes, les éléments suivants doivent être pris en compte. :
var unixDate = Date.parse('2016-01-08T19:00:00')
var desiredDate = new Date(unixDate).toLocaleDateString();
L'exemple de date peut également être une variable dans l'objet result.data après un appel d'API.
Pour les options pour afficher la date dans le format souhaité (par exemple pour afficher les longs jours de la semaine), consultez le document MDN .