Python/Pydantic -使用json对象的列表

c0vxltue  于 5个月前  发布在  Python
关注(0)|答案(4)|浏览(77)

我有一个使用pydantic接收json数据集的工作模型。模型数据集如下所示:

data = {'thing_number': 123, 
        'thing_description': 'duck',
        'thing_amount': 4.56}

字符串
我想做的是将json文件列表作为数据集,并能够对其进行验证。最终,该列表将转换为pandas中的记录,以供进一步处理。我的目标是验证一个任意长的json条目列表,如下所示:

bigger_data = [{'thing_number': 123, 
                'thing_description': 'duck',
                'thing_amount': 4.56}, 
               {'thing_number': 456, 
                'thing_description': 'cow',
                'thing_amount': 7.89}]


我现在的基本设置如下。注意,添加class ItemList是尝试让任意长度工作的一部分。

from typing import List
from pydantic import BaseModel
from pydantic.schema import schema
import json

class Item(BaseModel):
    thing_number: int
    thing_description: str
    thing_amount: float

class ItemList(BaseModel):
    each_item: List[Item]


然后,基本代码将在一个数组对象中生成我认为我要查找的内容,该数组对象将接受Item对象。

item_schema = schema([ItemList])
print(json.dumps(item_schema, indent=2)) 

    {
      "definitions": {
        "Item": {
          "title": "Item",
          "type": "object",
          "properties": {
            "thing_number": {
              "title": "Thing_Number",
              "type": "integer"
            },
            "thing_description": {
              "title": "Thing_Description",
              "type": "string"
            },
            "thing_amount": {
              "title": "Thing_Amount",
              "type": "number"
            }
          },
          "required": [
            "thing_number",
            "thing_description",
            "thing_amount"
          ]
        },
        "ItemList": {
          "title": "ItemList",
          "type": "object",
          "properties": {
            "each_item": {
              "title": "Each_Item",
              "type": "array",
              "items": {
                "$ref": "#/definitions/Item"
              }
            }
          },
          "required": [
            "each_item"
          ]
        }
      }
    }


安装程序在传递的单个json项上工作:

item = Item(**data)                                                      

print(item)

Item thing_number=123 thing_description='duck' thing_amount=4.56


但是,当我尝试将单个项传递到ItemList模型时,它返回一个错误:

item_list = ItemList(**data)

---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-94-48efd56e7b6c> in <module>
----> 1 item_list = ItemList(**data)

/opt/conda/lib/python3.7/site-packages/pydantic/main.cpython-37m-x86_64-linux-gnu.so in pydantic.main.BaseModel.__init__()

/opt/conda/lib/python3.7/site-packages/pydantic/main.cpython-37m-x86_64-linux-gnu.so in pydantic.main.validate_model()

ValidationError: 1 validation error for ItemList
each_item
  field required (type=value_error.missing)


我也试着将bigger_data传入数组,认为它需要以列表的形式开始。这也会返回一个错误- -尽管如此,我至少对字典错误有了更好的理解,但我不知道如何解决。

item_list2 = ItemList(**data_big)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-100-8fe9a5414bd6> in <module>
----> 1 item_list2 = ItemList(**data_big)

TypeError: MetaModel object argument after ** must be a mapping, not list


谢谢.
"我尝试过的其他事情“
我试着将数据传递到特定的键中,但运气稍好一些(也许?)

item_list2 = ItemList(each_item=data_big)

---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-111-07e5c12bf8b4> in <module>
----> 1 item_list2 = ItemList(each_item=data_big)

/opt/conda/lib/python3.7/site-packages/pydantic/main.cpython-37m-x86_64-linux-gnu.so in pydantic.main.BaseModel.__init__()

/opt/conda/lib/python3.7/site-packages/pydantic/main.cpython-37m-x86_64-linux-gnu.so in pydantic.main.validate_model()

ValidationError: 6 validation errors for ItemList
each_item -> 0 -> thing_number
  field required (type=value_error.missing)
each_item -> 0 -> thing_description
  field required (type=value_error.missing)
each_item -> 0 -> thing_amount
  field required (type=value_error.missing)
each_item -> 1 -> thing_number
  field required (type=value_error.missing)
each_item -> 1 -> thing_description
  field required (type=value_error.missing)
each_item -> 1 -> thing_amount
  field required (type=value_error.missing)

dbf7pr2w

dbf7pr2w1#

