使用自定义的Python JSON编码器将浮点数编码到字典中

3
这是一个简单的JSON编码器,将浮点数转换为字符串:
class NestedEncoder(json.JSONEncoder):
    '''
    A JSON Encoder that converts floats/decimals to strings and allows nested objects
    '''

    def default(self, obj):

        if isinstance(obj, float) or obj.__class__.__name__ == "float32":
            return self.floattostr(obj)
        elif obj.__class__.__name__ == "type":
            return str(obj)
        elif hasattr(obj, 'repr_json'):
            return obj.repr_json()
        else:
            return json.JSONEncoder.default(self, obj)

    def floattostr(self,o,_inf=float('Inf'), _neginf=-float('-Inf'),nan_str="None"):
        if o != o:
            text = nan_str
        elif o == _inf:
            text = 'Infinity'
        elif o == _neginf:
            text = '-Infinity'
        else:
            return o.__repr__()

        return text

现在有两个测试。第一个创建了一个无穷大的浮点数,并使用自定义编码器对其进行编码。测试通过。
def test_inf():
    inf = float('Inf')
    as_json = json.dumps(inf,cls=NestedEncoder)
    assert as_json == "Infinity"

第二个测试也是这样做,但是将浮点数放在了字典中:
def test_inf_dic():
    inf = float('Inf')
    as_json = json.dumps({'key':inf},cls=NestedEncoder)
    assert as_json == "{'key':'Infinity'}"

输出:

=================================== FAILURES ===================================
_________________________________ test_inf_dic _________________________________

    def test_inf_dic():
        inf = float('Inf')
        as_json = json.dumps({'key':inf},cls=NestedEncoder)
>       assert as_json == "{'key':'Infinity'}"
E       assert '{"key": Infinity}' == "{'key':'Infinity'}"
E         - {"key": Infinity}
E         ?  ^   ^ ^
E         + {'key':'Infinity'}
E         ?  ^   ^ ^        +

编辑:

自定义编码器仅在第一个测试中被调用,而不是第二个。


Python2.7 返回“float” 用于 float('Inf').__class__.__name__,因此不会匹配您的第一个条件语句。您还试图比较测试中的字符串和字典。 - jordanm
@jordanm 我修复了断言。请查看编码Infinity的问题。 你指出的float('Inf').__class__.__name__问题并不相关,因为它是一个OR条件。 - gidim
1个回答

1
您是正确的,您的自定义函数在测试期间没有被调用。原因在文档中解释如下:
如果指定了default,它应该是一个函数,用于为无法序列化的对象调用。它应该返回对象的JSON可编码版本或引发TypeError。如果未指定,则引发TypeError。
由于float是已知如何序列化的对象,因此不会调用它。对于浮点数序列化,您可以覆盖encode函数。

谢谢,但如果是这种情况,为什么第一个测试通过了呢? - gidim
默认情况下 json.dumps(float('Inf')) 返回一个字符串 'Infinity'。我不确定为什么会这样,但是我在 ipython 中进行了测试。我还通过插入一个从未触发的断点来测试了您的类和函数。 - jordanm
@gidim 看起来根据 JSON 规范,Infinity 只能是一个值,而且只能在对象或数组内部:https://tools.ietf.org/html/rfc7159 - jordanm
我认为规范根本不允许Infinity,这就是为什么我试图将其转换为字符串的原因。第一个测试确实会触发NestedEncoder,但第二个测试则不会。即使字典被支持,它也应该在其值上调用,不是吗? - gidim
@jordanm 怎么重写 encode 方法呢? - jtlz2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接