如何将JSON转换为对象?

11
我需要将一个JSON字符串转换为Python对象。所谓的“对象”,是指像这样的“新”的Python3对象:

I need to convert a json-string to python object. By object I mean "new" python3 object like:


class MyClass(object):

我在jsonpickle的文档中找到了一些帮助。但是我找到的都是将对象先转换为json,然后再将其反向转换的教程。

我想要将来自Rest-API的json字符串进行转换。

以下是我目前所做的内容:

import requests
import jsonpickle

class Goal(object):
    def __init__(self):
        self.GoaldID = -1
        self.IsPenalty = False

class Match(object):
    def __init__(self):
        self.Goals = []

headers = {
    "Content-Type": "application/json; charset=utf-8"
}

url = "https://www.openligadb.de/api/getmatchdata/39738"

result = requests.get(url=url, headers=headers)
obj = jsonpickle.decode(result.json)
print (obj)

这会导致:

TypeError: the JSON object must be str, bytes or bytearray, not 'method'

对我来说很清楚,jsonpickle无法将其转换为我的类(Goal、Match),因为我没有告诉jsonpickle应该在哪个类中将输出转换。问题是我不知道如何告诉jsonpickle将JSON转换为类型为Match的对象?还有,我怎样才能告诉它目标列表应该是List<Goal>类型的?


obj = jsonpickle.decode(result.content) => 这将会给你一个字典。 - falsetru
obj = result.json() 也会给你一个字典。 - falsetru
3个回答

9
以下内容将为您提供一个字典:
obj = jsonpickle.decode(result.content)  # NOTE: `.content`, not `.json`

obj = result.json()

但是以上方法都无法得到你所需要的(python对象(而不是字典))。因为该URL返回的JSON未使用jsonpickle.encode编码——这会向生成的JSON中添加额外信息(类似于{"py/object": "__main__.Goal", ....})。


>>> import jsonpickle
>>> class Goal(object):
...     def __init__(self):
...         self.GoaldID = -1
...         self.IsPenalty = False
...
>>> jsonpickle.encode(Goal())
'{"py/object": "__main__.Goal", "IsPenalty": false, "GoaldID": -1}'
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# JSON encoded with jsonpickle.encode  (default unpicklable=True)
#   => additional python class information attached
#   => can be decoded back to Python object
>>> jsonpickle.decode(jsonpickle.encode(Goal()))
<__main__.Goal object at 0x10af0e510>


>>> jsonpickle.encode(Goal(), unpicklable=False)
'{"IsPenalty": false, "GoaldID": -1}'
# with unpicklable=False   (similar output with json.dumps(..))
#   => no python class information attached
#   => cannot be decoded back to Python object, but a dict
>>> jsonpickle.decode(jsonpickle.encode(Goal(), unpicklable=False))
{'IsPenalty': False, 'GoaldID': -1}

如果你想要一个实际的 Python 对象而不是一个字典,也就是说你更喜欢使用 dic.Goals.[0].GoalGetterName 而不是 dic["Goals"][0]["GoalGetterName"],可以使用 json.loads 并且使用 object_hook 。

import json
import types    
import requests

url = "https://www.openligadb.de/api/getmatchdata/39738"

result = requests.get(url)
data = json.loads(result.content, object_hook=lambda d: types.SimpleNamespace(**d))
# OR   data = result.json(object_hook=lambda d: types.SimpleNamespace(**d))
goal_getter = data.Goals[0].GoalGetterName
# You get `types.SimpleNamespace` objects in place of dictionaries

@Sebi,我没有使用过这个,但是看看这个:http://jsonmodels.readthedocs.io/en/latest/ - falsetru
这是因为我不喜欢使用魔术字符串访问,例如:dic["Goals"][0]["GoalGetterName"],我更喜欢match.goals[0].goalGetterName,但也许这需要太多的努力。 - Sebi
@Sebi,如果您不想定义映射,可以定义自定义JSONDecoder,将JSON对象转换为types.SimpleNamespace而不是Python字典。 - falsetru
@Sebi,顺便说一下,dic["Goals"][0]["GoalGetterName"]没有什么魔法。只是简单的项访问 ;) - falsetru
@Sebi,没问题。愉快地编写Python代码吧! - falsetru
显示剩余6条评论

7
你是不是指像这样的东西?
import json

class JsonObject(object):   

    def __init__(self, json_content):
        data = json.loads(json_content)
        for key, value in data.items():
            self.__dict__[key] = value      


jo = JsonObject("{\"key1\":1234,\"key2\":\"Hello World\"}")
print(jo.key1)

输出如下:

1234
[Finished in 0.4s]

1
最近Python版本的一种干净方法可能是使用marshmallow-dataclass
from dataclasses import field
from marshmallow_dataclass import dataclass 
from typing import List

@dataclass
class Goal:
    GoaldID: int = field(default=-1)
    IsPenalty: bool = field(default=False)


@dataclass
class Match:
    Goals: List[Goal] = field(default_factory=lambda: [])

my_match, _ = Match.Schema().load(result.json())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接