Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apply JSON Schema with Marshmallow Serialization

I am using Marshmallow for serialization and de-serialization of JSON strings. From the Marshmallow API Docs (https://marshmallow.readthedocs.io/en/3.0/api_reference.html), it looks like you have specify a list of fields (and, unless using Meta) their data type. For example:

Marital_Status=Fields.Str()
Employer=Fields.Str()
ContactInfo(data) #where ContactInfo is a class not shown here

However, I already have a JSON schema that specifies the fields and the data types. For example:

the_template_schema={

"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://example.com/root.json",
"type": "object",
"title": "The Root Schema",
"properties": {
 "Marital_Status": {
  "$id": "#/properties/Marital_Status",
  "type": "string",
  "title": "The Marital_status Schema",
  "default": "",
  "examples": [
    "Married"
  ],
  "pattern": "^(.*)$"
}
"Employer": {
  "$id": "#/properties/Employer",
  "type": "string",
  "title": "The Employer Schema",
  "default": "",
  "examples": [
    "Roivant"
  ],
  "pattern": "^(.*)$"
        }

    }
}

My Question

I want to specify to Marshmallow the fields, based on the schema data provided. Something like:

fields.magicmethod(the_template_schema)
ContactInfo(data)

Is this possible? If so, how?

like image 838
Josh Avatar asked Jan 17 '19 21:01

Josh


2 Answers

In marshmallow, the schema should be specified as a class in your Python code (see example here: https://marshmallow.readthedocs.io/en/3.0/quickstart.html#declaring-schemas).

For your case, it might look something like

from marshmallow import Schema, fields

class ContactInfoSchema(Schema):
    Marital_Status=Fields.Str()
    Employer=Fields.Str()

Do you need to use marshmallow? If your schema already exists in the json-schema format, you can load your objects using json.load and validate against the schema using the jsonschema module.

https://medium.com/python-pandemonium/json-the-python-way-91aac95d4041

https://python-jsonschema.readthedocs.io/en/latest/

like image 85
axiomatic Avatar answered Oct 24 '22 02:10

axiomatic


You could write your own converter that will take a JSON Schema and create a dynamic marshmallow schema from it.

Here's an example ( it's not JSONSchema but something simpler ).

from marshmallow import Schema, fields
from functools import partial


def validator(valid_values, input):
    if input in valid_values:
        return True
    return False


def get_type(param_type):
    if param_type == "String":
        return fields.String
    if param_type == "Boolean":
        return fields.Boolean

def gen_schema(cls_name, params):
    fields = {}
    for p in params:
        field_type = get_type(p["type"])
        if p.get("valid_values"):
            fields[p["name"]] = field_type(validate=partial(validator, p["valid_values"]))
        else:
            fields[p["name"]] = field_type()
    schema = type(cls_name, (Schema,), fields)
    return schema

class ParameterSchema(Schema):
    name = fields.String(required=True)
    description = fields.String(required=False)
    required = fields.Bool(default=False)
    type = fields.String(required=True)
    valid_values = fields.List(fields.String, required=False)

p = [
  {"name": "filename",
   "description": "Should be a filename",
   "required": True,
   "type": "String",
   "valid_values": ["hello.txt", "foo.py", "bar.png"]
  },
  {"name": "SomeBool",
   "description": "Just a bool",
   "required": True,
   "type": "Boolean",
  },
  {"name": "NotRequiredBool",
   "description": "Another bool thats not required",
   "required": False,
   "type": "Boolean"
  }
]

req1 = {"filename": "foo.py", "SomeBool": False}
req2 = {"filename": "hi.txt", "SomeBool": True, "NotRequiredBool": False}

schema = ParameterSchema()
params1 = schema.load(p, many=True)

dynamic_schema = gen_schema("D1", params1.data)()

dynamic_res1 = dynamic_schema.load(req1)
dynamic_res2 = dynamic_schema.load(req2)
print(dynamic_res1)
print(dynamic_res2)

Running this prints:

UnmarshalResult(data={'filename': 'foo.py', 'SomeBool': False}, errors={})
UnmarshalResult(data={'NotRequiredBool': False, 'SomeBool': True}, errors={'filename': ['Invalid value.']})

You'd just have to change gen_schema to accept a valid JSONSchema instead of the simple one I made up here.

Hope that helps.

like image 32
sjmh Avatar answered Oct 24 '22 02:10

sjmh