2017-10-12 12 views
1

JSON只允許字符串作爲鍵。使用自定義編碼器將帶有十進制密鑰的python字典轉換爲JSON

以下代碼使用自定義JSONEncoder將Decimal值轉換爲字符串。

有沒有辦法指定一個編碼器,將Decimal鍵變成字符串?

import json 
import decimal 

class DecimalEncoder(json.JSONEncoder): 
    def default(self, obj): 
     if isinstance(obj, decimal.Decimal): 
      return str(obj) 
     return json.JSONEncoder.default(self, obj) 

d1 = {3: decimal.Decimal(50)} 
print(json.dumps(d1, cls=DecimalEncoder)) 

d2 = {decimal.Decimal(50): 3} 
json.dumps(d2, cls=DecimalEncoder) # TypeError: keys must be a string 

我正在使用python3.6

注意:顯然,我可以遍歷我的字典並用字符串值替換Decimal類型,但我希望能夠通過向編碼器添加行爲來找到更優雅的解決方案。

回答

0

不,你不能掛鉤鑰匙的處理,你必須在你編碼之前將它們轉換成。你可以用遞歸處理像這樣做:

from functools import singledispatch 

@singledispatch 
def string_keys(obj): 
    return obj 

@string_keys.register(dict) 
def _(d): 
    return {str(k): string_keys(v) for k, v in d.items()} 

@string_keys.register(list) 
def _(l): 
    return [string_keys(v) for v in l] 

這一切確實是轉換列表和類型的字典嵌套結構遞歸,所有按鍵被強制爲字符串。

使用此轉換爲JSON時:

json_encoded = json.dumps(string_keys(data)) 

您可以擴展此處理Decimal對象(鍵之外)也加入另一個註冊表:

@string_keys.register(Decimal) 
def _(d): 
    return str(d) 

走另一條路是有點棘手,除非明確標記Decimal鍵(帶有前綴),否則無法輕鬆區分以字符串開始的鍵和Decimal值。你可以使用一個try/except方法在這裏:

from functools import singledispatch 

@singledispatch 
def keys_to_decimal(obj): 
    return obj 

@keys_to_decimal.register(dict) 
def _(d): 
    def try_decimal(k): 
     try: 
      return Decimal(k) 
     except ValueError: 
      return k 
    return {try_decimal(k): keys_to_decimal(v) for k, v in d.items()} 

@keys_to_decimal.register(list) 
def _(l): 
    return [keys_to_decimal(v) for v in l] 

演示:

>>> string_keys([{Decimal(0): 'foo'}]) 
[{'0': 'foo'}] 
>>> keys_to_decimal(string_keys([{Decimal(0): 'foo'}])) 
[{Decimal('0'): 'foo'}] 
+0

我收到此錯誤'NameError:名字 'K' 是不是在該行 '{回報STR(K)defined' :string_keys(v)for v in d}'。 – ChrisGuest

+0

@ChrisGuest:抱歉,糾正。 –

+0

@ChrisGuest:'keys_to_decimal'處理中的另一個錯誤;現在實際測試。 :-) –

相關問題