#python #json #serialization
Вопрос:
Я создаю анализатор, который берет данные из нескольких источников с несколькими схемами данных, а затем преобразует их в стандартизированную структурированную схему.
Например, у меня есть 2 источника данных:
Источник 1:
{
"students: [{
"id": 129939,
"name": "Alice",
"gender": "female",
}]
}
Источник 2:
{
"students: [{
"id": 129939,
"fullname": "Alice",
"sex": "female",
}]
}
Оба источника данных могут быть преобразованы в стандартизированные структурированные данные, которые я уже определил:
class Student:
id: int
name: str
gender: str
Знаете ли вы, существует ли какая-либо существующая библиотека, которая поддерживает определение схемы для каждого источника входных данных, а затем позволяет сопоставлять каждое поле источника входных данных с требуемой структурой данных?
Например, это может быть картограф, подобный этому:
class Input1toStudentMapper:
id -> Student.id
name -> Student.name
gender -> Student.gender
class Input2toStudentMapper:
id -> Student.id
fullname -> Student.name
sex -> Student.gender
Любое предложение будет оценено по достоинству.
Комментарии:
1. Почему бы вам не выбрать одну структуру и не создать для нее синтаксический анализатор, а затем вы можете использовать регулярное выражение для редактирования jsons. например, прочитайте json, используйте re.sub, чтобы заменить «полное имя» на «имя», а затем проанализируйте
Ответ №1:
Я не знаю ни одной библиотеки, но для проблемы, которую вы описываете, вы, вероятно, можете закодировать логику в __init__
самом классе —
class Student:
name_field_variations = ['name', 'fullname']
sex_field_variations = ['gender', 'sex']
def __init__(self, **kwargs):
self.id = kwargs['id']
_name_field = set(kwargs.keys()) amp; set(Student.name_field_variations)
self.name = kwargs[_name_field.pop()]
_sex_field = set(kwargs.keys()) amp; set(Student.sex_field_variations)
self.gender = kwargs[_sex_field.pop()]
print(js1) # {'students': [{'id': 129939, 'name': 'Alice', 'gender': 'female'}]}
print(js2) # {'students': [{'id': 129939, 'fullname': 'Alice', 'sex': 'female'}]}
s1 = Student(**js1['students'][0])
s2 = Student(**js2['students'][0])
print(s1.gender) # female
print(s2.gender) # female
Ответ №2:
Я бы проверил библиотеку мастера классов данных для этого. Он хорошо работает со встроенным модулем dataclasses в Python. Он поддерживает несколько псевдонимов (или сопоставлений ключей) для каждого поля, а также односторонний псевдоним, который нам может понадобиться в этом случае-например, если мы хотим разрешить дополнительное сопоставление fullname
с name
полем, но сериализовать с использованием ключа по умолчанию name
.
from __future__ import annotations
from dataclasses import dataclass
from typing import List
from dataclass_wizard import json_key
from typing_extensions import Annotated
@dataclass
class Container:
students: List[Student]
@dataclass
class Student:
id: int
name: Annotated[str, json_key('fullname')]
gender: Annotated[str, json_key('sex')]
В приведенной выше аннотации объявление его как json_key('fullname')
является сокращением json_key('name', 'fullname', all=True)
, что также позволяет сопоставлять несколько псевдонимов с полем.
Обратите внимание, что если вы планируете поддерживать только Python 3.9 или выше, вы можете внести следующие изменения:
- Вместо этого импортируйте
Annotated
изtyping
модуля - Удалите
from typing import List
импорт и определите аннотацию следующим образомlist[Student]
Вот пример использования для тестирования приведенного выше кода:
if __name__ == '__main__':
from dataclass_wizard import asdict, fromdict, fromlist
source_1 = {
"students": [{
"id": 129939,
"name": "Alice",
"gender": "female",
}]
}
source_2 = {
"students": [{
"id": 129940,
"fullname": "Johnny",
"sex": "male",
}]
}
c1 = fromdict(Container, source_1)
print(c1)
# Container(students=[Student(id=129939, name='Alice', gender='female')])
c2 = fromdict(Container, source_2)
print(c2)
# Container(students=[Student(id=129940, name='Johnny', gender='male')])
# alternatively, if you just need a list of the `Student` instances:
students = fromlist(Student, source_1['students'])
print(students)
# [Student(id=129939, name='Alice', gender='female')]
# assert we get the same data when serializing the Container instance as a
# Python dict object.
serialized_dict = asdict(c1)
assert serialized_dict == source_1