Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/299.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python解码JSON中的嵌套JSON_Python_Json_Recursion - Fatal编程技术网

Python解码JSON中的嵌套JSON

Python解码JSON中的嵌套JSON,python,json,recursion,Python,Json,Recursion,我正在处理一个API,不幸的是它返回了格式错误(或“格式怪异”,更确切地说——谢谢@fjarri)的JSON,但从积极的方面来说,我认为这可能是一个机会,让我了解递归以及JSON。这是我用来记录训练的应用程序,我正在尝试制作备份脚本 我可以很好地收到JSON,但即使在requests.get(api\u url.JSON()(或JSON.loads(requests.get(api\u url.text))之后,其中一个值仍然是JSON编码的字符串。幸运的是,我可以将字符串json.loads(

我正在处理一个API,不幸的是它返回了格式错误(或“格式怪异”,更确切地说——谢谢@fjarri)的JSON,但从积极的方面来说,我认为这可能是一个机会,让我了解递归以及JSON。这是我用来记录训练的应用程序,我正在尝试制作备份脚本

我可以很好地收到JSON,但即使在
requests.get(api\u url.JSON()
(或
JSON.loads(requests.get(api\u url.text)
)之后,其中一个值仍然是JSON编码的字符串。幸运的是,我可以将字符串
json.loads()
正确解码为dict。特定的键是可预测的:
timezone\u id
,而它的值是不同的(因为数据记录在多个时区)。例如,解码后,它可能是:
dump
ed作为
时区id:{“name\”:“America/Denver\”,“seconds\”:“-21600\”}“
,或者
加载到Python中作为
时区id:“{”name:“America/Denver”,“seconds:“-21600”}

问题是我使用这个API来检索大量的数据,这些数据有几层目录和列表,而双重编码的
时区id
在多个级别上出现

这是我到目前为止的一些示例数据,但看起来我离基础还很远

#! /usr/bin/env python3

import json
from pprint import pprint

my_input = r"""{
    "hasMore": false,
    "checkins": [
        {
            "timestamp": 1353193745000,
            "timezone_id": "{\"name\":\"America/Denver\",\"seconds\":\"-21600\"}",
            "privacy_groups": [
                "private"
            ],
            "meta": {
                "client_version": "3.0",
                "uuid": "fake_UUID"
            },
            "client_id": "fake_client_id",
            "workout_name": "Workout (Nov 17, 2012)",
            "fitness_workout_json": {
                "exercise_logs": [
                    {
                        "timestamp": 1353195716000,
                        "type": "exercise_log",
                        "timezone_id": "{\"name\":\"America/Denver\",\"seconds\":\"-21600\"}",
                        "workout_log_uuid": "fake_UUID"
                    },
                    {
                        "timestamp": 1353195340000,
                        "type": "exercise_log",
                        "timezone_id": "{\"name\":\"America/Denver\",\"seconds\":\"-21600\"}",
                        "workout_log_uuid": "fake_UUID"
                    }
                ]
            },
            "workout_uuid": ""
        },
        {
            "timestamp": 1354485615000,
            "user_id": "fake_ID",
            "timezone_id": "{\"name\":\"America/Denver\",\"seconds\":\"-21600\"}",
            "privacy_groups": [
                "private"
            ],
            "meta": {
                "uuid": "fake_UUID"
            },
            "created": 1372023457376,
            "workout_name": "Workout (Dec 02, 2012)",
            "fitness_workout_json": {
                "exercise_logs": [
                    {
                        "timestamp": 1354485615000,
                        "timezone_id": "{\"name\":\"America/Denver\",\"seconds\":\"-21600\"}",
                        "workout_log_uuid": "fake_UUID"
                    },
                    {
                        "timestamp": 1354485584000,
                        "timezone_id": "{\"name\":\"America/Denver\",\"seconds\":\"-21600\"}",
                        "workout_log_uuid": "fake_UUID"
                    }
                ]
            },
            "workout_uuid": ""
        }]}"""

def recurse(obj):
    if isinstance(obj, list):
        for item in obj:
            return recurse(item)
    if isinstance(obj, dict):
        for k, v in obj.items():
            if isinstance(v, str):
                try:
                    v = json.loads(v)
                except ValueError:
                    pass
                obj.update({k: v})
            elif isinstance(v, (dict, list)):
                return recurse(v)

pprint(json.loads(my_input, object_hook=recurse))
有什么好方法可以在不改变对象其余部分的情况下加载所有这些双编码值的建议吗?非常感谢

这篇文章似乎是一个很好的参考:


编辑:这被标记为一个可能的副本——我认为它完全不同,正如我已经演示的那样,使用
json.loads()
不起作用。解决方案最终需要一个
object\u hook
,这是我在解码json时从未使用过的,在前面的问题中也没有提到。

因此,每次json加载程序完成构建字典时,都会调用json加载程序中的
object\u hook
t是最内部的字典,向外工作

object\u hook
回调所给出的字典被该函数返回的内容替换

因此,你不需要自己递归,加载器的本质是让你首先访问最内在的东西

我认为这会对你有用:

def hook(obj):
    value = obj.get("timezone_id")
    # this is python 3 specific; I would check isinstance against 
    # basestring in python 2
    if value and isinstance(value, str):
        obj["timezone_id"] = json.loads(value, object_hook=hook)
    return obj
data = json.loads(my_input, object_hook=hook)
当我测试它时,它似乎有我认为你在寻找的效果

我可能不会尝试对每个字符串值进行解码——我会从策略上说,在您希望存在json对象双重编码的地方调用它。如果您尝试对每个字符串进行解码,您可能会意外地解码本应为字符串的内容(如字符串
“12345“
当它是API返回的字符串时)


另外,您现有的函数比它需要的复杂,如果您总是返回
obj
(无论是否更新其内容),您的主要问题是您的
对象钩子
函数不应该递归
json.loads()
负责递归本身,并在每次找到字典时调用您的函数(aka
obj
始终是字典)。因此,您只需要修改有问题的键并返回dict——这应该满足您的要求:

def flatten_hook(obj):
    for key, value in obj.iteritems():
        if isinstance(value, basestring):
            try:
                obj[key] = json.loads(value, object_hook=flatten_hook)
            except ValueError:
                pass
    return obj

pprint(json.loads(my_input, object_hook=flatten_hook))

但是,如果您知道有问题的(双重编码)条目总是采用特定的形式(例如,
key==“timezone\u id”
),那么可能更安全的做法是只对这些键调用
json.loads()
,正如马特·安德森在回答中所建议的那样。

首先,您的json并没有格式错误,它的格式很奇怪(否则您将无法加载它)。其次,我认为您的解决方案没有任何问题,除非您知道
timezone\u id
是罪魁祸首,那么您为什么要尝试
json.load()<代码>每一个字符串值?可能的jjaRi复制:那么,是否会考虑JSON畸形的IFF抛出异常?是否有更好的方法来描述JSON,它的输出不是预期的输出,或者甚至是预期的类型?JSON是不合格的,如果它不是按照标准形成的,这相当于抛出一个分析器。错误(假设解析器正确实现了标准)。JSON的“预期输出”是存储在其中的任何内容,即
JSON.load(JSON.dump(data_structure))==data_structure
。很公平——当我写Q时,我停了下来,并认为有人会教我语义(谢谢,顺便说一句)。在这种情况下,问题似乎出在
数据结构的格式上,而不是json,因此将其描述为“格式错误的json”是不正确的。天啊,我最初认为
对象钩子
就是这样工作的,并尝试了类似的方法,但运气不佳。谢谢!我不认为
json.loads
钩子()内
需要
对象钩子,是吗?如果
时区id
有三重编码值,我想可以。@n8henrie不,内部
加载
可能不需要使用
对象钩子
。不应该有什么伤害,而且可能对更复杂的n级嵌套结构很重要。Gah,应该知道。您的代码正在使用我上面的示例,但仍然无法使用完整的API数据集。最终它是一个python2 python3错误(您的代码注释将我包括在内)。决定给Atom一个机会,由于某些原因,它没有使用我在其运行程序中指定的可执行文件进行更新。一些
import sys;print(sys.version))
s,一切都准备好了。我想知道这是否是我之前没有让
对象\u hook
正常工作的原因(
type==unicode
而不是
str
?)。无论如何,再次感谢。@n8henrie不客气。是的,在Python2上,json加载程序总是返回unicode,这是不可配置的(即使您正在编写和重新编写)