XSDATA生成的Python模型无法解析单条Book数据,多条正常
问题
使用XSDATA工具结合JSON生成Python数据类模型时,出现解析异常:
- 当
books节点下包含多个book对象时,模型可正常将其解析为List[Book]类型; - 当
books节点下仅存在单个book对象时,模型的book列表为空,无法读取该条数据。
相关代码示例
sample.xml
<?xml version="1.0"?> <catalog> <books> <book id="bk101"> <author>Gambardella, Matthew</author> <title>XML Developer's Guide</title> <genre>Computer</genre> <price>44.95</price> <publish_date>2000-10-01</publish_date> <description>An in-depth look at creating applications with XML.</description> </book> <book id="bk101"> <author>Gambardella, Matthew</author> <title>XML Developer's Guide</title> <genre>Computer</genre> <price>44.95</price> <publish_date>2000-10-01</publish_date> <description>An in-depth look at creating applications with XML.</description> </book> </books> </catalog>
多book的sample.json
{ "catalog": { "books": { "book": [ { "@id": "bk101", "author": "Gambardella, Matthew", "title": "XML Developer's Guide", "genre": "Computer", "price": "44.95", "publish_date": "2000-10-01", "description": "An in-depth look at creating applications \n with XML." }, { "@id": "bk101", "author": "Gambardella, Matthew", "title": "XML Developer's Guide", "genre": "Computer", "price": "44.95", "publish_date": "2000-10-01", "description": "An in-depth look at creating applications \n with XML." } ] } } }
单book的sample.json
{ "catalog": { "books": { "book": { "@id": "bk101", "author": "Gambardella, Matthew", "title": "XML Developer's Guide", "genre": "Computer", "price": "44.95", "publish_date": "2000-10-01", "description": "An in-depth look at creating applications \n with XML." } } } }
models.py
from dataclasses import dataclass, field from typing import List, Optional from xsdata.models.datatype import XmlDate @dataclass class Book: class Meta: name = "book" id: Optional[str] = field( default=None, metadata={ "name": "@id", "type": "Element", } ) author: Optional[str] = field( default=None, metadata={ "type": "Element", } ) title: Optional[str] = field( default=None, metadata={ "type": "Element", } ) genre: Optional[str] = field( default=None, metadata={ "type": "Element", } ) price: Optional[float] = field( default=None, metadata={ "type": "Element", } ) publish_date: Optional[XmlDate] = field( default=None, metadata={ "type": "Element", } ) description: Optional[str] = field( default=None, metadata={ "type": "Element", } ) @dataclass class Books: class Meta: name = "books" book: List[Book] = field( default_factory=list, metadata={ "type": "Element", } ) @dataclass class Catalog: class Meta: name = "catalog" books: Optional[Books] = field( default=None, metadata={ "type": "Element", } ) @dataclass class Models: class Meta: name = "models" catalog: Optional[Catalog] = field( default=None, metadata={ "type": "Element", } )
model_con.py
import logging import sys import json from xsdata.formats.dataclass.context import XmlContext from xsdata.formats.dataclass.parsers import JsonParser from xsdata.formats.dataclass.parsers.config import ParserConfig from models.models import Models from xml_to_json import XmlToJson class Utilities: @staticmethod def json_to_model(metadata_json: str) -> Models: logger = logging.getLogger(__name__) logger.info('%s: Converting JSON to model', sys._getframe().f_code.co_name) context: XmlContext = XmlContext() config = ParserConfig(fail_on_unknown_attributes=False, fail_on_unknown_properties=False) parser: JsonParser = JsonParser(context=context, config=config) extract_model: Models = parser.from_string(metadata_json, Models) return extract_model if __name__ == "__main__": extract_meta_path = r"C:\XSDATA\sample.xml" xtj = XmlToJson() xtj.xml_file_name = extract_meta_path # print(xtj.json_data) ut = Utilities() md = ut.json_to_model(metadata_json=xtj.json_data) print(md)
原因分析
XSDATA的JSON解析器默认严格区分JSON中的数组([])和单个对象({})。Books类中book字段定义为List[Book],当JSON里book是单个对象而非数组时,解析器无法将单个对象匹配到列表类型的字段,因此会忽略该值,保留字段默认的空列表。
解决方案
在Books类的book字段的metadata中添加"occurs": "unbounded"配置,告知XSDATA该字段既支持单个对象,也支持数组,解析时会自动将单个对象转换为包含该对象的列表。修改后的Books类代码如下:
@dataclass class Books: class Meta: name = "books" book: List[Book] = field( default_factory=list, metadata={ "type": "Element", "occurs": "unbounded" # 添加这一行配置 } )
修改后,无论是单个book对象还是多个book组成的数组,都能被正确解析为List[Book]类型,不会出现空列表的情况。
内容的提问来源于stack exchange,提问作者Ayush Singh
相关产品推荐
相关产品推荐

