使用自定义编码器将带有十进制键的python词典转换为JSON

时间:2017-10-12 06:42:09

标签: python json python-3.x decimal

JSON只允许字符串作为键。

下面的代码使用自定义JSONEncoder将Decimal值转换为字符串。

有没有办法指定将Decimal键转换成字符串的编码器?

import json
import decimal

class DecimalEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, decimal.Decimal):
            return str(obj)
        return json.JSONEncoder.default(self, obj)

d1 = {3: decimal.Decimal(50)}
print(json.dumps(d1, cls=DecimalEncoder))

d2 = {decimal.Decimal(50): 3}
json.dumps(d2, cls=DecimalEncoder)  # TypeError: keys must be a string

我正在使用python3.6

注意:显然我可以遍历我的字典并用字符串值替换Decimal类型,但我希望通过向编码器添加行为来找到更优雅的解决方案。

1 个答案:

答案 0 :(得分:1)

不,你无法挂钩键的处理,你必须在编码之前将它们转换为。您可以使用递归处理程序执行此操作,例如:

from functools import singledispatch

@singledispatch
def string_keys(obj):
    return obj

@string_keys.register(dict)
def _(d):
    return {str(k): string_keys(v) for k, v in d.items()}

@string_keys.register(list)
def _(l):
    return [string_keys(v) for v in l]

所有这一切都是递归转换列表和dicts的嵌套结构,其中所有键都被强制为字符串。

转换为JSON时使用此选项:

json_encoded = json.dumps(string_keys(data))

您可以通过添加另一个注册表来扩展它以处理Decimal个对象(在键之外):

@string_keys.register(Decimal)
def _(d):
    return str(d)

走另一条路是有点棘手的,除非你明确地标记Decimal个密钥(带有前缀,比方说),你不能轻易区分起始字符串的密钥和{{1} }值。您可以在此处使用Decimal方法:

try/except

演示:

from functools import singledispatch

@singledispatch
def keys_to_decimal(obj):
    return obj

@keys_to_decimal.register(dict)
def _(d):
    def try_decimal(k):
        try:
            return Decimal(k)
        except ValueError:
            return k
    return {try_decimal(k): keys_to_decimal(v) for k, v in d.items()}

@keys_to_decimal.register(list)
def _(l):
    return [keys_to_decimal(v) for v in l]