【问题标题】:How to find a particular JSON value by key?如何按键查找特定的 JSON 值?
【发布时间】:2012-12-12 12:07:23
【问题描述】:

有一个这样的JSON:

{
  "P1": "ss",
  "Id": 1234,
  "P2": {
      "P1": "cccc"
  },
  "P3": [
      {
          "P1": "aaa"
      }
  ]
}

如何在不迭代所有 JSON 的情况下找到所有 P1 的值?

P.S.:P1 可以在 JSON 中任何地方

如果没有方法可以做到这一点,你能告诉我如何遍历 JSON 吗?

【问题讨论】:

标签: python json search


【解决方案1】:

正如我在other answer 中所说,我认为没有一种方法可以在不遍历整个结构的情况下找到与"P1" 键关联的所有值。但是,当我查看@Mike Brennan 的answer 到另一个与JSON 相关的问题How to get string objects instead of Unicode from JSON? 时,我想出了更好的方法来做这件事。

基本思想是使用json.loads() 接受的object_hook 参数来观察正在解码的内容并检查所需要的值。

注意:这仅适用于 JSON object(即包含在 curly braces{} 中的内容)的表示形式,如您的示例中所示。

from __future__ import print_function
import json

def find_values(id, json_repr):
    results = []

    def _decode_dict(a_dict):
        try:
            results.append(a_dict[id])
        except KeyError:
            pass
        return a_dict

    json.loads(json_repr, object_hook=_decode_dict) # Return value ignored.
    return results

json_repr = '{"P1": "ss", "Id": 1234, "P2": {"P1": "cccc"}, "P3": [{"P1": "aaa"}]}'
print(find_values('P1', json_repr))

(Python 3)输出:

['cccc', 'aaa', 'ss']

