2014-10-29 56 views
4

我想解析從Web服務中獲取的JSON到對象結構中。因此,我正在使用object_hook方法實現json.JSONDecoder的子類。我還沒有找到一個好方法,但是爲給定的數據選擇正確的類。對於具有相同屬性的類,似乎不可能識別出正確的類,因爲這需要知道密鑰。讓我們來看一個例子:如何正確將JSON轉換爲Python對象?

我有以下類別:

class Post: 
    def __init__(self, title, user=None, group=None): 
     self.title = title 
     self.user = user 
     self.group = group 

class Group: 
    def __init__(self, name): 
     self.name = name 

class User: 
    def __init__(self, name): 
     self.name = name 

可觀察到GroupUser類具有相同的屬性。現在我JSONDecoder看起來是這樣的:

class JSONDecoder(json.JSONDecoder): 

    def __init__(self, encoding="UTF-8"): 
     json.JSONDecoder.__init__(self, object_hook=self.dict_to_object) 

    def dict_to_object(self, d): 

     if "posts" in d: 
      return d["posts"] 
     if "title" in d: 
      if "user" in d: 
       return Post(d["title"], user=d["user"]) 
      if "group" in d: 
       return Post(d["title"], group=d["group"]) 
     if "name" in d: 
      # How to decide if User(d["name"]) or Group(d["name")? 
      return None 
     return None 

當它看到包含密鑰「名」的字典,它不能決定是否要創建一個GroupUser對象(因此我此刻返回None)。

JSON字符串我想解析如下所示:

s = """ 
{ "posts" : [ 
    {"title" : "Hello World", "user" : {"name" : "uli"}}, 
    {"title" : "Hello Group", "group" : {"name" : "Workgroup"}} 
    ] 
} 
""" 

這將導致後對象的列表,每個都具有一個標題和一個組或用戶。

這怎麼能以最好的方式解決?這是否積累了if-dict_to_object真的要走的路? (由於嵌套的JSON結構是完整的,所以實際代碼看起來更加混亂。)或者是否還有其他模式或庫應該使用? (雖然我寧願去用標準庫。)

+0

你能展示一個你想要解析的JSON的例子,以及你想分析它的對象嗎? – BrenBarn 2014-10-29 21:35:34

+0

好點!我添加了示例JSON。我希望我想要的對象結構現在已經很清楚了。 – Robert 2014-10-29 21:48:44

+1

解析JSON後,您可能會更好地「手動」創建對象。然後,您可以通過嵌套的字典遞歸地迭代,並以您需要的任何方式使用鍵和值。 – BrenBarn 2014-10-29 21:53:24

回答

0

在這種情況下,和IME經常用JSON解碼,最好在解碼時分配一個通用字典,不要使用object_hook,並推遲創建直到所有解碼後的第二遍都是單獨輸入的對象,當你可以任意檢查你正在處理的流和層次結構時,即哪個對象是父/子/同胞的哪一個。 (像@BrenBam說)

使用類方法make_xyz功能,而不是構造

object_hook往往看起來誘人,但很少是你想要的東西。只是因爲它在那裏,往往是錯誤的選擇。 如果你始終知道100%確定每個對象使用哪個類,那麼它是唯一正確的選擇(並且即使如此,只有在易於評估而不需要傳遞狀態的情況下,即在對象內部編寫臨時解析器時,鉤子),通常這些元素遵循特定順序,即JSON永遠不會變形等。

這裏您遇到了一個普遍問題:在這種特定情況下,看到{"name" : "xyz"}的構造函數無法知道它是什麼類型的JSON對象,只有看到"user"/"group" :可以的父對象。 一個解決方案是將所有類和構造函數重構爲類方法make_group(),make_user()。但是,這只是將你的第二次解碼傳遞給你的第一次解碼傳遞,沒有特別的原因,給我們一個巨大的脆弱的object_hook函數。 IME很少是個好主意。

0

一種方法是推遲UserGroup的創建,直到您將自己的標籤放在手邊。也就是說,直到你所創建的Post

def dict_to_object(self, d): 
    if "posts" in d: 
     return d["posts"] 
    if "title" in d: 
     if "user" in d: 
      d["user"] = User(d["user"]["name"]) 
     if "group" in d: 
      d["group"] = Group(d["group"]["name"]) 
     return Post(d["title"], 
        d.get("user", None), 
        d.get("group", None)) 
    return d 
0

OK,下面是我終於解決了這個問題,而從json.JSONDecoder繼承:

class JSONDecoder: 

    def decode_json(self, js): 
     posts = [] 
     if "posts" in js: 
      for p in js["posts"]: 
       if "user" in p: 
        posts.append(Post(p["title"], user=self._decode_user(p["user"]))) 
       if "group" in p: 
        posts.append(Post(p["title"], group=self._decode_group(p["group"]))) 
     return posts 

    def _decode_user(self, js): 
     return User(js["name"]) 

    def _decode_group(self, js): 
     return Group(js["name"]) 

可與JSONDecoder().decode_json(json.loads(s))被調用。 BW:完整的代碼可以在on Bitbucket找到。