rgamber
rgamber

Reputation: 5849

Flask Marshmallow JSON fields

I have defined a POST call would that needs data:

{
    "one" : "hello",
    "two" : "world",
    "three" : { 
                "ab": "123", 
                "cd": false 
              }
}

For this, I am able to define one and two, but unsure what is the right was to define three. How can I specify a JSON field in Marshmallow? I am able to define basic fields such as:

from marshmallow import Schema, post_load, fields

class Foo(object):
    def __init__(self, one, two=None):
        self.one = one
        self.two = two

class MySchema(Schema):
    one = fields.String(required=True)
    two = fields.String()

    @post_load
    def create_foo(self, data, **kwargs):
        return Foo(**data)

How do I define three in MySchema? Should I:

  1. simply put it as a string and do manipulation to load it as a json using json.loads()/json.dumps()? Or is there a way to define it properly?
  2. define it as a fields.Dict?
  3. can I define a separate Schema for this field
  4. should I extend field.Field?

I am looking at https://marshmallow.readthedocs.io/en/3.0/api_reference.html, though still not sure. A JSON sub-field or a nested JSON seems like a common use-case, yet I am not able to find anything relevant on this.

Upvotes: 11

Views: 12891

Answers (3)

Mark Amery
Mark Amery

Reputation: 154735

If you want to support arbitrary nested values in the field, rather than defining a schema for them, you can use:

  • fields.Dict() (to accept an arbitrary Python dict, or, equivalently, an arbitrary JSON object), or
  • fields.Raw() (for arbitrary Python objects, or, equivalently, arbitrary JSON values)

An example script you can run that uses both of the above, based on the example in the question:

import json
from marshmallow import Schema, fields, post_load


class Foo(object):
    def __init__(self, one, two=None, three=None, four=None):
        self.one = one
        self.two = two
        self.three = three
        self.four = four


class MySchema(Schema):
    one = fields.String(required=True)
    two = fields.String()
    three = fields.Dict()
    four = fields.Raw()

    @post_load
    def create_foo(self, data, **kwargs):
        return Foo(**data)


post_data = json.loads(
    """{
    "one" : "hello",
    "two" : "world",
    "three" : {
                "ab": "123",
                "cd": false
              },
    "four" : 567
}"""
)

foo = MySchema().load(post_data)
print(foo.one)
print(foo.two)
print(foo.three)
print(foo.four)

Upvotes: 3

jspcal
jspcal

Reputation: 51904

This can be done with nested schemas: https://marshmallow.readthedocs.io/en/3.0/nesting.html

Your schema would look something like:

class MySchema(Schema):
    one = fields.String(required=True)
    two = fields.String()
    three = fields.Nested(ThreeSchema)

class ThreeSchema(Schema):
    ab = fields.String()
    cd = fields.Boolean()

Upvotes: 9

Gosha null
Gosha null

Reputation: 623

You can create your own field

import json
from marshmallow import fields

class JSON(fields.Field):
    def _deserialize(self, value, attr, data, **kwargs):
        if value:
            try:
                return json.loads(value)
            except ValueError:
                return None

        return None
...
from marshmallow import fields, Schema
from schemas.base import JSON

class ObjectSchema(Schema):
    id = fields.Integer()
    data = JSON()

Upvotes: 4

Related Questions