如何通过键找到特定的json值?

时间:2012-12-27 03:15:59

标签: python json search

有一个像这样的json:

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

如果不迭代所有json,我怎样才能找到所有P1的值?

PS:P1可以在json的任何地方。

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

8 个答案:

答案 0 :(得分:18)

正如我在其他答案中所说,我认为没有办法找到与"P1"键相关的所有值而不迭代整个结构。但是,我在考虑accepted answer到另一个问题How to get string objects instead of Unicode from JSON?

时,想出了更好的方法来做到这一点。

基本思想是使用object_hook仅接受的json.loads()参数来观察正在解码的内容并检查所追求的值。 注意:只有当表示形式为JSON Object(即大括号{}中包含的内容)时,此方法才有效,如示例json中所示。

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)

输出:

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

答案 1 :(得分:12)

前几天我遇到了同样的问题。我结束了搜索整个对象,并考虑了列表和dicts。以下代码段允许您搜索多个键的第一个出现。

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))

它返回一个dict,键是搜索的键。 Haystack预计已经是一个Python对象了,所以在将它传递给deep_search之前你必须先做json.loads。

欢迎任何有关优化的评论!

答案 2 :(得分:10)

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

由于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']

答案 3 :(得分: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)

答案 4 :(得分:6)

将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是字符串)

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

答案 5 :(得分:2)

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

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

    def _find_values(id, obj):
        try:
            for key, value in obj.iteritems():
                if key == id:
                    results.append(value)
                elif not isinstance(value, basestring):
                    _find_values(id, value)
        except AttributeError:
            pass

        try:
            for item in obj:
                if not isinstance(item, basestring):
                    _find_values(id, item)
        except TypeError:
            pass

    if not isinstance(obj, basestring):
        _find_values(id, obj)
    return results

import json

with open('data.json') as json_file:
    obj = json.load(json_file)

print find_values('P1', obj)

答案 6 :(得分:2)

请记住,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']

答案 7 :(得分:1)

您还可以使用生成器在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