【讨论】:

    【解决方案2】:

    我认为没有任何方法可以在不遍历整个结构的情况下找到与 P1 关联的所有值。这是一种递归方式,首先将 JSON 对象反序列化为等效的 Python 对象。为了简化事情,大部分工作都是通过递归私有嵌套函数完成的。

    import json
    
    try:
        STRING_TYPE = basestring
    except NameError:
        STRING_TYPE = str  # Python 3
    
    def find_values(id, obj):
        results = []
    
        def _find_values(id, obj):
            try:
                for key, value in obj.items():  # dict?
                    if key == id:
                        results.append(value)
                    elif not isinstance(value, STRING_TYPE):
                        _find_values(id, value)
            except AttributeError:
                pass
    
            try:
                for item in obj:  # iterable?
                    if not isinstance(item, STRING_TYPE):
                        _find_values(id, item)
            except TypeError:
                pass
    
        if not isinstance(obj, STRING_TYPE):
            _find_values(id, obj)
        return results
    
    json_repr = '{"P1": "ss", "Id": 1234, "P2": {"P1": "cccc"}, "P3": [{"P1": "aaa"}]}'
    
    obj = json.loads(json_repr)
    print(find_values('P1', obj))
    

    【讨论】:

      【解决方案3】:

      记住 json 只是一个字符串,使用带有前瞻和后视的正则表达式可以非常快速地完成这项任务。

      通常情况下,json 会从对外部 api 的请求中提取,因此包含显示其工作原理的代码但已被注释掉。

      import re
      #import requests
      #import json
      
      #r1 = requests.get( ... url to some api ...)
      #JSON = str(json.loads(r1.text))
      JSON = """
       {
        "P1": "ss",
        "Id": 1234,
        "P2": {
            "P1": "cccc"
        },
        "P3": [
           {
                "P1": "aaa"
           }
        ]
       }
      """
      rex1  = re.compile('(?<=\"P1\": \")[a-zA-Z_\- ]+(?=\")')
      rex2 = rex1.findall(JSON)  
      print(rex2)
      
      #['ss', 'cccc', 'aaa']
      

      【讨论】:

        【解决方案4】:

        您还可以使用生成器在 json.load() 之后搜索对象。

        我在这里回答的代码示例:https://stackoverflow.com/a/39016088/5250939

        def item_generator(json_input, lookup_key):
            if isinstance(json_input, dict):
                for k, v in json_input.iteritems():
                    if k == lookup_key:
                        yield v
                    else:
                        for child_val in item_generator(v, lookup_key):
                            yield child_val
            elif isinstance(json_input, list):
                for item in json_input:
                    for item_val in item_generator(item, lookup_key):
                        yield item_val
        

        【讨论】:

          【解决方案5】:

          我解决这个问题的方法会有所不同。

          由于 JSON 不允许深度优先搜索,因此将 json 转换为 Python 对象,将其提供给 XML 解码器,然后提取您打算搜索的节点

          from xml.dom.minidom import parseString
          import json        
          def bar(somejson, key):
              def val(node):
                  # Searches for the next Element Node containing Value
                  e = node.nextSibling
                  while e and e.nodeType != e.ELEMENT_NODE:
                      e = e.nextSibling
                  return (e.getElementsByTagName('string')[0].firstChild.nodeValue if e 
                          else None)
              # parse the JSON as XML
              foo_dom = parseString(xmlrpclib.dumps((json.loads(somejson),)))
              # and then search all the name tags which are P1's
              # and use the val user function to get the value
              return [val(node) for node in foo_dom.getElementsByTagName('name') 
                      if node.firstChild.nodeValue in key]
          
          bar(foo, 'P1')
          [u'cccc', u'aaa', u'ss']
          bar(foo, ('P1','P2'))
          [u'cccc', u'cccc', u'aaa', u'ss']
          

          【讨论】:

          • 什么是xmlrpclib?我认为python 3不支持它。我收到错误消息。
          • @JafferWilson:Python 2 documentation 表示“xmlrpclib 模块已在 Python 3 中重命名为 xmlrpc.client。”它的 Python 3 文档为 here
          • 不幸的是,它似乎只在值为字符串或我做错了什么时才有效。
          • @viveksinghggits:在 python 中没有 json 数据类型。您必须将 json 作为字符串传递。
          • @Abhijit 我同意,但我的意思是 bar(foo, 'Id') 会导致错误,因为键 Id 的值是 int。
          【解决方案6】:

          前几天我也遇到了同样的问题。我最终只是搜索了整个对象并考虑了列表和字典。下面的 sn-ps 允许您搜索 first 多个键的出现。

          import json
          
          def deep_search(needles, haystack):
              found = {}
              if type(needles) != type([]):
                  needles = [needles]
          
              if type(haystack) == type(dict()):
                  for needle in needles:
                      if needle in haystack.keys():
                          found[needle] = haystack[needle]
                      elif len(haystack.keys()) > 0:
                          for key in haystack.keys():
                              result = deep_search(needle, haystack[key])
                              if result:
                                  for k, v in result.items():
                                      found[k] = v
              elif type(haystack) == type([]):
                  for node in haystack:
                      result = deep_search(needles, node)
                      if result:
                          for k, v in result.items():
                              found[k] = v
              return found
          
          deep_search(["P1", "P3"], json.loads(json_string))
          

          它返回一个字典,其中的键是搜索的键。 Haystack 应该已经是一个 Python 对象了,所以你必须在将它传递给 deep_search 之前执行 json.loads。

          欢迎任何用于优化的 cmets!

          【讨论】:

          • 我知道这是一个旧答案,但我只想说我通过在两个循环中检查 len(needles) == len(found) 来调整您的解决方案,以在我已经找到所有键的情况下缩短执行时间.
          【解决方案7】:

          使用json 将 json 转换为 Python 对象,然后递归处理效果最好。此示例确实包括遍历列表。

          import json
          def get_all(myjson, key):
              if type(myjson) == str:
                  myjson = json.loads(myjson)
              if type(myjson) is dict:
                  for jsonkey in myjson:
                      if type(myjson[jsonkey]) in (list, dict):
                          get_all(myjson[jsonkey], key)
                      elif jsonkey == key:
                          print myjson[jsonkey]
              elif type(myjson) is list:
                  for item in myjson:
                      if type(item) in (list, dict):
                          get_all(item, key)
          

          【讨论】:

            【解决方案8】:

            将 JSON 转换为 Python 并递归搜索是迄今为止最简单的:

            def findall(v, k):
              if type(v) == type({}):
                 for k1 in v:
                     if k1 == k:
                        print v[k1]
                     findall(v[k1], k)
            
            findall(json.loads(a), 'P1')
            

            (其中 a 是字符串)

            示例代码忽略了数组。添加它作为练习。

            【讨论】:

            • @lichengwu -- 正如我所写,“示例代码忽略了数组。添加它作为练习。”添加elif
            猜你喜欢
            • 2014-01-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-12-24
            相关资源
            最近更新 更多