【问题标题】:Creating custom JSONEncoder创建自定义 JSONEncoder
【发布时间】:2011-06-20 15:45:52
【问题描述】:

我正在运行 Python 2.7,并且正在尝试创建 JSONEncoder 的自定义 FloatEncoder 子类。我遵循了许多示例,例如this,但似乎没有一个有效。这是我的 FloatEncoder 类:

class FloatEncoder(JSONEncoder):
    def _iterencode(self, obj, markers=None):
         if isinstance(obj, float):
            return (str(obj) for obj in [obj])
        return super(FloatEncoder, self)._iterencode(obj, markers)

这里是我调用 json.dumps 的地方:

with patch("utils.fileio.FloatEncoder") as float_patch:
        for val,res in ((.00123456,'0.0012'),(.00009,'0.0001'),(0.99999,'1.0000'),({'hello':1.00001,'world':[True,1.00009]},'{"world": [true, 1.0001], "hello": 1.0000}')): 
            untrusted = dumps(val, cls=FloatEncoder)
            self.assertTrue(float_patch._iterencode.called)
            self.assertEqual(untrusted, res)

第一个断言失败,意味着 _iterencode 没有被执行。阅读 JSON 文档后,我尝试覆盖 default() 方法,但也没有被调用。

【问题讨论】:

  • FWIW,default() 没有被调用,因为如果输入是编码器默认支持的类型之一,它甚至不会查看您的自定义方法。比较lib/json/encoder.py,在_iterencode() 的定义中:_default() 只在else: 分支中被调用,在所有已知类型都被覆盖之后。因此,您不能覆盖对已知类型的处理。

标签: python json


【解决方案1】:

在生成 JSON(基于测试示例)时,您似乎试图将浮点值四舍五入到小数点后 4 位。

JSONEncoder 附带 Python 2.7 没有 _iterencode 方法,所以这就是它没有被调用的原因。同样快速浏览json/encoder.py 表明这个类的编写方式使得很难改变浮点编码行为。也许,在进行 JSON 序列化之前分离关注点并舍入浮点数会更好。

编辑:Alex Martelli 还在a related answer. 中提供了一个猴子补丁解决方案,这种方法的问题是您正在对json 库行为进行全局修改,这可能会在不知不觉中影响一些您的应用程序中的另一段代码是在假设浮点数被编码时没有四舍五入的情况下编写的。

试试这个:

from collections import Mapping, Sequence
from unittest import TestCase, main
from json import dumps

def round_floats(o):
    if isinstance(o, float):
        return round(o, 4)
    elif isinstance(o, basestring):
        return o
    elif isinstance(o, Sequence):
        return [round_floats(item) for item in o]
    elif isinstance(o, Mapping):
        return dict((key, round_floats(value)) for key, value in o.iteritems())
    else:
        return o

class TestFoo(TestCase):
    def test_it(self):
        for val, res in ((.00123456, '0.0012'),
                         (.00009, '0.0001'),
                         (0.99999, '1.0'),
                         ({'hello': 1.00001, 'world': [True, 1.00009]},
                          '{"world": [true, 1.0001], "hello": 1.0}')):
            untrusted = dumps(round_floats(val))
            self.assertEqual(untrusted, res)

if __name__ == '__main__':
    main()

【讨论】:

    【解决方案2】:

    不要定义_iterencode,定义default,如该页第三个答案所示。

    【讨论】:

    • 我也尝试过,但问题相同:没有调用 default() 方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-01
    • 2013-08-29
    • 2010-12-01
    • 2013-11-07
    • 2019-02-21
    • 2011-07-09
    相关资源
    最近更新 更多