34

有一个这样的 JSON:

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

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

PS:P1可以在 JSON 中的任何位置。

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

4

8 回答 8

26

正如我在另一个答案中所说,我认为没有一种方法可以在"P1"不迭代整个结构的情况下找到与键关联的所有值。但是,在查看@Mike Brennan对另一个与 JSON 相关的问题的回答时,我想出了更好的方法来做到这一点。如何从 JSON 中获取字符串对象而不是 Unicode?

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

注意:这仅在表示为 JSON object(即用花括号括 {}起来的内容)时才有效,如您的示例中所示。

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']
于 2012-12-27T18:46:59.090 回答
13

前几天我也遇到了同样的问题。我最终只是搜索了整个对象并考虑了列表和字典。以下片段允许您搜索多个键的第一次出现。

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。

欢迎大家提出优化意见!

于 2012-12-27T03:53:19.160 回答
11

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

由于 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']
于 2012-12-27T06:08:56.710 回答
10

使用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)
于 2012-12-27T03:47:40.420 回答
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 是字符串)

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

于 2012-12-27T03:25:46.210 回答
5

记住 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']
于 2017-02-12T02:17:43.163 回答
3

我认为没有任何方法可以在不遍历整个结构的情况下找到与 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))
于 2012-12-27T12:58:47.490 回答
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
于 2016-08-18T11:39:17.907 回答