2014-10-29 59 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找到。