2013-01-31 53 views
1

下面的類用於提供可以作爲json編碼字典通過網絡傳遞的通用對象。我其實試圖編碼一個字典(!),但它不會工作。json.dumps Python上的TypeError字典

我知道它可以與自定義編碼器類一起工作,但我不明白爲什麼當我編碼字典時需要它。

有人可以解釋TypeError或提供一種方法來編碼這沒有繼承JSONEncoder?

這是不良行爲。

>>> def tree(): return CustomDict(tree) 
>>> d = tree() 
>>> d['one']['test']['four'] = 19 
>>> d.dict 
{ 'one' : { 'test': {'four': 19}}} 
>>> type(d.dict) 
<type 'dict'> 
>>> import json 
>>> json.dumps(d.dict) 
# stacktrace removed 
TypeError: {'one': {'test': {'four': 19}}} is not JSON serializable 
>>> normal_d = {'one': {'test': {'four': 19}}} 
>>> type(normal_d) 
<type 'dict'> 
>>> json.dumps(normal_d) 
"{'one': {'test': {'four': 19}}}" 
>>> normal_d == d 
True 

我很想能夠做到以下

>>>> json.dumps(dict(d)) 
"{'one': {'test': {'four': 19}}}" 

,但我加入了字典屬性爲「強制」(沒有明顯工作)。現在這是一個更大的謎團。下面是CustomDict類

class CustomDict(collections.MutableMapping):         
    """                   
    A defaultdict-like object that can also have properties and special methods 
    """                   

    def __init__(self, default_type=str, *args, **kwargs):      
     """                  
     instantiate as a default-dict (str if type not provided). Try to update 
     self with each arg, and then update self with kwargs.                 

     @param default_type: the type of the default dict      
     @type default_type: type (or class)          
     """                  
     self._type = default_type            
     self._store = collections.defaultdict(default_type)      
     self._dict = {}               

     for arg in args:               
      if isinstance(arg, collections.MutableMapping):      
       self.update(arg)             

     self.update(kwargs)              

    @property                 
    def dict(self):                
     return self._dict              

    def __contains__(self, key):             
     return key in self._store            

    def __len__(self):               
     return len(self._store)             

    def __iter__(self):               
     return iter(self._store)             

    def __getitem__(self, key):             
     self._dict[key] = self._store[key]          
     return self._store[key]             

    def __setitem__(self, key, val):            
     self._dict[key] = val             
     self._store[key] = val             

    def __delitem__(self, key):             
     del self._store[key]              

    def __str__(self):               
     return str(dict(self._store)) 

回答

1

你想使你的類型的dict一個子類,而不是collections.MutableMapping代碼,真的。

更妙的是,使用collections.defaultdict,而不是直接,它已經是dict一個子類,可以用來實現你的樹「類型」易:

from collections import defaultdict 

def Tree(): 
    return defaultdict(Tree) 

tree = Tree() 

示範:

>>> from collections import defaultdict 
>>> def Tree(): 
...  return defaultdict(Tree) 
... 
>>> tree = Tree() 
>>> tree['one']['two'] = 'foobar' 
>>> tree 
defaultdict(<function Tree at 0x107f40e60>, {'one': defaultdict(<function Tree at 0x107f40e60>, {'two': 'foobar'})}) 
>>> import json 
>>> json.dumps(tree) 
'{"one": {"two": "foobar"}}' 

如果你必須添加你自己的方法和行爲,然後我將子類defaultdict並建立在該基礎上:

class CustomDict(defaultdict): 
    pass 

由於這仍然是dict的子類,所以json庫會很樂意將其轉換爲JSON對象而無需特殊處理。

+0

的CustomDict類型不僅可用於樹()。它也將被廣泛地分類,並用於更復雜的對象,這些對象對__contains__,__getitem__等有不同的定義。我們希望將項存儲在defaultdict中以獲得該行爲,但不必使用defaultdict的內置方法。 – mehtunguh

+0

@mehtunguh:你是否保留'self._dict'只能將它變成JSON結果? –

+0

是的。我寧願不必存儲它,但只是作爲調查爲什麼json.dumps(dict(d))和json.dumps(dict(d._store))不起作用的一部分。 – mehtunguh

0

我忽略了一個事實,即INSIDE d.dict值沒有類型的字典

>>> type(d['one']) 
<class 'CustomDict'> 


def __getitem__(self, key):             
    self._dict[key] = self._store[key]          
    return self._store[key] 

愚蠢的錯誤。與

def __getitem__(self, key):             
    if isinstance(self._store[key], self.__class__):           
     self._dict[key] = self._store[key].dict 
    else: 
     self._dict[key] = self._store[key] 
    return self._store[key] 

固定這看似

+0

很明顯,你爲什麼需要'self._dict' *和*'self._store'。 JSON直接處理'dict'的子類。 –

+0

自我的默認類型。_store可能不可序列化。我添加了self._dict,以便測試其他可能性。這不是該課程最終實施的一部分。只是爲了發展。 – mehtunguh

+0

我認爲你這樣做是錯誤的;爲什麼不使用JSON API上的'default'掛鉤呢? –