Python基于POJO类实现JSON模式匹配(类Scala)及报错排查
问题原因
你遇到的AttributeError本质是嵌套的字典没有被转换成对应的类实例:
- 只有
Student类使用了@dataclass装饰器,而SubjectDetails、GradeDetails只是普通的注解类,没有实例化逻辑。 - 当用
Student(**data_dict)初始化时,data_dict['subjects']是一个字典,不会自动转换成SubjectDetails对象,所以student_data.subjects实际上是dict类型,自然没有subject_name属性。
最佳实现方案
方案1:基于dataclass手动处理嵌套解析
给所有嵌套类加上@dataclass装饰器,并编写递归解析函数,把字典逐层转换成对应的类实例:
import json from dataclasses import dataclass, asdict @dataclass class GradeDetails: first_term: str second_term: str pass_or_fail: str @dataclass class SubjectDetails: subject_name: str term_score: GradeDetails @dataclass class Student: name: str roll_number: str grade_class: str subjects: SubjectDetails def dict_to_instance(data, cls): """递归将字典转换为dataclass实例""" if hasattr(cls, '__annotations__'): field_types = cls.__annotations__ processed_data = {} for field, type_cls in field_types.items(): value = data.get(field) if isinstance(value, dict): processed_data[field] = dict_to_instance(value, type_cls) else: processed_data[field] = value return cls(**processed_data) return data def json_to_json_string(): json_data = ''' { "name": "John Doe", "roll_number": "123456", "grade_class": "10th Grade", "subjects": { "subject_name": "Math", "term_score": { "first_term": "A", "second_term": "B", "pass_or_fail": "Pass" } } } ''' parsed_data = json.loads(json_data) return parsed_data if __name__ == '__main__': data_dict = json_to_json_string() student_data = dict_to_instance(data_dict, Student) student_subject = student_data.subjects print(f" student data :: \n {student_data} \n") print(f" subject details are :: \n {student_subject.subject_name} \n") # 也可以轻松转回JSON print(json.dumps(asdict(student_data), indent=2))
方案2:使用Pydantic(更推荐,接近Scala的类型安全JSON解析)
Pydantic是专门用于数据验证和JSON序列化/反序列化的库,能自动处理嵌套类的解析、类型校验,完全符合你想要的类似Scala模式匹配的类型安全效果:
首先安装Pydantic:
pip install pydantic
然后修改代码:
import json from pydantic import BaseModel class GradeDetails(BaseModel): first_term: str second_term: str pass_or_fail: str class SubjectDetails(BaseModel): subject_name: str term_score: GradeDetails class Student(BaseModel): name: str roll_number: str grade_class: str subjects: SubjectDetails def json_to_json_string(): json_data = ''' { "name": "John Doe", "roll_number": "123456", "grade_class": "10th Grade", "subjects": { "subject_name": "Math", "term_score": { "first_term": "A", "second_term": "B", "pass_or_fail": "Pass" } } } ''' parsed_data = json.loads(json_data) return parsed_data if __name__ == '__main__': data_dict = json_to_json_string() student_data = Student(**data_dict) student_subject = student_data.subjects print(f" student data :: \n {student_data} \n") print(f" subject details are :: \n {student_subject.subject_name} \n") # 直接转JSON字符串 print(student_data.model_dump_json(indent=2))
Pydantic还支持自动校验字段类型、缺失字段处理、默认值等功能,比手动写dataclass解析更健壮,是Python中实现类型安全JSON解析的首选方案。
内容的提问来源于stack exchange,提问作者Sarkuna
相关产品推荐
相关产品推荐

