Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Data Classes Generation (A.K.A POPO/DTO) #33

Open
slavaatsig opened this issue Aug 13, 2017 · 1 comment
Open

Support Data Classes Generation (A.K.A POPO/DTO) #33

slavaatsig opened this issue Aug 13, 2017 · 1 comment

Comments

@slavaatsig
Copy link

Defining JSON schema in JSL is very similar to defining your data class (DTO/POPO) using the attrs library but JSL only gives you JSON schema for validation.

Once payload was validated using JSL object it would make sense to work with data using data class rather than a big dictionary.

It would be super awesome to marry JSL with Attrs and have an ability not only having JSON schema generation but also a data class using the attrs library.

@slavaatsig
Copy link
Author

Here is a nice POC for Python 3.6.2 where you can reify schema into attrs class having the same structure.
Nice bonus using generic type hinting is that you get correct auto-completion and preserved documentation from the original schema definition.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant