2011-06-20 13 views
7

Estoy ejecutando Python 2.7 y estoy tratando de crear una subclase FloatEncoder personalizada de JSONEncoder. He seguido muchos ejemplos, como this, pero ninguno parece funcionar. Aquí está mi clase FloatEncoder:Creando JSONEncoder personalizado

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) 

Y aquí es donde yo llamo json.dumps:

with patch("utils.fileio.FloatEncoder") as float_patch: 
     for val,res in ((.0,'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) 

La primera aserción falla, lo que significa que no está siendo ejecutado _iterencode. Después de leer la documentación de JSON, intenté sobreescribir el método predeterminado() pero tampoco fue invocado.

+2

Fwiw, 'por defecto()' no se está llamando porque si la entrada es uno de los tipos del codificador soporta por defecto, ni siquiera mirar a su método personalizado. Compare 'lib/json/encoder.py', en la definición de' _iterencode() ': El' _default() 'solo se llama en la rama' else: ', después de que se hayan cubierto todos los tipos conocidos. Por lo tanto, no puede anular el manejo de un tipo conocido. – Tomalak

Respuesta

2

Parece que está intentando redondear los valores de flotación hasta 4 decimales al generar JSON (según los ejemplos de prueba).

JSONEncoder envío con Python 2.7 no tiene el método _iterencode, por eso es que no se llama. También un vistazo rápido al json/encoder.py sugiere que esta clase está escrita de tal manera que dificulta el cambio del comportamiento de codificación de flotación. Tal vez, sería mejor separar las preocupaciones y redondear las carrozas antes de hacer la serialización JSON.

EDITAR: Alex Martelli también suministra una solución de mono-parche en a related answer. El problema con este enfoque es que se está introduciendo una modificación global para json comportamiento biblioteca que puede afectar involuntariamente alguna otra parte del código de la aplicación que fue escrito con la suposición de que los flotadores fueron codificados sin redondeo.

Prueba esto:

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 ((.0, '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() 
-1

No defina _iterencode, defina default, como se muestra en la tercera respuesta en esa página.

+1

Lo intenté también pero es el mismo problema: no se está llamando al método predeterminado(). – Matt

Cuestiones relacionadas