2015-01-22 2 views
3

Я знаю, что я могу сделать:JSON-сериализации нестроковые ключи словаря

import datetime 

def default(o): 
    if type(o) is datetime.datetime: 
     return o.isoformat() 

data = {'a': datetime.datetime.today()} 
json.dumps(data, default=default) 
# '{"a": "2015-01-22T01:04:23.121392"}' 

Это работает идеально. А что, если у меня есть DateTimes как мои ключи:

data = {datetime.datetime.today(): 'a'} 

Если я попробовать то же самое он терпит неудачу с:

TypeError: keys must be a string 

Есть ли способ, что я могу сделать что-то подобное, то есть пользовательский преобразователь, но и для ключи?

Примечание: Это простой пример. У меня есть глубокая вложенная структура dict, где некоторые ключи не являются строками.

EDIT: Вложенный пример, но обратите внимание, что у меня нет контроля над структурой данных, приходит из внешней функции:

data = {'a': {datetime.datetime.today(): '1'}} 
+1

Почему вы используете Python? – s16h

+0

У вас есть существующая структура с вложенными объектами datetime? Я предлагаю исправить это, прежде чем вы их в гнездо, иначе вам придется рекурсивно пересекать dict. – user3467349

+0

@ s16h, 2.7, но решение 3.4 было бы в порядке. – Elian

ответ

3

Вы могли бы это сделать :

class DatesToStrings(json.JSONEncoder): 
    def _encode(self, obj): 
     if isinstance(obj, dict): 
      def transform_date(o): 
       return self._encode(o.isoformat() if isinstance(o, datetime) else o) 
      return {transform_date(k): transform_date(v) for k, v in obj.items()} 
     else: 
      return obj 

    def encode(self, obj): 
     return super(DatesToStrings, self).encode(self._encode(obj)) 

>>> json.dumps({"a": {datetime.now(): 3}}, cls=DatesToStrings) 
'{"a": {"2015-01-22T11:49:25.910261": 3}}' 
+0

Передача 'cls'' 'json.dumps' является опрятной идеей, но я не уверен, что она сработает. В частности, ваш пример не работает для вложенных dicts. – user3467349

+0

Да, не работает для 'data = {'a': {datetime.today(): '1'}}' – Elian

+0

Хороший вопрос .. как насчет этого? –

1

Просто использовать ул изменить тип будет хорошо:

>>> import datetime 
>>> type(datetime.datetime.today()) 
<type 'datetime.datetime'> 
>>> data = {str(datetime.datetime.today()): 'a'} 
>>> data 
{'2015-01-22 08:13:11.554000': 'a'} 
>>> data = {repr(datetime.datetime.today()): 'a'} 
>>> data 
{'datetime.datetime(2015, 1, 22, 8, 15, 0, 551000)': 'a'} 
>>> data = {'a': {datetime.datetime.today(): '1'}} 
>>> data 
{'a': {datetime.datetime(2015, 1, 22, 8, 32, 25, 175000): '1'}} 
+0

'repr' будет лучше, чем' str' – ashwinjv

+0

unix будет лучше, чем datetime – user3467349

+0

@Ashwin Hm, почему? С помощью 'str' можно использовать' strptime' для преобразования строки в 'datetime'.С 'repr', нужно либо использовать' eval' (лучше убедиться, что строка - это то, что вы думаете!), Либо проанализировать строку. Или есть лучший способ получить 'datetime' из его' repr'? – jme

1

Если вы собираетесь сериализовать программу python для последующего чтения, используйте для этого модуль pickle. Он будет сохранять пользовательские классы и объекты, пока определения будут видны скрипту/модулю, который будет десериализовать его и использовать.

Вы могли бы сделать что-то вроде:

data = {datetime.datetime.today(): 'a'} 

try:  
    import cPickle as pickle # Try it. It could be faster 
except: 
    import pickle # Regular pickle as a fallback 

with open("c:/mypickle.DAT", "w") as f: 
    pickle.dump(data, f) 

Если запись на физический диск не то, что вы хотите, особенно по соображениям производительности, вы можете попробовать написать в файл-подобный объект, как StringIO.

+0

Просто комментарий, ОП сказал * Это просто простой пример *. Поэтому я не предлагаю помогать исправлять программу 'json', мы должны пойти в корень. –

+0

@Elian, если это * ваша проблема, тогда я могу вам помочь. В какой ОС вы работаете? –

+0

Как у Mac/Windows/Linux/Ubuntu –

0

Вы можете использовать рекурсивную функцию для исправления ключей, включая ключи в вложенных словарях, перед передачей их в сериализатор.

def datetime_key_fix(o): 
    if isinstance(o, dict): 
     for key in o: 
      o[key] = datetime_key_fix(o[key]) 
      if type(key) is datetime.datetime: 
       o[key.isoformat()] = o[key] 
       del o[key] 
    return o 


data = {datetime.datetime.today(): {datetime.datetime.today(): "a"}} 
print json.dumps(datetime_key_fix(data)) 
+1

Это не работает для гнездования. – user3467349

+0

На самом деле это не работает ни для чего. Один момент ... –

+0

Исправлено. Теперь он должен работать и для гнездования. –

2

Вот рекурсивный вариант - обратите внимание, что я не гарантирую, что это будет быстрее, чем маринованная версия:

def dictRecursiveFormat(d): 
     for key, val in list(d.items()): 
      if isinstance(key, datetime.datetime): 
       val = d.pop(key) 
       d[str(key)] = val 
      if isinstance(val, datetime.datetime) and isinstance(key, datetime.datetime): 
       d[str(key)] = str(val) 
      elif isinstance(val, datetime.datetime): 
       d[key] = str(val) 
      if type(val) is dict: 
       dictRecursiveFormat(val) 

пример:

In [52]: d= {'a': datetime.datetime.now(), 'b': {datetime.datetime.now(): datetime.datetime.now()}} 

In [53]: dictRecursiveFormat(d) 

In [54]: d 
Out[54]: 
{'a': '2015-01-21 19:33:52.293182', 
'b': {'2015-01-21 19:33:52.293240': '2015-01-21 19:33:52.293229'}} 
+0

На данный момент единственное рабочее решение не имеет никаких повышений. – Elian

+0

Зачем мне нужно иметь 15 очков для увеличения? – Elian

0

Попробуйте это :

import datetime 
import json 

data = {1:{datetime.datetime.today(): 'a'}, 2:{datetime.datetime.today(): 'a'}} 
dataString = repr(data) 
dataString 
#"{1: {datetime.datetime(2015, 1, 21, 16, 56, 15, 219567): 'a'}, 2: {datetime.datetime(2015, 1, 21, 16, 56, 15, 219567): 'a'}}" 
dataDictionary = eval(dataString) 
dataDictionary 
#{1: {datetime.datetime(2015, 1, 21, 16, 56, 15, 219567): 'a'}, 2: {datetime.datetime(2015, 1, 21, 16, 56, 15, 219567): 'a'}} 
datajsonString = json.dumps(dataString) 
#'"{1: {datetime.datetime(2015, 1, 21, 16, 56, 15, 219567): \'a\'}, 2: {datetime.datetime(2015, 1, 21, 16, 56, 15, 219567): \'a\'}}"' 
Смежные вопросы