Создание собственного JSONEncoder - PullRequest
7 голосов
/ 20 июня 2011

Я использую Python 2.7 и пытаюсь создать собственный подкласс FloatEncoder в JSONEncoder.Я следовал за многими примерами, такими как 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 (), но он также не вызывался.

Ответы [ 2 ]

2 голосов
/ 20 июня 2011

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

JSONEncoder В доставке с Python 2.7 нет метода _iterencode, поэтомувот почему это не называют.Также быстрый взгляд на json/encoder.py показывает, что этот класс написан таким образом, что затрудняет изменение поведения кодирования с плавающей точкой.Возможно, было бы лучше разделить проблемы и округлить числа перед тем, как выполнять сериализацию JSON.

EDIT : Алекс Мартелли также предлагает решение с обезьяньим патчем в связанном ответе. Проблема этого подхода заключается в том, что вы вводите глобальную модификацию поведения библиотеки 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()
0 голосов
/ 20 июня 2011

Не определять _iterencode, определять default, как показано в третьем ответе на этой странице.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...