jsonmatch
is a small library for matching Python JSON dictionaries against a
specification in a flexible, informative way. It was created to make testing
API responses simple, quick, and easy.
>>> import jsonmatch, re
>>> m = jsonmatch.compile({'a': int, 'b': re.compile(r'\d+'), 'c': 'c'})
>>> m.matches({'a': 1, 'b': '321', 'c': 'c'})
True
>>> m.matches({'a': 1, 'b': 'not a digit', 'c': 'c'})
False
>>> m.assert_matches({'a': 1, 'b': 'not a digit', 'c': 'c'})
Expected:
{'a': <type 'int'>, 'b': <_sre.SRE_Pattern object at 0x7f70340d5160>, 'c': 'c'}
Got:
{'a': 1, 'b': 'not a digit', 'c': 'c'}
Diffs:
{('b',): (RegexpMatch(r'\d+'), 'not a digit')}
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
...
>>> m.breaks({'a': 1, 'b': 'not a digit', 'c': 'c'}).paths_to_breaks
{('b',): (RegexpMatch(r'\d+'), 'not a digit')}
jsonmatch is on pypi
pip install jsonmatch
- Flexible matching based on
- type
- regexp
- callable
- or plain ol' object.
- Return unmet expectations in a useful datastructure, not just
a string.
{('path', 'to', 'diff'): (expected_val, actual_val), ...}
- Optionally ignore ordering in lists.
There are some other great solutions out there for doing data validation in Python, including
These libraries are much more robust than jsonmatch, but they're also significantly more complex.
>>> import jsonmatch
>>> import re
>>> matcher = jsonmatch.compile({
'a': 123,
'b': {
'c': re.compile('[abc][ABC]{3}'), # we can use regexp
'd': [1, 2, 3]},
'e': lambda x: len(x) == 3, # and callables
'f': list, # and also types
})
# candidates that match the specification yield no breaks
>>> None == matcher.breaks({
'a': 123,
'b': {
'c': 'aABC',
'd': [1, 2, 3],
},
'e': 'one',
'f': [],
})
True
# on the other hand, mismatches yield Breaks objects (scalar breakage in 'a')
>>> print matcher.breaks({
'a': 1234,
'b': {
'c': 'aABC',
'd': [1, 2, 3],
},
'e': 'one',
'f': [],
}).breaks_str
"""
Expected:
{'a': 123,
'b': {'c': <_sre.SRE_Pattern object at 0x2c8ed78>, 'd': [1, 2, 3]},
'e': <function <lambda> at 0x2e8c1b8>,
'f': []}
Got:
{'a': 1234, 'b': {'c': 'aABCeee', 'd': [1, 2, 3]}, 'e': 'one', 'f': []}
Diffs:
{('a',): (123, 1234)}
"""
# regexp breakage in 'b.c'
>>> print matcher.breaks({
'a': 123,
'b': {
'c': "doesn't match",
'd': [1, 2, 3],
},
'e': 'one',
'f': [],
}).breaks_str
"""
Expected:
{'a': 123,
'b': {'c': <_sre.SRE_Pattern object at 0x2c8ed78>, 'd': [1, 2, 3]},
'e': <function <lambda> at 0x2e8c1b8>,
'f': []}
Got:
{'a': 123, 'b': {'c': "doesn't match", 'd': [1, 2, 3]}, 'e': 'one', 'f': []}
Diffs:
{('b', 'c'): (RegexpMatch('[abc][ABC]{3}'), "doesn't match")}
"""
from __future__ import print_function, unicode_literals
>>> import jsonmatch
>>> msg = '\U0001f600'
>>> print(msg)
😀
# In PY2, the `str` type will match both unicode and byte string
>>> matcher = jsonmatch.compile({'message': str})
>>> print(matcher.matches({'message': b'bytestring'}))
True
>>> print(matcher.matches({'message': msg}))
True
# In PY3, `str` will ONLY match unicode string
>>> matcher = jsonmatch.compile({'message': str})
>>> print(matcher.matches({'message': b'bytestring'}))
False
>>> print(matcher.matches({'message': msg}))
True
# In order to match byte string, we must compile as `bytes`
>>> matcher = jsonmatch.compile({'message': bytes})
>>> print(matcher.matches({'message': b'bytestring'}))
True
>>> print(matcher.matches({'message': msg}))
False