【问题标题】:How to convert JSON data into a Python object?如何将 JSON 数据转换为 Python 对象?
【发布时间】:2011-09-28 13:40:36
【问题描述】:

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

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

我在 Django (Python) 中的当前视图(request.POST 包含 JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • 这很好用,但是如何处理复杂的 JSON 数据对象?
  • 如果我能以某种方式将此 JSON 对象转换为 Python 对象以便于使用,那不是更好吗?

【问题讨论】:

  • 通常 JSON 被转换为普通列表或字典。那是你要的吗?还是您希望将 JSON 直接转换为自定义类型?
  • 我想把它转换成一个对象,我可以使用“.”来访问它。 .就像上面的例子一样 -> reponse.name、response.education.id 等等......
  • 使用dicts 是进行面向对象编程的一种弱酱方式。字典是向代码读者传达期望的一种非常糟糕的方式。使用字典,您如何清楚且可重用地指定某些字典键值对是必需的,而另一些则不是?如何确认给定值在可接受的范围或设置内?特定于您正在使用的对象类型的函数(又名方法)呢?字典既方便又通用,但太多的开发人员表现得好像他们忘记了 Python 是一种面向对象的语言是有原因的。
  • 这个github.com/jsonpickle/jsonpickle有一个python库(评论因为答案在线程中太低并且无法访问。)

标签: python json django


【解决方案1】:

更新

使用 Python3,您可以在一行中使用 SimpleNamespaceobject_hook

import json
from types import SimpleNamespace

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse JSON into an object with attributes corresponding to dict keys.
x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
print(x.name, x.hometown.name, x.hometown.id)

旧答案 (Python2)

在 Python2 中,您可以使用namedtupleobject_hook 在一行中完成(但是对于许多嵌套对象来说速度很慢):

import json
from collections import namedtuple

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse JSON into an object with attributes corresponding to dict keys.
x = json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
print x.name, x.hometown.name, x.hometown.id

或者,轻松地重用它:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def json2obj(data): return json.loads(data, object_hook=_json_object_hook)

x = json2obj(data)

如果您希望它处理不是好的属性名称的键,请查看namedtuplerename parameter

【讨论】:

  • 这可能会导致Value错误,ValueError:类型名称和字段名称不能以数字开头:'123'
  • 作为 Python 的新手,我很感兴趣,当安全是一个问题时,这是否也是一种节省。
  • 这会在解析时每次遇到 JSON 对象时创建一个新的 不同 类,对吧?
  • 有趣。我认为不能保证依靠d.keys()d.values() 以相同的顺序迭代,但我错了。 docs 说:“如果键、值和项目视图被迭代而没有对字典进行干预修改,项目的顺序将直接对应。”。很高兴知道如此小的本地代码块。不过,我会添加一条评论,以明确提醒维护者注意这种依赖关系的代码。
  • 我不知道有什么好的通用反向操作。任何单个的命名元组都可以使用x._asdict() 转换为字典,这可能有助于简单的情况。
【解决方案2】:

查看jsonmodule documentation 中标题为专业化 JSON 对象解码的部分。您可以使用它将 JSON 对象解码为特定的 Python 类型。

这是一个例子:

class User(object):
    def __init__(self, name, username):
        self.name = name
        self.username = username

import json
def object_decoder(obj):
    if '__type__' in obj and obj['__type__'] == 'User':
        return User(obj['name'], obj['username'])
    return obj

json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}',
           object_hook=object_decoder)

print type(User)  # -> <type 'type'>

更新

如果您想通过 json 模块访问字典中的数据,请执行以下操作:

user = json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}')
print user['name']
print user['username']

就像一本普通的字典。

【讨论】:

  • 嘿,我刚刚在阅读,我意识到字典完全可以,只是我想知道如何将 JSON 对象转换为字典以及如何从字典中访问这些数据?
  • 太棒了,这几乎是清楚的,只是想知道一个小事,如果有这个对象 -> { 'education' : { 'name1' : 456 , 'name2' : 567 } },如何我要访问这些数据吗?
  • 只是 topLevelData['education']['name1'] ==> 456. 有意义吗?
  • @Ben:我认为你的评论不恰当。在目前所有的答案中,它是唯一一个让课程正确的答案。这意味着:这是一次操作,结果使用正确的类型。 Pickle 本身适用于与 JSON 不同的应用程序(二进制与文本表示),而 jsonpickle 是一个非标准库。我很想看看你如何解决 std json lib 没有为对象钩子提供上层解析树的问题
  • 我必须同意@Ben 的观点。这是一个非常糟糕的解决方案。根本不可扩展。您需要将字段名称维护为字符串和字段。如果您想重构您的字段,则解码将失败(当然,已经序列化的数据将不再相关)。 jsonpickle 已经很好地实现了相同的概念
【解决方案3】:

你可以试试这个:

class User(object):
    def __init__(self, name, username):
        self.name = name
        self.username = username

import json
j = json.loads(your_json)
u = User(**j)

只需创建一个新对象,并将参数作为映射传递。

注意:它不适用于嵌套类。


您也可以使用带有对象的 JSON:

import json
class Address(object):
    def __init__(self, street, number):
        self.street = street
        self.number = number

    def __str__(self):
        return "{0} {1}".format(self.street, self.number)

class User(object):
    def __init__(self, name, address):
        self.name = name
        self.address = Address(**address)

    def __str__(self):
        return "{0} ,{1}".format(self.name, self.address)

if __name__ == '__main__':
    js = '''{"name":"Cristian", "address":{"street":"Sesame","number":122}}'''
    j = json.loads(js)
    print(j)
    u = User(**j)
    print(u)

【讨论】:

  • 我得到 TypeError: 'User' object is not subscriptable
  • 这应该是公认的答案。为我工作的广告比其他所有广告都简单得多。
  • 我没有使用 *args, **kwargs,但解决方案有效。
  • User(**j) 说它缺少名称和用户名参数,dict 是如何初始化的?
  • 效果很好。对原始 init 标头和简单的导入字典或 json 到对象的最小且不显眼的修改。太棒了!
【解决方案4】:

这不是代码高尔夫,但这是我的最短技巧,使用 types.SimpleNamespace 作为 JSON 对象的容器。

与领先的namedtuple解决方案相比,是:

  • 可能更快/更小,因为它不会为每个对象创建一个类
  • 更短
  • 没有rename 选项,并且可能对无效标识符的键有相同的限制(在封面下使用setattr

例子:

from __future__ import print_function
import json

try:
    from types import SimpleNamespace as Namespace
except ImportError:
    # Python 2.x fallback
    from argparse import Namespace

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

x = json.loads(data, object_hook=lambda d: Namespace(**d))

print (x.name, x.hometown.name, x.hometown.id)

【讨论】:

  • 顺便说一下,序列化库 Marshmallow 提供了类似的功能,其 @post_load 装饰器。 marshmallow.readthedocs.io/en/latest/…
  • 为了避免对 argparse 的依赖:将 argparse 导入替换为 from types import SimpleNamespace 并使用:x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
  • 这是最优雅的解决方案,应该在顶部。
  • 编辑为在 Python 3.x 下运行时使用 @maxschlepzig 的解决方案(不幸的是,types.SimpleNamespace 在 2.7 中不存在)。
  • 这是迄今为止最干净的方法。唯一需要指出的是,SimpleNamespace 将逐字解析 JSON 布尔值“真”或“假”——在这些情况下,可以在 JSON 中使用 1 和 0 来代替建立真实性。
【解决方案5】:

这是一个快速而肮脏的 json pickle 替代方法

import json

class User:
    def __init__(self, name, username):
        self.name = name
        self.username = username

    def to_json(self):
        return json.dumps(self.__dict__)

    @classmethod
    def from_json(cls, json_str):
        json_dict = json.loads(json_str)
        return cls(**json_dict)

# example usage
User("tbrown", "Tom Brown").to_json()
User.from_json(User("tbrown", "Tom Brown").to_json()).to_json()

【讨论】:

  • 这不是好方法。首先 to_json 和 from_json 不应该放在你的类中。其次,它不适用于嵌套类。
【解决方案6】:

对于复杂的对象,可以使用JSON Pickle

用于将任意对象图序列化为 JSON 的 Python 库。 它可以接受几乎任何 Python 对象并将对象转换为 JSON。 此外,它可以将对象重构回 Python。

【讨论】:

  • 我认为jsonstruct 更好。 jsonstruct originally a fork of jsonpickle (Thanks guys!). The key difference between this library and jsonpickle is that during deserialization, jsonpickle requires Python types to be recorded as part of the JSON. This library intends to remove this requirement, instead, requires a class to be passed in as an argument so that its definition can be inspected. It will then return an instance of the given class. This approach is similar to how Jackson (of Java) works.
  • jsonstruct 的问题在于它似乎没有被维护(实际上,它看起来被遗弃了)并且它无法转换对象列表,例如'[{"name":"object1"},{"name":"object2"}]'。 jsonpickle 也不能很好地处理它。
  • 我不知道为什么这个答案没有得到更多的选票。大多数其他解决方案都非常流行。有人为 JSON 反序列化开发了一个很棒的库——为什么不使用它呢?此外,列表似乎可以正常工作 - 你对它有什么问题@LS?
  • @guyarad,问题是: x= jsonpickle.decode('[{"name":"object1"},{"name":"object2"}]') 给出字典列表([{'name': 'object1'}, {'name': 'object2'}]),而不是具有属性的对象列表 (x[0].name == 'object1'),这就是原来的需要的问题。为此,我最终使用了 eddygeek 建议的 object_hook/Namespace 方法,但 ubershmekel 的快速/脏方法看起来也不错。我想我可以将 object_hook 与 jsonpickle 的 set_encoder_options() 一起使用(未记录!),但它需要比基本 json 模块更多的代码。我很想被证明是错误的!
  • @LS 如果您无法控制输入,这确实是 OP 所要求的,jsonpickle 并不理想,因为它期望每个级别的实际类型(如果缺少,将假定基本类型) .两种解决方案都很“可爱”。
【解决方案7】:

如果您使用的是 Python 3.5+,则可以使用 jsons 序列化和反序列化为普通的旧 Python 对象:

import jsons

response = request.POST

# You'll need your class attributes to match your dict keys, so in your case do:
response['id'] = response.pop('user_id')

# Then you can load that dict into your class:
user = jsons.load(response, FbApiUser)

user.save()

您也可以让FbApiUser 继承自jsons.JsonSerializable 以获得更优雅:

user = FbApiUser.from_json(response)

如果您的类包含 Python 默认类型(如字符串、整数、列表、日期时间等),这些示例将起作用。不过,jsons 库将需要自定义类型的类型提示。

【讨论】:

    【解决方案8】:

    如果你使用的是 python 3.6+,你可以使用marshmallow-dataclass。与上面列出的所有解决方案相反,它既简单又类型安全:

    from marshmallow_dataclass import dataclass
    
    @dataclass
    class User:
        name: str
    
    user = User.Schema().load({"name": "Ramirez"})
    

    【讨论】:

    【解决方案9】:

    改进 lovasoa 的非常好的答案。

    如果你使用python 3.6+,你可以使用:
    pip install marshmallow-enum
    pip install marshmallow-dataclass

    它简单且类型安全。

    您可以在 string-json 中转换您的类,反之亦然:

    从对象到字符串 Json:

        from marshmallow_dataclass import dataclass
        user = User("Danilo","50","RedBull",15,OrderStatus.CREATED)
        user_json = User.Schema().dumps(user)
        user_json_str = user_json.data
    

    从字符串 Json 到对象:

        json_str = '{"name":"Danilo", "orderId":"50", "productName":"RedBull", "quantity":15, "status":"Created"}'
        user, err = User.Schema().loads(json_str)
        print(user,flush=True)
    

    类定义:

    class OrderStatus(Enum):
        CREATED = 'Created'
        PENDING = 'Pending'
        CONFIRMED = 'Confirmed'
        FAILED = 'Failed'
    
    @dataclass
    class User:
        def __init__(self, name, orderId, productName, quantity, status):
            self.name = name
            self.orderId = orderId
            self.productName = productName
            self.quantity = quantity
            self.status = status
    
        name: str
        orderId: str
        productName: str
        quantity: int
        status: OrderStatus
    

    【讨论】:

    • 您不需要构造函数,只需将 init=True 传递给 dataclass 即可。
    【解决方案10】:

    dacite 也可能是您的解决方案,它支持以下功能:

    • 嵌套结构
    • (基本)类型检查
    • 可选字段(即打字。可选)
    • 工会
    • 前向引用
    • 收藏
    • 自定义类型挂钩

    https://pypi.org/project/dacite/

    from dataclasses import dataclass
    from dacite import from_dict
    
    
    @dataclass
    class User:
        name: str
        age: int
        is_active: bool
    
    
    data = {
        'name': 'John',
        'age': 30,
        'is_active': True,
    }
    
    user = from_dict(data_class=User, data=data)
    
    assert user == User(name='John', age=30, is_active=True)
    

    【讨论】:

      【解决方案11】:

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

      在您的情况下,我猜您想从字典(使用json.loads 获得)转换为复杂对象response.education ; response.name,具有嵌套结构response.education.id 等... 所以这正是这个框架的目的。文档还不是很好,但是通过使用any2any.simple.MappingToObject,您应该能够非常轻松地做到这一点。如果您需要帮助,请询问。

      【讨论】:

      • Sebpiq,已安装 any2any,但无法理解预期的方法调用顺序。你能举一个简单的例子,将字典转换为每个键都有一个属性的 Python 对象吗?
      • 嗨@sansjoe!如果你是从 pypi 安装的,版本已经完全过时了,我在几周前做了一个完整的重构。您应该使用 github 版本(我需要进行适当的发布!)
      • 我是从 pypy 安装的,因为 github 说要从 pypy 安装。另外,你说 pypy 几个月前已经过时了。它没有用 :( 我提交了一个错误报告!github.com/sebpiq/any2any/issues/11
      【解决方案12】:

      由于没有人提供像我一样的答案,我将在此处发布。

      这是一个健壮的类,可以轻松地在我从my answer to another question复制的jsonstrdict之间来回转换:

      import json
      
      class PyJSON(object):
          def __init__(self, d):
              if type(d) is str:
                  d = json.loads(d)
      
              self.from_dict(d)
      
          def from_dict(self, d):
              self.__dict__ = {}
              for key, value in d.items():
                  if type(value) is dict:
                      value = PyJSON(value)
                  self.__dict__[key] = value
      
          def to_dict(self):
              d = {}
              for key, value in self.__dict__.items():
                  if type(value) is PyJSON:
                      value = value.to_dict()
                  d[key] = value
              return d
      
          def __repr__(self):
              return str(self.to_dict())
      
          def __setitem__(self, key, value):
              self.__dict__[key] = value
      
          def __getitem__(self, key):
              return self.__dict__[key]
      
      json_str = """... json string ..."""
      
      py_json = PyJSON(json_str)
      

      【讨论】:

        【解决方案13】:

        在寻找解决方案时,我偶然发现了这篇博文:https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/

        它使用与先前答案中所述相同的技术,但使用了装饰器。 我发现有用的另一件事是它在反序列化结束时返回一个类型化对象

        class JsonConvert(object):
            class_mappings = {}
        
            @classmethod
            def class_mapper(cls, d):
                for keys, cls in clsself.mappings.items():
                    if keys.issuperset(d.keys()):   # are all required arguments present?
                        return cls(**d)
                else:
                    # Raise exception instead of silently returning None
                    raise ValueError('Unable to find a matching class for object: {!s}'.format(d))
        
            @classmethod
            def complex_handler(cls, Obj):
                if hasattr(Obj, '__dict__'):
                    return Obj.__dict__
                else:
                    raise TypeError('Object of type %s with value of %s is not JSON serializable' % (type(Obj), repr(Obj)))
        
            @classmethod
            def register(cls, claz):
                clsself.mappings[frozenset(tuple([attr for attr,val in cls().__dict__.items()]))] = cls
                return cls
        
            @classmethod
            def to_json(cls, obj):
                return json.dumps(obj.__dict__, default=cls.complex_handler, indent=4)
        
            @classmethod
            def from_json(cls, json_str):
                return json.loads(json_str, object_hook=cls.class_mapper)
        

        用法:

        @JsonConvert.register
        class Employee(object):
            def __init__(self, Name:int=None, Age:int=None):
                self.Name = Name
                self.Age = Age
                return
        
        @JsonConvert.register
        class Company(object):
            def __init__(self, Name:str="", Employees:[Employee]=None):
                self.Name = Name
                self.Employees = [] if Employees is None else Employees
                return
        
        company = Company("Contonso")
        company.Employees.append(Employee("Werner", 38))
        company.Employees.append(Employee("Mary"))
        
        as_json = JsonConvert.to_json(company)
        from_json = JsonConvert.from_json(as_json)
        as_json_from_json = JsonConvert.to_json(from_json)
        
        assert(as_json_from_json == as_json)
        
        print(as_json_from_json)
        

        【讨论】:

          【解决方案14】:

          稍微扩展 DS 的答案,如果您需要对象是可变的(namedtuple 不是),您可以使用 recordclass 库而不是 namedtuple:

          import json
          from recordclass import recordclass
          
          data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'
          
          # Parse into a mutable object
          x = json.loads(data, object_hook=lambda d: recordclass('X', d.keys())(*d.values()))
          

          修改后的对象可以使用simplejson很容易地转换回json:

          x.name = "John Doe"
          new_json = simplejson.dumps(x)
          

          【讨论】:

            【解决方案15】:

            稍微修改@DS响应,从文件加载:

            def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
            def load_data(file_name):
              with open(file_name, 'r') as file_data:
                return file_data.read().replace('\n', '')
            def json2obj(file_name): return json.loads(load_data(file_name), object_hook=_json_object_hook)
            

            有一件事:这不能加载前面有数字的项目。像这样:

            {
              "1_first_item": {
                "A": "1",
                "B": "2"
              }
            }
            

            因为“1_first_item”不是有效的 Python 字段名。

            【讨论】:

              【解决方案16】:

              这里给出的答案没有返回正确的对象类型,因此我在下面创建了这些方法。如果您尝试向给定 JSON 中不存在的类添加更多字段,它们也会失败:

              def dict_to_class(class_name: Any, dictionary: dict) -> Any:
                  instance = class_name()
                  for key in dictionary.keys():
                      setattr(instance, key, dictionary[key])
                  return instance
              
              
              def json_to_class(class_name: Any, json_string: str) -> Any:
                  dict_object = json.loads(json_string)
                  return dict_to_class(class_name, dict_object)
              

              【讨论】:

                【解决方案17】:

                我认为最轻的解决方案是

                import json
                from typing import NamedTuple
                
                _j = '{"name":"Иван","age":37,"mother":{"name":"Ольга","age":58},"children":["Маша","Игорь","Таня"],"married": true,' \
                     '"dog":null} '
                
                
                class PersonNameAge(NamedTuple):
                    name: str
                    age: int
                
                
                class UserInfo(NamedTuple):
                    name: str
                    age: int
                    mother: PersonNameAge
                    children: list
                    married: bool
                    dog: str
                
                
                j = json.loads(_j)
                u = UserInfo(**j)
                
                print(u.name, u.age, u.mother, u.children, u.married, u.dog)
                
                >>> Ivan 37 {'name': 'Olga', 'age': 58} ['Mary', 'Igor', 'Jane'] True None
                

                【讨论】:

                  【解决方案18】:

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

                  关于转换我们有 3 种情况:

                  • 列表
                  • 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, Ruby, C#, Java, Swift, Rust, Kotlin, C++, @9 @、JavaScriptElmJSON Schema

                  • 从 JSON、JSON Schema 和 TypeScript 以交互方式生成类型和(反)序列化代码
                  • 将 JSON/JSON 架构/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 Code Marketplace 中免费提供。

                  【讨论】:

                  • 刚看到你甚至可以在线使用它:quicktype.io app
                  • 对于一次性使用,我想在线解决方案会有所帮助。对于过程的自动化,即重复步骤,在线解决方案不可用。在该示例中,书面解决方案将根据需要进行调整,以成功解决问题。
                  【解决方案19】:

                  如果您使用的是 Python 3.6 或更新版本,您可以查看 squema - 一个用于静态类型数据结构的轻量级模块。它使您的代码易于阅读,同时提供简单的数据验证、转换和序列化,无需额外工作。您可以将其视为命名元组和数据类的更复杂和更有主见的替代方案。以下是你可以如何使用它:

                  from uuid import UUID
                  from squema import Squema
                  
                  
                  class FbApiUser(Squema):
                      id: UUID
                      age: int
                      name: str
                  
                      def save(self):
                          pass
                  
                  
                  user = FbApiUser(**json.loads(response))
                  user.save()
                  

                  【讨论】:

                  • 这也更类似于JVM语言的做法。
                  【解决方案20】:

                  你可以使用

                  x = Map(json.loads(response))
                  x.__class__ = MyClass
                  

                  在哪里

                  class Map(dict):
                      def __init__(self, *args, **kwargs):
                          super(Map, self).__init__(*args, **kwargs)
                          for arg in args:
                              if isinstance(arg, dict):
                                  for k, v in arg.iteritems():
                                      self[k] = v
                                      if isinstance(v, dict):
                                          self[k] = Map(v)
                  
                          if kwargs:
                              # for python 3 use kwargs.items()
                              for k, v in kwargs.iteritems():
                                  self[k] = v
                                  if isinstance(v, dict):
                                      self[k] = Map(v)
                  
                      def __getattr__(self, attr):
                          return self.get(attr)
                  
                      def __setattr__(self, key, value):
                          self.__setitem__(key, value)
                  
                      def __setitem__(self, key, value):
                          super(Map, self).__setitem__(key, value)
                          self.__dict__.update({key: value})
                  
                      def __delattr__(self, item):
                          self.__delitem__(item)
                  
                      def __delitem__(self, key):
                          super(Map, self).__delitem__(key)
                          del self.__dict__[key]
                  

                  对于通用的、面向未来的解决方案。

                  【讨论】:

                    【解决方案21】:

                    我正在寻找与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
                    #  }
                    #}
                    

                    【讨论】:

                      【解决方案22】:

                      已经有多个可行的答案,但是有一些个人制作的小型库可以为大多数用户解决问题。

                      一个例子是json2object。给定一个已定义的类,它将 json 数据反序列化为您的自定义模型,包括自定义属性和子对象。

                      它的使用非常简单。来自图书馆 wiki 的示例:

                      from json2object import jsontoobject as jo
                      
                      class Student:
                          def __init__(self):
                              self.firstName = None
                              self.lastName = None
                              self.courses = [Course('')]
                      
                      class Course:
                          def __init__(self, name):
                              self.name = name
                      
                      data = '''{
                      "firstName": "James",
                      "lastName": "Bond",
                      "courses": [{
                          "name": "Fighting"},
                          {
                          "name": "Shooting"}
                          ]
                      }
                      '''
                      
                      model = Student()
                      result = jo.deserialize(data, model)
                      print(result.courses[0].name)

                      【讨论】:

                      • 由 Milovan above 提出的 quicktype.io 做得稍微好一点,因为它使用了 Python 提供的更多功能。但有时拥有一个 python 库肯定会更有用!
                      【解决方案23】:
                      class SimpleClass:
                          def __init__(self, **kwargs):
                              for k, v in kwargs.items():
                                  if type(v) is dict:
                                      setattr(self, k, SimpleClass(**v))
                                  else:
                                      setattr(self, k, v)
                      
                      
                      json_dict = {'name': 'jane doe', 'username': 'jane', 'test': {'foo': 1}}
                      
                      class_instance = SimpleClass(**json_dict)
                      
                      print(class_instance.name, class_instance.test.foo)
                      print(vars(class_instance))
                      

                      【讨论】:

                        【解决方案24】:

                        dataclass-wizard 是一个现代选项,同样适用于您。它支持自动 key case 转换,例如 camelCaseTitleCase,这两者在 API 响应中都很常见。

                        将实例转储到 dict/JSON 时的默认键转换是 camelCase,但可以使用主数据类上提供的元配置轻松覆盖。

                        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}
                        

                        设置 Meta config 的示例,当序列化为 dict/JSON 时将字段转换为 lisp-case

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

                        【讨论】:

                          【解决方案25】:

                          Python3.x

                          根据我的知识,我能达到的最佳方法是这个。
                          请注意,此代码也处理 set()。
                          这种方法是通用的,只需要类的扩展(在第二个例子中)。
                          请注意,我只是对文件执行此操作,但根据您的喜好修改行为很容易。

                          然而这是一个编解码器。

                          再做一点工作,您就可以用其他方式构建您的类。 我假设一个默认构造函数来实例化它,然后我更新类字典。

                          import json
                          import collections
                          
                          
                          class JsonClassSerializable(json.JSONEncoder):
                          
                              REGISTERED_CLASS = {}
                          
                              def register(ctype):
                                  JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype
                          
                              def default(self, obj):
                                  if isinstance(obj, collections.Set):
                                      return dict(_set_object=list(obj))
                                  if isinstance(obj, JsonClassSerializable):
                                      jclass = {}
                                      jclass["name"] = type(obj).__name__
                                      jclass["dict"] = obj.__dict__
                                      return dict(_class_object=jclass)
                                  else:
                                      return json.JSONEncoder.default(self, obj)
                          
                              def json_to_class(self, dct):
                                  if '_set_object' in dct:
                                      return set(dct['_set_object'])
                                  elif '_class_object' in dct:
                                      cclass = dct['_class_object']
                                      cclass_name = cclass["name"]
                                      if cclass_name not in self.REGISTERED_CLASS:
                                          raise RuntimeError(
                                              "Class {} not registered in JSON Parser"
                                              .format(cclass["name"])
                                          )
                                      instance = self.REGISTERED_CLASS[cclass_name]()
                                      instance.__dict__ = cclass["dict"]
                                      return instance
                                  return dct
                          
                              def encode_(self, file):
                                  with open(file, 'w') as outfile:
                                      json.dump(
                                          self.__dict__, outfile,
                                          cls=JsonClassSerializable,
                                          indent=4,
                                          sort_keys=True
                                      )
                          
                              def decode_(self, file):
                                  try:
                                      with open(file, 'r') as infile:
                                          self.__dict__ = json.load(
                                              infile,
                                              object_hook=self.json_to_class
                                          )
                                  except FileNotFoundError:
                                      print("Persistence load failed "
                                            "'{}' do not exists".format(file)
                                            )
                          
                          
                          class C(JsonClassSerializable):
                          
                              def __init__(self):
                                  self.mill = "s"
                          
                          
                          JsonClassSerializable.register(C)
                          
                          
                          class B(JsonClassSerializable):
                          
                              def __init__(self):
                                  self.a = 1230
                                  self.c = C()
                          
                          
                          JsonClassSerializable.register(B)
                          
                          
                          class A(JsonClassSerializable):
                          
                              def __init__(self):
                                  self.a = 1
                                  self.b = {1, 2}
                                  self.c = B()
                          
                          JsonClassSerializable.register(A)
                          
                          A().encode_("test")
                          b = A()
                          b.decode_("test")
                          print(b.a)
                          print(b.b)
                          print(b.c.a)
                          

                          编辑

                          通过更多研究,我找到了一种无需 SUPERCLASS 注册方法调用即可泛化的方法,使用 元类

                          import json
                          import collections
                          
                          REGISTERED_CLASS = {}
                          
                          class MetaSerializable(type):
                          
                              def __call__(cls, *args, **kwargs):
                                  if cls.__name__ not in REGISTERED_CLASS:
                                      REGISTERED_CLASS[cls.__name__] = cls
                                  return super(MetaSerializable, cls).__call__(*args, **kwargs)
                          
                          
                          class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable):
                          
                              def default(self, obj):
                                  if isinstance(obj, collections.Set):
                                      return dict(_set_object=list(obj))
                                  if isinstance(obj, JsonClassSerializable):
                                      jclass = {}
                                      jclass["name"] = type(obj).__name__
                                      jclass["dict"] = obj.__dict__
                                      return dict(_class_object=jclass)
                                  else:
                                      return json.JSONEncoder.default(self, obj)
                          
                              def json_to_class(self, dct):
                                  if '_set_object' in dct:
                                      return set(dct['_set_object'])
                                  elif '_class_object' in dct:
                                      cclass = dct['_class_object']
                                      cclass_name = cclass["name"]
                                      if cclass_name not in REGISTERED_CLASS:
                                          raise RuntimeError(
                                              "Class {} not registered in JSON Parser"
                                              .format(cclass["name"])
                                          )
                                      instance = REGISTERED_CLASS[cclass_name]()
                                      instance.__dict__ = cclass["dict"]
                                      return instance
                                  return dct
                          
                              def encode_(self, file):
                                  with open(file, 'w') as outfile:
                                      json.dump(
                                          self.__dict__, outfile,
                                          cls=JsonClassSerializable,
                                          indent=4,
                                          sort_keys=True
                                      )
                          
                              def decode_(self, file):
                                  try:
                                      with open(file, 'r') as infile:
                                          self.__dict__ = json.load(
                                              infile,
                                              object_hook=self.json_to_class
                                          )
                                  except FileNotFoundError:
                                      print("Persistence load failed "
                                            "'{}' do not exists".format(file)
                                            )
                          
                          
                          class C(JsonClassSerializable):
                          
                              def __init__(self):
                                  self.mill = "s"
                          
                          
                          class B(JsonClassSerializable):
                          
                              def __init__(self):
                                  self.a = 1230
                                  self.c = C()
                          
                          
                          class A(JsonClassSerializable):
                          
                              def __init__(self):
                                  self.a = 1
                                  self.b = {1, 2}
                                  self.c = B()
                          
                          
                          A().encode_("test")
                          b = A()
                          b.decode_("test")
                          print(b.a)
                          # 1
                          print(b.b)
                          # {1, 2}
                          print(b.c.a)
                          # 1230
                          print(b.c.c.mill)
                          # s
                          

                          【讨论】:

                            【解决方案26】:

                            这不是一件很困难的事情,我看到上面的答案,大多数在“列表”中都有性能问题

                            这段代码比上面的快很多

                            import json 
                            
                            class jsonify:
                                def __init__(self, data):
                                    self.jsonify = data
                            
                                def __getattr__(self, attr):
                                    value = self.jsonify.get(attr)
                                    if isinstance(value, (list, dict)):
                                        return jsonify(value)
                                    return value
                            
                                def __getitem__(self, index):
                                    value = self.jsonify[index]
                                    if isinstance(value, (list, dict)):
                                        return jsonify(value)
                                    return value
                            
                                def __setitem__(self, index, value):
                                    self.jsonify[index] = value
                            
                                def __delattr__(self, index):
                                    self.jsonify.pop(index)
                            
                                def __delitem__(self, index):
                                    self.jsonify.pop(index)
                            
                                def __repr__(self):
                                    return json.dumps(self.jsonify, indent=2, default=lambda x: str(x))
                            

                            例子

                            response = jsonify(
                                {
                                    'test': {
                                        'test1': [{'ok': 1}]
                                    }
                                }
                            )
                            response.test -> jsonify({'test1': [{'ok': 1}]})
                            response.test.test1 -> jsonify([{'ok': 1}])
                            response.test.test1[0] -> jsonify({'ok': 1})
                            response.test.test1[0].ok -> int(1)
                            

                            【讨论】:

                              【解决方案27】:

                              这似乎是一个 AB 问题(问 A 实际问题在哪里 B)。

                              问题的根源是:如何有效地引用/修改深层嵌套的 JSON 结构,而无需执行 ob['foo']['bar'][42]['quux'],这带来了打字挑战、代码膨胀问题、可读性问题和错误捕获问题?

                              使用glom

                              https://glom.readthedocs.io/en/latest/tutorial.html

                              from glom import glom
                              
                              # Basic deep get
                              
                              data = {'a': {'b': {'c': 'd'}}}
                              
                              print(glom(data, 'a.b.c'))
                              

                              它还将处理列表项:glom(data, 'a.b.c.42.d')

                              我已经将它与一个简单的实现进行了基准测试:

                              def extract(J, levels):
                                  # Twice as fast as using glom
                                  for level in levels.split('.'):
                                      J = J[int(level) if level.isnumeric() else level]
                                  return J
                              

                              ...它在复杂的 JSON 对象上返回 0.14 毫秒,而天真的 impl 则为 0.06 毫秒。

                              它还可以处理复杂的查询,例如拉出所有foo.bar.records 其中.name == 'Joe Bloggs'

                              编辑:

                              另一种高效的方法是递归使用覆盖__getitem____getattr__ 的类:

                              class Ob:
                                  def __init__(self, J):
                                      self.J = J
                              
                                  def __getitem__(self, index):
                                      return Ob(self.J[index])
                              
                                  def __getattr__(self, attr):
                                      value = self.J.get(attr, None)
                                      return Ob(value) if type(value) in (list, dict) else value
                              

                              现在你可以这样做了:

                              ob = Ob(J)
                              
                              # if you're fetching a final raw value (not list/dict
                              ob.foo.bar[42].quux.leaf
                              
                              # for intermediate values
                              ob.foo.bar[42].quux.J
                              

                              这也令人惊讶地很好地进行了基准测试。可与我之前的幼稚 impl 相媲美。如果有人能找到整理非叶查询访问权限的方法,请发表评论!

                              【讨论】:

                                【解决方案28】:

                                使用几乎总是安装的json module (new in Python 2.6) 或simplejson 模块。

                                【讨论】:

                                • 您好,谢谢您的回复。您能否发布一个如何解码 JSON 然后访问该数据的示例?
                                • 嘿,现在你明白了,但不知何故,我更喜欢在不知道的情况下做,然后对其进行逆向工程:D.
                                • @Zach:我链接到的文档顶部有一些示例。
                                猜你喜欢
                                • 2021-06-20
                                • 2016-09-23
                                • 1970-01-01
                                • 1970-01-01
                                • 2018-10-11
                                • 2011-03-17
                                • 2011-05-20
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多