How to materialize attrs data class from JSON schema defined by JSL document

心不动则不痛 提交于 2020-01-15 09:30:12

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!