我想将JSON数据转换为Python对象。

我从Facebook API收到JSON数据对象,我想将其存储在数据库中。

我的当前视图在Django (Python)(请求。POST包含JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()

这很好,但是如何处理复杂的JSON数据对象呢? 如果我能以某种方式将这个JSON对象转换为易于使用的Python对象,是不是会更好?


当前回答

因此,我正在寻找一种不需要大量自定义反序列化代码就能解组任意类型(想想数据类的字典,或者数据类数组的字典的字典)的方法。

这是我的方法:

import json
from dataclasses import dataclass, make_dataclass

from dataclasses_json import DataClassJsonMixin, dataclass_json


@dataclass_json
@dataclass
class Person:
    name: str


def unmarshal_json(data, t):
    Unmarhsal = make_dataclass('Unmarhsal', [('res', t)],
                               bases=(DataClassJsonMixin,))
    d = json.loads(data)
    out = Unmarhsal.from_dict({"res": d})
    return out.res


unmarshalled = unmarshal_json('{"1": {"name": "john"} }', dict[str, Person])
print(unmarshalled)

打印:{'1':Person(name='john')}

其他回答

如果你正在寻找将JSON或任何复杂字典的类型安全反序列化到python类中,我强烈推荐python 3.7+的pydantic。它不仅有一个简洁的API(不需要编写“helper”样板),可以与Python数据类集成,而且具有复杂和嵌套数据结构的静态和运行时类型验证。

使用示例:

from pydantic import BaseModel
from datetime import datetime

class Item(BaseModel):
    field1: str | int           # union
    field2: int | None = None   # optional
    field3: str = 'default'     # default values

class User(BaseModel):
    name: str | None = None
    username: str
    created: datetime           # default type converters
    items: list[Item] = []      # nested complex types

data = {
    'name': 'Jane Doe',
    'username': 'user1',
    'created': '2020-12-31T23:59:00+10:00',
    'items': [
        {'field1': 1, 'field2': 2},
        {'field1': 'b'},
        {'field1': 'c', 'field3': 'override'}
    ]
}

user: User = User(**data)

要了解更多细节和特性,请查看文档中的pydantic的rational部分。

我正在寻找一个与recordclass一起工作的解决方案。RecordClass,支持嵌套对象,可用于json序列化和json反序列化。

扩展DS的答案,扩展BeneStr的解决方案,我想出了以下似乎有效的方法:

代码:

import json
import recordclass

class NestedRec(recordclass.RecordClass):
    a : int = 0
    b : int = 0

class ExampleRec(recordclass.RecordClass):
    x : int       = None
    y : int       = None
    nested : NestedRec = NestedRec()

class JsonSerializer:
    @staticmethod
    def dumps(obj, ensure_ascii=True, indent=None, sort_keys=False):
        return json.dumps(obj, default=JsonSerializer.__obj_to_dict, ensure_ascii=ensure_ascii, indent=indent, sort_keys=sort_keys)

    @staticmethod
    def loads(s, klass):
        return JsonSerializer.__dict_to_obj(klass, json.loads(s))

    @staticmethod
    def __obj_to_dict(obj):
        if hasattr(obj, "_asdict"):
            return obj._asdict()
        else:
            return json.JSONEncoder().default(obj)

    @staticmethod
    def __dict_to_obj(klass, s_dict):
        kwargs = {
            key : JsonSerializer.__dict_to_obj(cls, s_dict[key]) if hasattr(cls,'_asdict') else s_dict[key] \
                for key,cls in klass.__annotations__.items() \
                    if s_dict is not None and key in s_dict
        }
        return klass(**kwargs)

用法:

example_0 = ExampleRec(x = 10, y = 20, nested = NestedRec( a = 30, b = 40 ) )

#Serialize to JSON

json_str = JsonSerializer.dumps(example_0)
print(json_str)
#{
#  "x": 10,
#  "y": 20,
#  "nested": {
#    "a": 30,
#    "b": 40
#  }
#}

# Deserialize from JSON
example_1 = JsonSerializer.loads(json_str, ExampleRec)
example_1.x += 1
example_1.y += 1
example_1.nested.a += 1
example_1.nested.b += 1

json_str = JsonSerializer.dumps(example_1)
print(json_str)
#{
#  "x": 11,
#  "y": 21,
#  "nested": {
#    "a": 31,
#    "b": 41
#  }
#}

数据类向导是一种现代的选项,可以类似地为您工作。它支持自动键大小写转换,如camelCase或TitleCase,这两者在API响应中都很常见。

当将实例转储到dict/JSON时,默认的键转换是camelCase,但这可以很容易地使用主数据类上提供的Meta配置来覆盖。

https://pypi.org/project/dataclass-wizard/

from dataclasses import dataclass

from dataclass_wizard import fromdict, asdict


@dataclass
class User:
    name: str
    age: int
    is_active: bool


data = {
    'name': 'John',
    'age': 30,
    'isActive': True,
}

user = fromdict(User, data)
assert user == User(name='John', age=30, is_active=True)

json_dict = asdict(user)
assert json_dict == {'name': 'John', 'age': 30, 'isActive': True}

设置元配置的例子,当序列化为dict/JSON时,将字段转换为lisp-case:

DumpMeta(key_transform='LISP').bind_to(User)

我已经编写了一个名为any2any的小型(反)序列化框架,它可以帮助在两种Python类型之间进行复杂的转换。

在您的情况下,我猜您想从字典(通过json.loads获得)转换为复杂的对象response.education;Response.name,具有嵌套结构response.education.id,等等… 这就是这个框架的用途。文档还不是很好,但是通过使用any2any.simple。MappingToObject,你应该可以很容易地做到。如果需要帮助,请询问。

JSON到python对象

下面的代码递归地使用对象键创建动态属性。

JSON对象- fb_data.json:

{
    "name": "John Smith",
    "hometown": {
        "name": "New York",
        "id": 123
    },
    "list": [
        "a",
        "b",
        "c",
        1,
        {
            "key": 1
        }
    ],
    "object": {
        "key": {
            "key": 1
        }
    }
}

在转换中我们有三种情况:

列表 Dicts(新对象) Bool, int, float和STR

import json


class AppConfiguration(object):
    def __init__(self, data=None):
        if data is None:
            with open("fb_data.json") as fh:
                data = json.loads(fh.read())
        else:
            data = dict(data)

        for key, val in data.items():
            setattr(self, key, self.compute_attr_value(val))

    def compute_attr_value(self, value):
        if isinstance(value, list):
            return [self.compute_attr_value(x) for x in value]
        elif isinstance(value, dict):
            return AppConfiguration(value)
        else:
            return value


if __name__ == "__main__":
    instance = AppConfiguration()

    print(instance.name)
    print(instance.hometown.name)
    print(instance.hometown.id)
    print(instance.list[4].key)
    print(instance.object.key.key)

键值对是属性-对象。

输出:

John Smith
New York
123
1
1

将JSON作为代码粘贴

支持TypeScript、Python、Go、Ruby、c#、Java、Swift、Rust、Kotlin、c++、Flow、Objective-C、JavaScript、Elm、JSON Schema。

从JSON、JSON Schema和TypeScript中交互式地生成类型和(反)序列化代码 将JSON/JSON Schema/TypeScript作为代码粘贴

quicktype从示例JSON数据中推断类型,然后输出强类型模型和序列化器,以便用所需的编程语言处理这些数据。

输出:

# Generated by https://quicktype.io
#
# To change quicktype's target language, run command:
#
#   "Set quicktype target language"

from typing import List, Union


class Hometown:
    name: str
    id: int

    def __init__(self, name: str, id: int) -> None:
        self.name = name
        self.id = id


class Key:
    key: int

    def __init__(self, key: int) -> None:
        self.key = key


class Object:
    key: Key

    def __init__(self, key: Key) -> None:
        self.key = key


class FbData:
    name: str
    hometown: Hometown
    list: List[Union[Key, int, str]]
    object: Object

    def __init__(self, name: str, hometown: Hometown, list: List[Union[Key, int, str]], object: Object) -> None:
        self.name = name
        self.hometown = hometown
        self.list = list
        self.object = object

这个扩展可以在Visual Studio代码市场中免费获得。