2011-06-20 86 views
7

我正在運行Python 2.7,我試圖創建一個JSONEncoder的自定義FloatEncoder子類。我遵循了很多例子,如this,但似乎沒有任何工作。這裏是我FloatEncoder類:創建自定義JSONEncoder

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 ((.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) 

第一個斷言失敗,這意味着沒有被執行_iterencode。在閱讀JSON文檔之後,我試着覆蓋默認的()方法,但那個也沒有被調用。

+2

FWIW,默認'()'不會被調用,因爲如果輸入的是編碼器默認支持的類型之一,它甚至不會看你的自定義的方法。在'_iterencode()'的定義中比較'lib/json/encoder.py':'_default()'只在所有已知類型被覆蓋之後在'else:'分支中被調用。因此,您無法覆蓋已知類型的處理。 – Tomalak

回答

2

您似乎在生成JSON(基於測試示例)時將浮點值舍入到小數點後4位。

JSONEncoder與Python 2.7一起發貨沒有_iterencode方法,所以這就是爲什麼它沒有被調用。另外快速瀏覽一下json/encoder.py表明,這個類的編寫方式很難改變float編碼行爲。或許,最好將問題分開,並在執行JSON序列化之前圍繞浮點數。

編輯:亞歷克斯·馬爾泰利還提供在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 ((.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

不定義_iterencode,定義default,如該頁上的第三個答案所示。

+1

我也嘗試過,但它是同樣的問題:未調用default()方法。 – Matt