【问题标题】:How to validate 2 nested json using Pydantic model in python?如何在 python 中使用 Pydantic 模型验证 2 个嵌套的 json?
【发布时间】:2022-01-02 07:28:26
【问题描述】:
test1 = {
  "abx": [
    {
      "name": "toyota",
      "colors": "abc",
      "demo": [
        {
          "name1": "pqr",
          "surname": "abc",
          "columns": [
            {
              "name2": "demo",
              "nameid": "1"
            }
          ]
        }
      ]
    },
    {
      "name": "suzuki",
      "colors": "deq",
      "demo": [
        {
          "name1": "abc",
          "surname": "asd",
          "columns": [
            {
              "name2": "demo1",
              "nameid": "2"
            }
          ]
        }
      ]
    }
  ]
}

我有 2 个嵌套的 JSON,并且两个响应都有随机播放值。我想匹配使用 Pydantic 模型在 Python 中进行测试。

有什么方法可以比较、验证两个响应吗?

【问题讨论】:

  • 您需要验证什么?需要搭配什么?定义 pydantic 类后的预期行为是什么?嵌套的 JSON 只是转换为嵌套的 python 类......但不清楚你期望这些类做什么。

标签: python pydantic


【解决方案1】:

嵌套的 JSON 可以简单地由嵌套的 Pydantic 模型表示。每个对象都可以映射到一个模型,并且该模型可以具有其他 Pydantic 模型或 Pydantic 模型列表的属性。

你从最里面的对象开始:

{
    "name2": "demo1",
    "nameid": "2",
}

可以表示为:

from pydantic import BaseModel, constr

class Column(BaseModel):
    name2: str
    nameid: constr(regex="[0-9]+")  # See: https://pydantic-docs.helpmanual.io/usage/types/#arguments-to-constr
In [2]: raw = {"name2": "demo1", "nameid": "2"}
In [3]: col = Column(**raw)
In [4]: col.name2
Out[4]: 'demo1'
In [5]: col.nameid
Out[5]: '2'

如果其中一个值错误,Pydantic 会引发验证错误:

In [6]: raw = {"name2": 1, "nameid": True}
In [7]: col = Column(**raw)
...
ValidationError: 1 validation error for Column
nameid
  string does not match regex "[0-9]+" (type=value_error.str.regex; pattern=[0-9]+)

然后你向上移动到每个Column 的父对象:

{
    "name1": "abc",
    "surname": "asd",
    "columns": [
        {"name2": "demo1", "nameid": "2"},
    ],
}

然后可以表示为:

from pydantic import BaseModel, conlist

class Demo(BaseModel):
    name1: str
    surname: str
    columns: conlist(Column, min_items=1)  # See: https://pydantic-docs.helpmanual.io/usage/types/#arguments-to-conlist

我们重用之前的Column 类定义,即columns 属性是Column 的列表。 Pydantic可以实例化和验证DemoColumn的各个属性:

In [8]: raw
Out[8]: {'name1': 'abc', 'surname': 'asd', 'columns': [{'name2': 'demo1', 'nameid': '2'}]}
In [9]: demo = Demo(**raw)
In [11]: demo.name1
Out[11]: 'abc'
In [13]: demo.surname
Out[13]: 'asd'
In [14]: demo.columns
Out[14]: [Column(name2='demo1', nameid='2')]
In [15]: demo.columns[0].name2
Out[15]: 'demo1'
In [16]: demo.columns[0].nameid
Out[16]: '2'

同样,如果任何值是错误的(无论是来自外部属性还是来自最内部的属性),Pydantic 都会引发验证错误:

In [20]: raw
Out[20]: {'name1': 'abc', 'surname': None, 'columns': [{'name2': 'demo1', 'nameid': '2'}]}
In [21]: demo = Demo(**raw)
...
ValidationError: 1 validation error for Demo
surname
  none is not an allowed value (type=type_error.none.not_allowed)
In [27]: raw
Out[27]: {'name1': 'abc', 'surname': 'asd', 'columns': [{'name2': None, 'nameid': '2'}]}
In [28]: demo = Demo(**raw)
...
ValidationError: 1 validation error for Demo
columns -> 0 -> name2
  none is not an allowed value (type=type_error.none.not_allowed)

然后,您再次向上移动到每个Demo 的父对象

{
    "name": "suzuki",
    "colors": "deq",
    "demo": [
        {
            "name1": "abc",
            "surname": "asd",
            "columns": [
                {"name2": "demo1", "nameid": "2"},
            ],
        }
    ],
}

可以表示为

from pydantic import BaseModel, conlist

class Car(BaseModel):
    name: str
    colors: str
    demo: conlist(Demo, min_items=1)

与我们对Column 所做的相同,我们有一个demo 属性,它被建模为Demo 对象的列表。 Pydantic 可以处理 demo 的嵌套初始化和验证,其中包括其内部 Column 类。

最后,你会到达最上面的物体:

test1 = {
    "abx": [
        {...},
        {...},
    ]
}

它有一个abx 属性,它是Car 对象的列表

from pydantic import BaseModel, conlist

class Test(BaseModel):
    abx: conlist(Car, min_items=1)
In [3]: obj = Test(**test1)
In [4]: obj
Out[4]: Test(abx=[Car(name='toyota', colors='abc', demo=[Demo(name1='pqr', surname='abc', columns=[Column(name2='demo', nameid='1')])]), Car(name='suzuki', colors='deq', demo=[Demo(name1='abc', surname='asd', columns=[Column(name2='demo1', nameid='2')])])])

In [5]: obj.abx[1]
Out[5]: Car(name='suzuki', colors='deq', demo=[Demo(name1='abc', surname='asd', columns=[Column(name2='demo1', nameid='2')])])

In [6]: obj.abx[1].demo[0]
Out[6]: Demo(name1='abc', surname='asd', columns=[Column(name2='demo1', nameid='2')])

您还可以在任何地方停止建模,例如,停止建模 Car 并从原始 abx 对象进行验证:

In [15]: car1 = Car(**test1['abx'][0])
In [16]: car1
Out[16]: Car(name='toyota', colors='abc', demo=[Demo(name1='pqr', surname='abc', columns=[Column(name2='demo', nameid='1')])])

In [17]: car2 = Car(**test1['abx'][1])
In [18]: car2
Out[18]: Car(name='suzuki', colors='deq', demo=[Demo(name1='abc', surname='asd', columns=[Column(name2='demo1', nameid='2')])])

同样,如果您有错误,即使在内部 Column 模型的某个地方,您也会收到验证错误。

这样做的好处是您可以依次测试和验证每个对象,从最里面的对象一直到最外面的对象。有关constrconlist 用法的更多详细信息,请查看Constrained Types 的Pydantic 文档以及其他Field TypesValidators 以获得更复杂的验证。

【讨论】:

    猜你喜欢
    • 2021-09-06
    • 1970-01-01
    • 2021-03-04
    • 1970-01-01
    • 2020-08-07
    • 1970-01-01
    • 2022-01-15
    • 2022-07-28
    • 2021-11-10
    相关资源
    最近更新 更多