Skip to content

fuhrysteve/marshmallow-jsonschema

Repository files navigation

marshmallow-jsonschema: JSON Schema formatting with marshmallow

Build Status Code style: black

marshmallow-jsonschema translates marshmallow schemas into JSON Schema Draft v7 compliant documents.

Why would I want my schema translated to JSON?

A few common reasons:

  • Render a marshmallow schema as a form in another runtime (web browser, mobile, native desktop) where you can't import Python.
  • Validate request/response bodies in an API gateway or contract-test layer that consumes JSON Schema directly.
  • Publish your schema as documentation alongside an OpenAPI spec.

Installation

Requires Python 3.9+ and marshmallow 3.13 or later (works on both marshmallow 3 and marshmallow 4).

pip install marshmallow-jsonschema

For older environments:

  • marshmallow 2 → marshmallow-jsonschema<0.11
  • Python 3.6–3.8 or marshmallow 3.11–3.12 → marshmallow-jsonschema<0.14
  • A marshmallow-4-broken intermediate state → marshmallow-jsonschema<0.14 (with marshmallow<4)

Client tools that render forms from JSON Schema

Examples

Simple example

from marshmallow import Schema, fields
from marshmallow_jsonschema import JSONSchema

class UserSchema(Schema):
    username = fields.String()
    age = fields.Integer()
    birthday = fields.Date()

JSONSchema().dump(UserSchema())

Yields:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$ref": "#/definitions/UserSchema",
    "definitions": {
        "UserSchema": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "age": {"title": "age", "type": "integer"},
                "birthday": {"title": "birthday", "type": "string", "format": "date"},
                "username": {"title": "username", "type": "string"}
            }
        }
    }
}

Nested example

Nested schemas land in definitions and are referenced via $ref:

from marshmallow import Schema, fields
from marshmallow_jsonschema import JSONSchema

class AddressSchema(Schema):
    street = fields.String()
    city = fields.String()

class UserSchema(Schema):
    name = fields.String()
    address = fields.Nested(AddressSchema)

JSONSchema().dump(UserSchema())

Yields:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$ref": "#/definitions/UserSchema",
    "definitions": {
        "AddressSchema": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "city": {"title": "city", "type": "string"},
                "street": {"title": "street", "type": "string"}
            }
        },
        "UserSchema": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "address": {"type": "object", "$ref": "#/definitions/AddressSchema"},
                "name": {"title": "name", "type": "string"}
            }
        }
    }
}

fields.Nested("Self") and fields.Nested(lambda: SomeSchema()) are both supported for recursive references.

Top-level array (many=True)

Passing many=True to a schema describes a list of objects rather than a single one; the dumped JSON Schema reflects that with an array envelope:

JSONSchema().dump(UserSchema(many=True))

Yields:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "definitions": {"UserSchema": { ... }},
    "type": "array",
    "items": {"$ref": "#/definitions/UserSchema"}
}

Flask + JSON Schema form rendering

A complete runnable Flask example lives at example/example.py. It exposes a marshmallow schema as JSON Schema and renders it as a form using json-editor — pure JS, no build pipeline, drop in via CDN:

pip install -r example/requirements.txt
python example/example.py
# open http://127.0.0.1:5000/

For a richer React-based alternative, see ReactJsonSchemaFormJSONSchema below.

Validators

Marshmallow's standard validators translate automatically into JSON Schema constraints when the field is dumped:

Validator JSON Schema output
validate.Length(min=, max=) minLength / maxLength for strings, minItems / maxItems for lists & nested
validate.Range(min=, max=, min_inclusive=, max_inclusive=) minimum / maximum (or exclusiveMinimum / exclusiveMaximum)
validate.OneOf(choices, labels=) enum (and the non-standard enumNames for compatibility with react-jsonschema-form)
validate.Equal(value) enum: [value]
validate.Regexp(pattern) pattern
validate.ContainsOnly(choices, labels=) items.anyOf: [{const}, ...] + uniqueItems: true
from marshmallow import Schema, fields, validate
from marshmallow_jsonschema import JSONSchema

class UserSchema(Schema):
    age = fields.Integer(validate=validate.Range(min=0, max=150))
    name = fields.String(validate=validate.Length(min=1, max=100))
    role = fields.String(validate=validate.OneOf(["admin", "user"]))

For a custom validator that's a subclass of one of the above, set _jsonschema_base_validator_class = validate.<Base> on it so the translation still fires.

