问题
Assuming you using Python JSL library for defining JSON schema of your data and you using attrs library for quick definition of your DTO.
How can you easily validate data structure against its JSON schema definition (as jsl.Document
class) and reify it into attrs instance conforming to its JSL definition without extra boilerplate?
Because creating JSL documents and duplicate their definitions just to have a corresponding attrs DTO doesn’t feel to be the right way.
回答1:
Define a function to consume JSL type for validation with actual data and once dictionary validates against schema collect JSL document attributes and use make_class method to produce attrs instance dynamically.
Here is proof of concept for Python 3.6.1:
from typing import TypeVar, Type
import attr
import jsl
import jsonschema
class Demo(jsl.Document):
"""
Demo schema
"""
ip = jsl.IPv4Field(required=True)
"""IPv4 address string"""
headers = jsl.DictField(required=True,
min_properties=1,
additional_properties=jsl.StringField())
"""Dictionary of HTTP headers"""
email = jsl.EmailField()
"""Optional User email"""
T = TypeVar('T') # Nice hack using generic type hinting. It preserves auto-completion
def reify(schema: Type[T], data: dict) -> T:
"""
Consumes JSON schema (as jsl.Document object) with dictionary data for validation
and if data is valid then “attrs” instance is produced having same structure
as schema object with populated data.
:param T schema: Schema type (as jsl.Document type.)
:param dict data: Data dictionary for validation against **schema**.
:return: Schema transformed into equivalent **attrs** instance with populated
**data**.
:rtype: T
:raises: ValueError — When **data** does not conforms with **schema**.
"""
try:
jsonschema.validate(data, schema.get_schema())
props = [name for name, _ in schema.get_schema()['properties'].items()]
fields = {key: attr.ib(default=None) for key in props}
# noinspection PyTypeChecker
return attr.make_class(schema.__name__, fields)(**data)
except jsonschema.ValidationError as e:
raise ValueError(f'Payload does not conform to JSON schema: {e.message}')
demo = reify(Demo, {'ip': '1.2.3.4', 'headers': {'Accept': '*/*'}})
print(demo)
print(f"{demo.ip} Headers: {demo.headers} Email {demo.email}")
# Prints:
# Demo(ip='1.2.3.4', headers={'Accept': '*/*'}, email=None)
# 1.2.3.4 Headers: {'Accept': '*/*'} Email None
As a nice bonus, PyCharm will infer correct auto-completion preserving documentation from JSL document class.
来源:https://stackoverflow.com/questions/45770755/how-to-materialize-attrs-data-class-from-json-schema-defined-by-jsl-document