【问题标题】:How do I get all of one key from a list of dicts in Python?如何从 Python 中的 dicts 列表中获取所有一个键?
【发布时间】:2015-07-20 04:27:15
【问题描述】:

我有一个像这样的大型 JSON 文件:

{
  "data" : [
    {"album": "I Look to You", "writer": "Leon Russell", "artist": "Whitney Houston", "year": "2009", "title": "\"A Song for You\""},
    {"album": "Michael Zager Band", "writer": "Michael Zager", "artist": "Whitney Houston", "year": "1983", "title": "\"Life's a Party\""},
    {"album": "Paul Jabara & Friends", "writer": "Paul Jabara", "artist": "Whitney Houston", "year": "1978", "title": "\"Eternal Love\""},
    ...

...我正在尝试制作一个非常简单的 API 来获取不同的值。现在我可以很容易地获得localhost/data/1/title,例如获得第一个标题值,但我想通过localhost/titles 或其他方式获得所有的标题。我将如何修改此处的 do_GET 方法以添加此类功能?

def do_GET(self):
    self.send_response(200)
    self.send_header('Content-type', 'application/json')
    self.end_headers()

    path = self.path[1:]
    components = string.split(path, '/')

    node = content
    for component in components:
        if len(component) == 0 or component == "favicon.ico":
            continue

        if type(node) == dict:
            node = node[component]

        elif type(node) == list:
            node = node[int(component)]

    self.wfile.write(json.dumps(node))

    return

【问题讨论】:

  • 你试过python的json模块了吗? docs.python.org/2/library/json.html 这可能会有所帮助。
  • @HaochenWu,我想她已经在使用那个模块了。
  • 哦,我明白了。您的代码中有转储。那么loads函数有什么问题呢?它应该返回一个包含 json 文件中所有结构的对象。你可以从那开始,它应该很容易迭代。抱歉,我可能仍然没有在这里得到您的问题。
  • 您的 do_GET 函数似乎是一些 Web 应用程序,但如果没有更多上下文,很难看出它的作用。

标签: python json dictionary list-comprehension


【解决方案1】:

这里的答案将遵循您当前的动态 URL 模式,无需重大架构更改或要求。

在这里,我使用“all”代替您的 url 模式中的给定数字索引,因为我觉得这更能代表您的 data/[item(s)]/[attribute] 范式

以下是一些 URL 和示例输出:

  1. /data/1/album => “迈克尔·扎格乐队”
  2. /data/0/title => “一首歌给你”
  3. /data/all/title => [“给你的歌”、“生活是一场派对”、“永恒的爱”]
  4. /data/all/year => ["2009", "1983", "1978"]
  5. /data/1 => {“专辑”:“迈克尔·扎格乐队”,“标题”:“生活是一场派对”,“作家”:“迈克尔·扎格”,“年份”:“1983”,“艺术家”:“惠特尼·休斯顿"}

PS - 我稍微改变了架构,使用递归,我认为这更好地遵循你想要做的事情。

def do_GET(self):
    self.send_response(200)
    self.send_header('Content-type', 'application/json')
    self.end_headers()

    path = self.path[1:]
    components = string.split(path, '/')

    node = parse_node(content, components)

    self.wfile.write(json.dumps(node))

    return

def parse_node(node, components):
    # For a valid node and component list:
    if node and len(components) and components[0] != "favicon.ico":
        # Dicts will return parse_node of the top-level node component found, 
        # reducing the component list by 1
        if type(node) == dict:
            return parse_node(node.get(components[0], None), components[1:])

        elif type(node) == list:
            # A list with an "all" argument will return a full list of sub-nodes matching the rest of the URL criteria
            if components[0] == "all":
                return [parse_node(n, components[1:]) for n in node]
            # A normal list node request will work as it did previously
            else:
                return parse_node(node[int(components[0])], components[1:])
    else:
        return node

    # Handle bad URL
    return None

【讨论】:

    【解决方案2】:

    我认为您遇到了麻烦,因为您试图遍历路径组件以确定要做什么。这是解决问题的一种有点复杂的方法。

    我将首先定义您希望 API 支持的“路由”或“操作”,然后编写代码来处理其中的每一个。这就是大多数 Web 框架的运行方式(例如 django's URL patternsflask's routes)。在您的代码中使用相同的模式非常简单。

    所以,从您的描述看来,您需要两条路线:

    /data/{id}/{attr} - look up the value of `attr` for the given `id`
    /{attr} - search all items for `attr`
    

    我还将简化“title”与“titles”并仅使用单数形式,因为复数可能会比它的价值更麻烦。但如果你真的想这样做,有一些库可以提供帮助(例如 this one)。

    一旦我们决定 URL 将遵循这两种模式,就很容易检查组件是否与这两种模式匹配。请注意,我在这里简化了您的代码以使其运行,因为我不确定do_GET 是如何被调用的,或者self 是什么:

    import json
    
    JSON = {
        "data" : [
            {"album": "I Look to You", "writer": "Leon Russell", "artist": "Whitney Houston", "year": "2009", "title": "\"A Song for You\""},
            {"album": "Michael Zager Band", "writer": "Michael Zager", "artist": "Whitney Houston", "year": "1983", "title": "\"Life's a Party\""},
            {"album": "Paul Jabara & Friends", "writer": "Paul Jabara", "artist": "Whitney Houston", "year": "1978", "title": "\"Eternal Love\""},
        ]
    }
    
    def do_GET(path):
        path = path[1:]
        components = path.split('/')
    
        if components[0] == 'favicon.ico':
            return "favicon response"
        elif len(components) == 0 or not path:
            return "error response"
        elif len(components) == 3 and components[0] == "data":
            #/data/{id}/{attr} - look up the value of `attr` for the given `id`
            key, item_id, attr = components
            item_id = int(item_id)
            return json.dumps(JSON[key][item_id][attr])
        elif len(components) == 1:
            #/{attr} - search all items for `attr`
            attr = components[0]
            out = []
            for k in JSON:
                for d in JSON[k]:
                    if attr in d:
                        out.append(d[attr])
            return json.dumps(out)
        else:
            return "unknown response"
    
        return json.dumps(node)
    
    if __name__ == "__main__":
        urls = [
            "/data/1/title",
            "/title",
            "/some_missing_attr",
            "/favicon.ico",
            "/",
        ]
        for u in urls:
            print u, "->", do_GET(u)
    

    输出:

    /data/1/title -> "\"Life's a Party\""
    /title -> ["\"A Song for You\"", "\"Life's a Party\"", "\"Eternal Love\""]
    /some_missing_attr -> []
    /favicon.ico -> favicon response
    / -> error response
    

    除非您真的想在任意 JSON 中进行任意嵌套查找,否则这应该可以正常工作。如果是这种情况,那么我认为您提出的 URL 不会起作用,您怎么知道“/titles”应该搜索所有元素而“/data”会查找一个元素?如果你真的想这样做,我会在谷歌搜索“JSON 查询语言”,看看你可以重用哪些项目或从中获得想法。

    【讨论】:

      【解决方案3】:

      这是一个非常模糊的想法,但我希望这个概念能够被理解。

      在本例中,如果 url 不以 'data' 开头,它会将数据集合与 url 中指定的组件(如 'titles')进行映射。

      def do_GET(self):
          self.send_response(200)
          self.send_header('Content-type', 'application/json')
          self.end_headers()
      
          path = self.path[1:]
          components = string.split(path, '/')
      
          if components and components[0] != 'data':
              node = map(lambda x: x.get(components[0]), content)
          else:
              node = content
              for component in components:
                  if len(component) == 0 or component == "favicon.ico":
                      continue
      
                  if type(node) == dict:
                      node = node[component]
      
                  elif type(node) == list:
                      node = node[int(component)]
      
          self.wfile.write(json.dumps(node))
      
          return
      

      【讨论】:

        猜你喜欢
        • 2021-02-04
        • 1970-01-01
        • 2020-12-04
        • 2019-08-09
        • 1970-01-01
        • 2020-02-13
        • 1970-01-01
        • 2017-12-27
        • 2012-05-15
        相关资源
        最近更新 更多