Enums

marshmallow.fields.Enum (added in marshmallow 3.18) is supported out of the box and emits the enum-member names. The third-party marshmallow-enum EnumField is also supported when installed; native Enum is preferred when both are present.

by_value=True enums are supported when every member's value is a string — the common pattern for serialising enums as their string value in the wire format:

class Status(str, Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"

class S(Schema):
    status = fields.Enum(Status, by_value=True)
# {"status": {"enum": ["active", "inactive"], "type": "string"}}

Mixed-type or non-string enum values still raise NotImplementedError — use the class MyEnum(str, Enum) pattern, or load by name instead.

Advanced usage

Schema-level title and description

Setting title or description on a schema's inner Meta class emits them at the corresponding definition entry:

class UserSchema(Schema):
    class Meta:
        title = "User"
        description = "A user account record."

    name = fields.String()

Customizing the definitions path

By default nested schemas live under #/definitions/<Name>. Pass definitions_path to use a different single-segment key:

JSONSchema(definitions_path="schemas").dump(MySchema())
# {"$ref": "#/schemas/MySchema", "schemas": {...}, ...}

Multi-segment paths (e.g. "components/schemas") are rejected because they would produce a flat dict key with a slash in it rather than the nested structure consumers expect — wrap the output yourself if you need that shape.

Custom field types

Add a _jsonschema_type_mapping method to your field so we know how to serialize it. Field-level metadata={...} and dump_default values are then merged in automatically, so a single mapping method gets you the full set of standard schema attributes.

class Colour(fields.Field):
    def _jsonschema_type_mapping(self):
        return {"type": "string"}

    def _serialize(self, value, attr, obj):
        r, g, b = value
        return "#%02X%02X%02X" % (r, g, b)


class UserSchema(Schema):
    favourite_colour = Colour(
        dump_default="#ffffff",
        metadata={"title": "Colour", "description": "Hex RGB"},
    )

For wrapper-style custom fields that need to re-enter the dumping machinery (e.g. to emit a $ref to a recursive schema), declare _jsonschema_type_mapping(self, json_schema, obj) with the two extra parameters and the JSONSchema instance + obj will be passed in.

Polymorphic schemas (marshmallow-oneofschema)

When the optional marshmallow-oneofschema package is installed, dumping a OneOfSchema produces a JSON Schema oneOf over each variant — with the discriminator field pinned to its constant value:

from marshmallow import Schema, fields
from marshmallow_oneofschema import OneOfSchema
from marshmallow_jsonschema import JSONSchema

class TriangleSchema(Schema):
    base = fields.Float(required=True)
    height = fields.Float(required=True)

class CircleSchema(Schema):
    radius = fields.Float(required=True)

class ShapeSchema(OneOfSchema):
    type_schemas = {"triangle": TriangleSchema, "circle": CircleSchema}

JSONSchema().dump(ShapeSchema())

Yields (abbreviated):

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "oneOf": [
    {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "base": {"type": "number", "...": "..."},
        "height": {"type": "number", "...": "..."},
        "type": {"const": "triangle", "type": "string"}
      },
      "required": ["base", "height", "type"]
    },
    { "...circle variant...": "..." }
  ]
}

Variants are inlined (not $ref) so the schema actually validates the wire format OneOfSchema produces. Custom type_field (default "type") is honored. Install with pip install marshmallow-jsonschema[oneofschema].

React-JSONSchema-Form Extension

react-jsonschema-form renders JSON Schema as a React form. It accepts a separate uiSchema that controls presentation; this package's ReactJsonSchemaFormJSONSchema extension dumps both at once:

from marshmallow import Schema, fields
from marshmallow_jsonschema.extensions import ReactJsonSchemaFormJSONSchema

class MySchema(Schema):
    first_name = fields.String(metadata={"ui:autofocus": True})
    last_name = fields.String()

    class Meta:
        react_uischema_extra = {"ui:order": ["first_name", "last_name"]}


json_schema_obj = ReactJsonSchemaFormJSONSchema()
data = json_schema_obj.dump(MySchema())
ui_schema_json = json_schema_obj.dump_uischema(MySchema())

Contributing

Bug reports and pull requests are welcome. See CONTRIBUTING.md for local-dev setup and CONTRIBUTORS.md for the people who built this.

About

JSON Schema Draft v7 (http://json-schema.org/) formatting with marshmallow

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors