【问题标题】:Deserialise a JSON string to nested objects using jsons使用 jsons 将 JSON 字符串反序列化为嵌套对象
【发布时间】:2019-09-11 19:42:39
【问题描述】:

我正在尝试使用 jsons 将 json 字符串反序列化为对象,但嵌套对象有问题,但无法解决语法问题。

作为示例,以下代码尝试将数据结构定义为一系列数据类,但未能反序列化嵌套对象 C 和 D ?语法显然是错误的,但我不清楚它应该如何构造

import jsons
from dataclasses import dataclass

@dataclass
class D:
    E: str
class C:
    id: int
    name:str
@dataclass
class test:
    A: str
    B: int
    C: C()
    D: D()

jsonString = {"A":"a","B":1,"C":[{"id":1,"name":"one"},{"id":2,"name":"two"}],"D":[{"E":"e"}]}
instance = jsons.load(jsonString, test)

谁能指出从 json 反序列化对象的正确方法?

【问题讨论】:

  • 需要dataclass还是直接用namedtuples
  • 我不喜欢dataClass(这是jsons 使用的并且很干净,但是C 类和D 类也有相关的方法(我在这个例子中没有详细说明)。我可以扩展 namedtuples 但这会变得一团糟?

标签: json python-3.x object python-dataclasses


【解决方案1】:

您的尝试有两个相对简单的问题:

  1. 你忘了用@dataclass装饰C
  2. Test.CTest.D 不是用类型定义的,而是用类型的 instances 定义的。 (此外,您希望两个字段都是给定类型的 列表,而不是每个字段的单个实例。)

给定代码

import jsons
from dataclasses import dataclass
from typing import List


@dataclass
class D:
    E: str


@dataclass  # Problem 1 fixed
class C:
    id: int
    name: str


@dataclass
class Test:
    A: str
    B: int
    C: List[C]  # Problem 2 fixed; List[C] not C() or even C
    D: List[D]  # Problem 2 fixed; List[D], not D() or even D

然后

>>> obj = {"A":"a", "B":1, "C": [{"id": 1,"name": "one"}, {"id": 2, "name": "two"}], "D":[{"E": "e"}]}
>>> jsons.load(obj, Test)
test(A='a', B=1, C=[C(id=1, name='one'), C(id=2, name='two')], D=[D(E='e')])

【讨论】:

  • 谢谢,#1 是错字,但 #2 正是我所缺少的
  • 虽然我可以理解为什么test.C 需要添加为一个列表,因为有多个实例,但test.D 不是这种情况,因为只有一个实例。比如我直接初始化test如下instance = test(D=D(E="e"))test.D被定义为对象D而不是List。同样,test.D 是否有类型定义以避免定义为 List
  • 只有一个,但它仍然是一个项目的列表。如果您的 Python dict 也可以是 {..., "D": {"E": "e"}},您需要使用 union 类型定义 D Union[D,List[D]]
  • 谢谢,但Union[D,List[D]] 仍将D 定义为一个项目的List 而不是单个对象Dtest
  • 更准确地说,提供给数据类的类型提示不会影响您可以直接分配给该字段的值,但jsons 似乎确实考虑了类型提示。
【解决方案2】:
from dataclasses import dataclass
from typing import List

from validated_dc import ValidatedDC


@dataclass
class D(ValidatedDC):
    E: str


@dataclass
class C(ValidatedDC):
    id: int
    name: str


@dataclass
class Test(ValidatedDC):
    A: str
    B: int
    C: List[C]
    D: List[D]


jsonString = {
    "A": "a",
    "B": 1,
    "C": [{"id": 1, "name": "one"}, {"id": 2, "name": "two"}],
    "D": [{"E": "e"}]
}

instance = Test(**jsonString)

assert instance.C == [C(id=1, name='one'), C(id=2, name='two')]
assert instance.C[0].id == 1
assert instance.C[1].name == 'two'

assert instance.D == [D(E='e')]
assert instance.D[0].E == 'e'

验证DC:https://github.com/EvgeniyBurdin/validated_dc

【讨论】:

    【解决方案3】:

    你可以这样做:

    from collections import namedtuple
    # First parameter is the class/tuple name, second parameter
    # is a space delimited string of varaibles.
    # Note that the variable names should match the keys from 
    # your dictionary of arguments unless only one argument is given.
    A = namedtuple("A", "a_val") # Here the argument `a_val` can be called something else
    B = namedtuple("B", "num")
    C = namedtuple("C", "id name")
    D = namedtuple("D", "E") # This must be `E` since E is the key in the dictionary.
    # If you dont want immutable objects to can use full classes
    # instead of namedtuples
    
    # A dictionary which matches the name of an object seen in a payload
    # to the object we want to create for that name.
    object_options = {
      "A": A,
      "B": B,
      "C": C,
      "D": D
    }
    my_objects = [] # This is the list of object we get from the payload
    
    jsonString = {"A":"a","B":1,"C":[{"id":1,"name":"one"},{"id":2,"name":"two"}],"D":[{"E":"e"}]}
    
    for key, val in jsonString.items():
        if key in object_options: # If this is a valid object
            if isinstance(val, list): # If it is a list of this object
                for v in val: # Then we need to add each object in the list
                    my_objects.append(object_options[key](**v))
            elif isinstance(val, dict): # If the object requires a dict then pass the whole dict as arugments
                my_objects.append(object_options[key](**val))
            else: # Else just add this object with a singular argument.
                my_objects.append(object_options[key](val))
    print(my_objects)
    

    输出:

    [A(a_val='a'), B(num=1), C(id=1, name='one'), C(id=2, name='two'), D(E='e')]
    

    【讨论】:

      【解决方案4】:

      我终于设法通过删除dataClass 定义并扩展类定义old school....代码如下...

      import jsons
      
      class D:
           def __init__(self, E = ""):
               self.E = E
      class C:
          def __init__(self, id = 0, name=""):
              self.id = id
              self.name = name
      class test:
          def __init__(self, A = "", B = 0, C = C(), D = D()):
              self.A = A
              self.B = B
              self.C = C
              self.D = D
      
      jsonString = {"A":"a","B":1,"C":[{"id":1,"name":"one"},{"id":2,"name":"two"}],"D":[{"E":"e"}]}
      instance = jsons.load(jsonString, test)
      

      它现在可以工作,但不如dataClass 干净。如果有人能指出如何使用 dataClass 定义构建原始帖子,则不胜感激。

      【讨论】:

        猜你喜欢
        • 2022-01-20
        • 2020-06-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-03
        • 1970-01-01
        相关资源
        最近更新 更多