jsonweb.schema

Declarative

jsonweb.schema provides a layer of validation before json.decode returns your object instances. It can also simply be used to validate the resulting python data structures returned from json.loads(). It’s main use is through a declarative style api. Here is an example of validating the structure of a python dict:

>>>
>>> from jsonweb.schema import ObjectSchema, ValidationError
>>> from jsonweb.validators import String

>>> class PersonSchema(ObjectSchema):
...     first_name = String()
...     last_name = String()

>>> try:
...     PersonSchema().validate({"first_name": "shawn"})
... except ValidationError, e:
...     print e.errors
{"last_name": "Missing required parameter."}

Validating plain old python data structures is fine, but the more interesting exercise is tying a schema to a class definition:

>>> from jsonweb.decode import from_object, loader
>>> from jsonweb.schema import ObjectSchema, ValidationError
>>> from jsonweb.validators import String, Integer, EnsureType

>>> class PersonSchema(ObjectSchema):
...    id = Integer()
...    first_name = String()
...    last_name = String()
...    gender = String(optional=True)
...    job = EnsureType("Job")

You can make any field optional by setting optional to True.

Warning

The field is only optional at the schema level. If you’ve bound a schema to a class via from_object() and the underlying class requires that field a ObjectAttributeError will be raised if missing.

As you can see its fine to pass a class name as a string, which we have done for the Job class above. We must later define Job and decorate it with from_object()

>>> class JobSchema(ObjectSchema):
...    id = Integer()
...    title = String()

>>> @from_object(schema=JobSchema)
... class Job(object):
...     def __init__(self, id, title):
...         self.id = id
...         self.title = title

>>> @from_object(schema=PersonSchema)
... class Person(object):
...     def __init__(self, first_name, last_name, job, gender=None):
...         self.first_name = first_name
...         self.last_name = last_name
...         self.gender = gender
...         self.job = job
...     def __str__(self):
...         return '<Person name="%s" job="{0}">'.format(
...             " ".join((self.first_name, self.last_name)),
...             self.job.title
...         )

>>> person_json = '''
...     {
...         "__type__": "Person",
...         "id": 1,
...         "first_name": "Bob",
...         "last_name": "Smith",
...         "job": {"__type__": "Job", "id": 5, "title": "Police Officer"},
...     }'''
...

>>> person = loader(person_json)
>>> print person
<Person name="Bob" job="Police Officer">

Non-Declarative

New in version 0.8.1.

Use the staticmethod ObjectSchema.create() to build object schemas in a non declarative style. Handy for validating dicts with string keys that are not valid python identifiers (e.g “first-name”):

MySchema = ObjectSchema.create("MySchema", {
    "first-name": String(),
    "last-name": String(optional=True)
})

jsonweb.validators

class jsonweb.validators.BaseValidator(optional=False, nullable=False, default=None, reason_code=None)

Abstract base validator which all JsonWeb validators should inherit from. Out of the box JsonWeb comes with a dozen or so validators.

All validators will override BaseValidator._validate() method which should accept an object and return the passed in object or raise a ValidationError if validation failed.

Note

You are not required to return the exact passed in object. You may for instance want to transform the object in some way. This is exactly what DateTime does.

__init__(optional=False, nullable=False, default=None, reason_code=None)

All validators that inherit from BaseValidator should pass optional, nullable and default as explicit kw arguments or **kw.

Parameters:
  • optional – Is the item optional?
  • nullable – Can the item’s value can be None?
  • default – A default value for this item.
  • reason_code – Failure reason_code that is passed to any ValidationError raised from this instance.
class jsonweb.validators.String(min_len=None, max_len=None, **kw)

Validates something is a string

>>> String().validate("foo")
... 'foo'
>>> String().validate(1)
Traceback (most recent call last):
    ...
ValidationError: Expected str got int instead.

Specify a maximum length

>>> String(max_len=3).validate("foobar")
Traceback (most recent call last):
...
ValidationError: String exceeds max length of 3.

Specify a minimum length

>>> String(min_len=3).validate("fo")
Traceback (most recent call last):
...
ValidationError: String must be at least length 3.
class jsonweb.validators.Regex(regex, **kw)

New in version 0.6.3: Validates a string against a regular expression ::

>>> Regex(r"^foo").validate("barfoo")
Traceback (most recent call last):
...
ValidationError: String does not match pattern '^foo'.
class jsonweb.validators.Number(**kw)

