serialize-mcr='0.0.1'
Junpu Fan (Original Author, serial-j
= https://github.com/JunpuFan/serial-j/blob/master)
Andrew Ray, rayam@iu.edu (Primary Maintainer)
- serialize-mcr JSON / Python Dictionary data into Python object based on a compact data
schema
.- Data
schema
is a python list[]
of many{}
. - Each
{}
in theschema
defines a property in yourJSON
data. - The easiest form of a property definition is
{'name':'my_property'}
which means:- Your
JSON
data MUST contain a property calledmy_property
. - Its value MUST be a non-empty value.
- Non-empty means that the value of
my_property
can not beNone
,""
,()
,[]
, or{}
.
- Your
- Additional options are available to give you more control over your data definition. Those options are:
nullable
,optional
,is_compound
,compound_serialize-mcrr
,compound_schema
,type
, anddefault
.- Option
nullable: True
means the value ofmy_property
can beNone
, or a nullable equivalent. - Option
optional: True
meansmy_property
may or may not exist in yourJSON
data.- In case
my_property
exist, verify all applicable options. - In case
my_property
doesn't exist, we ignoremy_property
.
- In case
- Option
is_compound: True
meansmy_property
is a nestedJSON
object or an Array ofJSON
objects.- When
is_compound: True
, you must provide eithercompound_serialize-mcrr
orcompound_schema
so we can property serialize-mcr this nested data structure.compound_serialize-mcrr
is aserialize-mcr
serialize-mcrr class.compound_schema
has the same structure as the dataschema
.
- When
- Option
type
gives you the power to validate the value of each property in yourJSON
data. Currently supported type definitions are:'type': (bool,)
a boolean value.'type': (float,)
a floating point number.'type': (int,)
an integer.'type': (int, (1, 64, 343))
an enumeration of integers, this means that the value of aJSON
property should be in(1, 64, 343)
.'type': (int, range(1, 10, 3)
, a range of integers, this means that the value of aJSON
property should be inrange(1, 10, 3)
.'type': (int, lambda x: x % 2 == 0)
a user definedlambda
expression used to filter desired integer values, the above examplelambda
specifies the value of theJSON
property should be aeven
number.'type': (str,)
a string value.'type': (str, ('SUCCESS', 'FAILURE'))
an enumeration of strings, this means that the value of aJSON
property should be in('SUCCESS', 'FAILURE')
. Note that('SUCCESS', 'FAILURE')
is just an example here, you can define anything you like.'type': (str, 'email')
an email address.'type': (str, 'url')
a web url.'type': (str, 'ipv4')
an IPv4 address.'type': (str, 'ipv6')
an IPv6 address.'type': (str, 'uuid')
an UUID string.'type': (str, '[^@]+@[^@]+\.[^@]+')
a user definedregex
.
- Option
default
gives you the power to set a defaulted value for a property in the case where it was not passed in AND the property was optionaldefault
does not work if the property is optional, or in other wordsrequired
default
does a type check against the definedtype
if passed in- if the type of the
default
value does not conform with thetype
defined, then an error will be thrown
- if the type of the
default
defaulted value isNone
, therefore bothTrue
andFalse
are also supported in thedefault
realm of options.
- Option
- Data
- Automatically validate every JSON properties defined in the
schema
based on varies additional options specified inschema
. - You are given convenient built-in methods that you can use to convert your data back to JSON encoded string or JSON / Python Dictionary.
- You have the flexibility of defining additional methods in your serialize-mcrr class that utilize your data in anyway you want.
Name | Code |
---|---|
Basic Example | basic_ex.py |
serialize-mcr Nested Json Data with compound_schema |
nested_ex2.py |
serialize-mcr Nested Json Data with compound_serialize-mcrr |
nested_ex1.py |
Data Type Validation: all in one example | typed_ex.py |
Data Type Validation: bool |
bool_data.py |
Data Type Validation: float |
float_data.py |
Data Type Validation: int |
int_data.py |
Data Type Validation: int enum |
int_enum_data.py |
Data Type Validation: int range |
int_ranged_data.py |
Data Type Validation: int lambda |
int_lambda_data.py |
Data Type Validation: str |
str_data.py |
Data Type Validation: str enum |
str_enum_data.py |
Data Type Validation: str email |
str_email_data.py |
Data Type Validation: str url |
str_url_data.py |
Data Type Validation: str uuid |
str_uuid_data.py |
Data Type Validation: str ipv4 |
str_ipv4_data.py |
Data Type Validation: str ipv6 |
str_ipv6_data.py |
Data Type Validation: str regex |
str_regex_data.py |
Default Data: default |
extra_params_ex.py |
Let's first see a basic example.
from serialize_mcr import SerializeMCR
class FruitBucket(SerializeMCR):
# define how our data should look like using `schema`.
schema = [
{'name': 'apple'},
{'name': 'orange'},
{'name': 'pineapple'},
]
# test data for FruitBucket
test1 = dict(
apple="good apple",
orange="very good orange",
pineapple="nice pineapple",
)
serialize-mcr
fruits = FruitBucket(test1)
# `fruits` is a proper python object , which means that you can use
# `fruits.apple` syntax to retrieve the value of `apple`.
print(fruits.apple)
>>> good apple
# ...and other fruits too.
print(fruits.orange)
>>> very good orange
print(fruits.pineapple)
>>> nice pineapple
# you can get the JSON formatted string back too.
print(fruits)
>>> {"apple": "good apple", "orange": "very good orange", "pineapple": "nice pineapple"}
# interested to get the python dictionary back?
fruits_data = fruits.as_dict()
print(fruits_data)
>>> {'apple': 'good apple', 'orange': 'very good orange', 'pineapple': 'nice pineapple'}
Let's see how we can serialize-mcr more complex data structure into python object.
Define a nested data schema
called compound_schema
to serialize-mcr nested JSON
data.
from serialize_mcr import SerializeMCR
class SnackBucket(SerializeMCR):
schema = [
{'name': 'apple'},
{'name': 'orange'},
{'name': 'pineapple'},
{'name': 'snack', 'is_compound': True,
'compound_schema': [
{'name': 'cheese', 'optional': True},
{'name': 'chocolate'},
{'name': 'chips', 'nullable': True},
],
},
]
test3 = dict(
apple="good apple",
orange="very good orange",
pineapple="nice pineapple",
snack=[
dict(
cheese="Feta",
chocolate="Ferrero Rocher",
chips=[]
),
dict(
chocolate="Swiss milk chocolate",
chips=["Cheetos", "Lays Classic Potato Chips", "Cool Ranch Doritos"]
),
]
)
mysnacks = SnackBucket(test3)
print(mysnacks)
>>> {"apple": "good apple", "orange": "very good orange", "pineapple": "nice pineapple",
>>> "snack": [{"cheese": "Feta", "chocolate": "Ferrero Rocher", "chips": []},
>>> {"chocolate": "Swiss milk chocolate", "chips":
>>> ["Cheetos", "Lays Classic Potato Chips", "Cool Ranch Doritos"]}]}
Define a separete data serialize-mcr
serialize-mcrr called compound_serialize-mcrr
to serialize-mcr nested JSON
data.
from serialize_mcr import SerializeMCR
class Snack(SerializeMCR):
schema = [
# cheese is nice but is optional.
{'name': 'cheese', 'optional': True},
# chocolate is a MUST have.
{'name': 'chocolate'},
# chips is a must but we have to decide which kind later,
# so its value can be None, False, "", {}, [].
{'name': 'chips', 'nullable': True},
]
class NestedBucket(SerializeMCR):
schema = [
{'name': 'apple'},
{'name': 'orange'},
{'name': 'pineapple'},
{'name': 'snack', 'is_compound': True, 'compound_serialize-mcrr': Snack}
]
# test data for NestedBucket
test2 = dict(
apple="good apple",
orange="very good orange",
pineapple="nice pineapple",
snack=dict(
chocolate="Ferrero Rocher",
chips=[] # yeah its a list of chips!
),
)
my_snacks = NestedBucket(test2)
print(my_snacks)
>>> {"apple": "good apple", "orange": "very good orange", "pineapple": "nice pineapple",
>>> "snack": {"chocolate": "Ferrero Rocher", "chips": []}}
a compact example that shows all data types currently supported by this package.
from serialize_mcr import SerializeMCR
class TypedData(SerializeMCR):
schema = [
{'name': 'prop1', 'type': (int,)},
{'name': 'prop2', 'type': (int, (1, 64, 343))},
{'name': 'prop3', 'type': (int, range(1, 10, 3))},
{'name': 'prop4', 'type': (int, lambda x: x % 2 == 0)},
{'name': 'prop5', 'type': (str,)},
{'name': 'prop6', 'type': (str, ('SUCCESS', 'FAILURE'))},
{'name': 'prop7', 'type': (str, 'email')},
{'name': 'prop8', 'type': (str, 'url')},
{'name': 'prop9', 'type': (str, 'ipv4')},
{'name': 'prop10', 'type': (str, 'ipv6')},
{'name': 'prop11', 'type': (str, 'uuid')},
{'name': 'prop12', 'type': (str, '[^@]+@[^@]+\.[^@]+')},
{'name': 'prop13', 'type': (float,)},
{'name': 'prop14', 'type': (bool,)},
]
test1 = {
'prop1': 1,
'prop2': 64,
'prop3': 4,
'prop4': 2,
'prop5': "str",
'prop6': 'SUCCESS',
'prop7': 'anyone@emailservice.com',
'prop8': 'https://www.something.com/something-something/something/12345',
'prop9': '172.16.255.1',
'prop10': '2001:0db8:0a0b:12f0:0000:0000:0000:0001',
'prop11': 'c026dd66-86f2-498e-8c2c-858179c0c93d',
'prop12': 'junpufan@me.com',
'prop13': 0.1,
'prop14': True
}
data1 = TypedData(test1)
print(data1)
# >>> {"prop1": 1, "prop2": 64, "prop3": 4, "prop4": 2, "prop5": "str",
# >>> "prop6": "SUCCESS", "prop7": "anyone@emailservice.com",
# >>> "prop8": "https://www.something.com/something-something/something/12345",
# >>> "prop9": "172.16.255.1", "prop10": "2001:0db8:0a0b:12f0:0000:0000:0000:0001",
# >>> "prop11": "c026dd66-86f2-498e-8c2c-858179c0c93d", "prop12": "junpufan@me.com",
# >>> "prop13": 0.1, "prop14": true}
a simple way to ensure a value is set at all times for optional properties in a JSON schema
from serialize_mcr import SerializeMCR
class DefaultingValues(SerializeMCR):
schema = [
{'name': 'var1', 'type': (int,), 'optional': True, 'default': 0},
{'name': 'var2', 'type': (str,), 'optional': True, 'default': 'str'},
{'name': 'var3', 'type': (float,), 'optional': True, 'default': 1.25},
{'name': 'var4', 'type': (bool,), 'optional': True, 'default': True},
{'name': 'var5', 'type': (int, (1, 3, 5)), 'optional': True, 'default': 3}
]
### acceptable bodies to pass in
# since all properties are optional, no values are required to be passed in
# all values will be set to their defined default value
test1 = {}
data1 = DefaultingValues(data=test1)
# choosing some but not all properties will also work, as var2, var3, var5 will be set to their default values
test2 = {'var1': 24, 'var4': False}
data2 = DefaultingValues(data=test2)
class InvalidDefaultValues(SerializeMCR):
schema = [
{'name': 'var1', 'type': (int,), 'default': 0},
{'name': 'var2', 'type': (str,), 'optional': True, 'default': 1},
{'name': 'var3', 'type': (float,), 'optional': True, 'default': True},
{'name': 'var4', 'type': (bool,), 'optional': True, 'default': False},
{'name': 'var5', 'type': (int, (1, 3, 5)), 'default': 3}
]
# var1 is not optional, therefore a default value cannot be set
# var2 has a defined type of `str`, but a defaulted value of `int`
# var3 has a defined type of `float`, but a defaulted value of `bool`
# var4 is acceptable
# var5 is not optional, therefore a default value cannot be set
Note: The schema's of a serialize-mcr are not validated until a dataset is passed into the Class, therefore improper schema definitions are acceptable, but will fail to build.