使用Pydantic解析JSON数据时遭遇float类型验证错误求助
解决Pydantic解析带千分位的价格字符串为float的问题
问题重现
Python代码
import json import pydantic from typing import Optional, List class Car(pydantic.BaseModel): manufacturer: str model: str date_of_manufacture: str date_of_sale: str number_plate: str price: float type_of_fuel: Optional[str] location_of_sale: Optional[str] def load_data() -> None: with open("./data.json") as file: data = json.load(file) cars: List[Car] = [Car(**item) for item in data] print(cars[0]) if __name__ == "__main__": load_data()
JSON数据
[ { "manufacturer": "BMW", "model": "i8", "date_of_manufacture": "14/06/2021", "date_of_sale": "19/11/2022", "number_plate": "ND21WHP", "price": "100,000", "type_of_fuel": "electric", "location_of_sale": "Leicester, England" }, { "manufacturer": "Audi", "model": "TT RS", "date_of_manufacture": "22/02/2019", "date_of_sale": "12/08/2021", "number_plate": "LR69FOW", "price": "67,000", "type_of_fuel": "petrol", "location_of_sale": "Manchester, England" } ]
错误信息
File "pydantic\main.py", line 342, in pydantic.main.BaseModel.__init__ pydantic.error_wrappers.ValidationError: 1 validation error for Car price value is not a valid float (type=type_error.float)
错误原因
JSON中的price字段是带千分位逗号的字符串(如"100,000"),Pydantic默认的float类型解析无法识别这种格式——逗号不是浮点数的合法组成字符,即使在末尾添加.00,只要逗号存在就会解析失败。
解决方法
方法1:使用Pydantic的@validator装饰器处理
在Car类中添加验证器,自动清理价格字符串中的逗号并转换为float:
import json from pydantic import BaseModel, validator from typing import Optional, List class Car(BaseModel): manufacturer: str model: str date_of_manufacture: str date_of_sale: str number_plate: str price: float type_of_fuel: Optional[str] location_of_sale: Optional[str] @validator('price', pre=True) def parse_price(cls, value): # 如果是字符串,去掉逗号后转float if isinstance(value, str): return float(value.replace(',', '')) return value def load_data() -> None: with open("./data.json") as file: data = json.load(file) cars: List[Car] = [Car(**item) for item in data] print(cars[0]) if __name__ == "__main__": load_data()
pre=True表示这个验证器在Pydantic默认类型检查之前执行,先把处理后的数值传给字段解析。
方法2:解析JSON后预处理数据
在加载JSON后,先遍历每个条目处理price字段,再传给Pydantic:
import json from pydantic import BaseModel from typing import Optional, List class Car(BaseModel): manufacturer: str model: str date_of_manufacture: str date_of_sale: str number_plate: str price: float type_of_fuel: Optional[str] location_of_sale: Optional[str] def load_data() -> None: with open("./data.json") as file: data = json.load(file) # 预处理每个条目,清理price字段 for item in data: if isinstance(item['price'], str): item['price'] = float(item['price'].replace(',', '')) cars: List[Car] = [Car(**item) for item in data] print(cars[0]) if __name__ == "__main__": load_data()
方法3:自定义Pydantic字段类型
如果需要在多个模型中复用这种价格解析逻辑,可以自定义字段类型:
import json from pydantic import BaseModel from typing import Optional, List, Any from pydantic.fields import ModelField class Price(float): @classmethod def __get_validators__(cls): yield cls.validate @classmethod def validate(cls, v: Any, field: ModelField, config): if isinstance(v, str): return cls(v.replace(',', '')) return cls(v) class Car(BaseModel): manufacturer: str model: str date_of_manufacture: str date_of_sale: str number_plate: str price: Price type_of_fuel: Optional[str] location_of_sale: Optional[str] def load_data() -> None: with open("./data.json") as file: data = json.load(file) cars: List[Car] = [Car(**item) for item in data] print(cars[0]) if __name__ == "__main__": load_data()
内容的提问来源于stack exchange,提问作者TaranJS
相关产品推荐
相关产品推荐