为了避免在ItemList中包含"each_item",可以使用__root__ Pydantic关键字:

from typing import List
from pydantic import BaseModel

class Item(BaseModel):
    thing_number: int
    thing_description: str
    thing_amount: float

class ItemList(BaseModel):
    __root__: List[Item]    # ⯇-- __root__

字符串
构建item_list

just_data = [
    {"thing_number": 123, "thing_description": "duck", "thing_amount": 4.56},
    {"thing_number": 456, "thing_description": "cow", "thing_amount": 7.89},
]
item_list = ItemList(__root__=just_data)

a_json_duck = {"thing_number": 123, "thing_description": "duck", "thing_amount": 4.56}
item_list.__root__.append(a_json_duck)


支持Pydantic的Web框架通常将ItemList作为JSON数组进行jsonify,而不使用中间的__root__关键字。

ppcbkaq5

ppcbkaq52#

下面的代码也可以工作,并且不需要根类型。
List[dict]转换为List[Item]

items = parse_obj_as(List[Item], bigger_data)

字符串
从JSON str转换为List[Item]

items = parse_raw_as(List[Item], bigger_data_json)


List[Item]转换为JSON str

from pydantic.json import pydantic_encoder

bigger_data_json = json.dumps(items, default=pydantic_encoder)


或者使用自定义编码器:

from pydantic.json import pydantic_encoder

def custom_encoder(**kwargs):
    def base_encoder(obj):
        if isinstance(obj, BaseModel):
            return obj.dict(**kwargs)
        else:
            return pydantic_encoder(obj)
    return base_encoder

bigger_data_json = json.dumps(items, default=custom_encoder(by_alias=True))

x7rlezfr

x7rlezfr3#

from typing import List
from pydantic import BaseModel
import json

class Item(BaseModel):
    thing_number: int
    thing_description: str
    thing_amount: float

class ItemList(BaseModel):
    each_item: List[Item]

字符串
基于您的代码,将each_item作为项目列表

a_duck = Item(thing_number=123, thing_description="duck", thing_amount=4.56)
print(a_duck.json())

a_list = ItemList(each_item=[a_duck])

print(a_list.json())


生成以下输出:

{"thing_number": 123, "thing_description": "duck", "thing_amount": 4.56}
{"each_item": [{"thing_number": 123, "thing_description": "duck", "thing_amount": 4.56}]}


使用这些作为“entry json”:

a_json_duck = {"thing_number": 123, "thing_description": "duck", "thing_amount": 4.56}
a_json_list = {
    "each_item": [
        {"thing_number": 123, "thing_description": "duck", "thing_amount": 4.56}
    ]
}

print(Item(**a_json_duck))
print(ItemList(**a_json_list))


工作很好,并生成:

Item thing_number=123 thing_description='duck' thing_amount=4.56
ItemList each_item=[<Item thing_number=123 thing_description='duck' thing_amount=4.56>]


我们只剩下唯一的数据:

just_datas = [
    {"thing_number": 123, "thing_description": "duck", "thing_amount": 4.56},
    {"thing_number": 456, "thing_description": "cow", "thing_amount": 7.89},
]
item_list = ItemList(each_item=just_datas)
print(item_list)
print(type(item_list.each_item[1]))
print(item_list.each_item[1])


这些工作如预期:

ItemList each_item=[<Item thing_number=123 thing_description='duck'thing_amount=4.56>,<Item thin…
<class '__main__.Item'>
Item thing_number=456 thing_description='cow' thing_amount=7.89


因此,如果我错过了一些东西,pydantic librairy工程作为预期。
我的pydantic版本:0.30 python 3.7.4
阅读类似文件:

json_data_file = """[
{"thing_number": 123, "thing_description": "duck", "thing_amount": 4.56},
{"thing_number": 456, "thing_description": "cow", "thing_amount": 7.89}]"""

from io import StringIO
item_list2 = ItemList(each_item=json.load(StringIO(json_data_file)))


工作也很好。

qybjjes1

qybjjes14#

对我来说,诀窍是fastapi.encoders.jsonable_encoder(看看https://fastapi.tiangolo.com/tutorial/encoder/
因此,在您的情况下,我已经将“单个”项目附加到列表result中,即result.append(Item(thing_number=123, thing_description="duck", thing_amount=4.56))
最后是fastapi.JSONResponse(content=fastapi.encoders.jsonable_encoder(result))

相关问题