JSON序列化非字符串字典键

时间:2015-01-22 00:07:39

标签: python 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"}'

这很完美。如果我将日期作为我的键,那该怎么办呢?

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

如果我尝试相同的操作,则会失败:

TypeError: keys must be a string

我有什么方法可以做类似的事情,即自定义转换器,但是用于键?

注意:这只是一个简单的例子。我有一个深层嵌套的dict结构,其中一些键不是字符串。

编辑:一个嵌套的例子,但请注意我无法控制数据结构,它来自外部函数:

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

7 个答案:

答案 0 :(得分:4)

你可以这样做:

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}}'

答案 1 :(得分:3)

这是递归版本 - 请注意,我不保证它会比腌制版本更快:

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'}}

答案 2 :(得分:1)

只需使用str来改变类型即可:

>>> 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'}}

答案 3 :(得分: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

答案 4 :(得分: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))

答案 5 :(得分: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\'}}"'

答案 6 :(得分:0)

在Andrew Magee的基础上,并添加了嵌套列表/集合支持:

import json
from datetime import datetime


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

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


print(json.dumps({"a": datetime.now()}, cls=DatesToStrings))
print(json.dumps({datetime.now(): 1}, cls=DatesToStrings))
print(json.dumps({"a": {datetime.now(): 3}}, cls=DatesToStrings))
print(json.dumps({"a": [datetime.now()]}, cls=DatesToStrings))
print(json.dumps({"a": {1: [datetime.now()]}}, cls=DatesToStrings))
print(json.dumps({"a": [{1: [datetime.now()]}]}, cls=DatesToStrings))
print(json.dumps({"a": {datetime.now()}}, cls=DatesToStrings))