Validates something is a number

>>> Number().validate(1)
... 1
>>> Number().validate(1.1)
>>> 1.1
>>> Number().validate("foo")
Traceback (most recent call last):
    ...
ValidationError: Expected number got int instead.
class jsonweb.validators.Integer(**kw)

Validates something in an integer

class jsonweb.validators.Float(**kw)

Validates something is a float

class jsonweb.validators.Boolean(**kw)

Validates something is a Boolean (True/False)

class jsonweb.validators.DateTime(format='%Y-%m-%d %H:%M:%S', **kw)

Validates that something is a valid date/datetime string and turns it into a datetime.datetime instance

>>> DateTime().validate("2010-01-02 12:30:00")
... datetime.datetime(2010, 1, 2, 12, 30)

>>> DateTime().validate("2010-01-02 12:300")
Traceback (most recent call last):
    ...
ValidationError: time data '2010-01-02 12:300' does not match format '%Y-%m-%d %H:%M:%S'

The default datetime format is %Y-%m-%d %H:%M:%S. You can specify your own

>>> DateTime("%m/%d/%Y").validate("01/02/2010")
... datetime.datetime(2010, 1, 2, 0, 0)
class jsonweb.validators.EnsureType(_type, type_name=None, **kw)

Validates something is a certain type

>>> class Person(object):
...     pass
>>> EnsureType(Person).validate(Person())
... <Person>
>>> EnsureType(Person).validate(10)
Traceback (most recent call last):
    ...
ValidationError: Expected Person got int instead.
class jsonweb.validators.List(validator, **kw)

Validates a list of things. The List constructor accepts a validator and each item in the list will be validated against it

>>> List(Integer).validate([1,2,3,4])
... [1,2,3,4]

>>> List(Integer).validate(10)
Traceback (most recent call last):
    ...
ValidationError: Expected list got int instead.

Since ObjectSchema is also a validator we can do this

>>> class PersonSchema(ObjectSchema):
...     first_name = String()
...     last_name = String()
...
>>> List(PersonSchema).validate([
...     {"first_name": "bob", "last_name": "smith"},
...     {"first_name": "jane", "last_name": "smith"}
... ])
class jsonweb.validators.Dict(validator, key_validator=None, **kw)

New in version 0.8.

Validates a dict of things. The Dict constructor accepts a validator and each value in the dict will be validated against it

>>> Dict(Number).validate({'foo': 1})
... {'foo': 1}

>>> Dict(Number).validate({'foo': "abc"})
Traceback (most recent call last):
    ...
ValidationError: Error validating dict.

In order see what part of the dict failed validation we must dig deeper into the exception:

>>> str(e.errors["foo"])
... 'Expected number got str instead.'

Dict also accepts an optional key_validator, which must be a subclass of String:

validator = Dict(Number, key_validator=Regex(r'^[a-z]{2}_[A-Z]{2}$'))
try:
    validator.validate({"en-US": 1})
except ValidationError as e:
     print(e.errors["en-US"])
     print(e.errors["en-US"].reason_code)

# output
# String does not match pattern '^[a-z]{2}_[A-Z]{2}$'.
# invalid_dict_key
class jsonweb.validators.OneOf(*values, **kw)

New in version 0.6.4: Validates something is a one of a list of allowed values::

>>> OneOf("a", "b", "c").validate(1)
Traceback (most recent call last):
    ...
ValidationError: Expected one of (a, b, c) got 1 instead.
class jsonweb.validators.SubSetOf(super_set, **kw)

New in version 0.6.4: Validates a list is subset of another list::

>>> SubSetOf([1, 2, 3, 4]).validate([1, 4])
... [1, 4]
>>> SubSetOf([1, 2, 3, 4]).validate([1,5])
Traceback (most recent call last):
    ...
ValidationError: [1, 5] is not a subset of [1, 2, 3, 4].

ValidationErrors

class jsonweb.validators.ValidationError(reason, reason_code=None, errors=None, **extras)

Raised from JsonWeb validators when validation of an object fails.

__init__(reason, reason_code=None, errors=None, **extras)
Parameters:
  • reason – A nice message describing what was not valid
  • reason_code – programmatic friendly reason code
  • errors – A list or dict of nested ValidationError
  • extras – Any extra info about the error you want to convey