Tomas Greif
Tomas Greif

Reputation: 22661

Nested class in JSON de-serialization using marshmallow

How would I de-serialize nested JSON using marshmallow so that I can use dot notation like app.data.person.lname? Currently my example works only one level down, but I can't get last name (lname) from the nested person structure:

from marshmallow import Schema, fields, post_load
import datetime as dt
import json

class Person(object):
    def __init__(self, fname, lname):
        self.fname = fname
        self.lname = lname

class PersonSchema(Schema):
    fname = fields.Str()
    lname = fields.Str()

class App(object):
    def __init__(self, appid, channel, person):
        self.appid = appid
        self.channel = channel
        self.person = person
        self.created_at = dt.datetime.now()

class AppSchema(Schema):
    appid = fields.Str()
    channel = fields.Str()
    person = fields.Nested(PersonSchema)
    created_at = fields.DateTime()

    @post_load
    def make_user(self, data):
        return App(**data)

json_data = """{
    "appid": "2309wfjwef",
    "channel": "retail",
    "person": {
        "fname": "John",
        "lname": "Doe"
        }
}"""

app_data = json.loads(json_data)

schema = AppSchema()
app = schema.load(app_data)

print(app.data.person.lname)

Getting:

AttributeError: 'dict' object has no attribute 'lname'

Upvotes: 2

Views: 2757

Answers (2)

Takuto Yamada
Takuto Yamada

Reputation: 1

in addition to Tomas Greif's last post, I get an error: TypeError: make_app() got an unexpected keyword argument 'many'

Until I added the argument to the @post_load functions like:

def make_person(self, data, **kwargs):

and

def make_app(self, data, **kwargs):

and then there was an additional error AttributeError: 'App' object has no attribute 'data', so I removed the data from the print statement:

print(app.person.fname)

AFter those changes, the code ran without errors.

Upvotes: 0

Tomas Greif
Tomas Greif

Reputation: 22661

Obviously, you need to create Person in PersonSchema:

from marshmallow import Schema, fields, post_load
import datetime as dt
import json

class Person(object):
    def __init__(self, fname, lname):
        self.fname = fname
        self.lname = lname

class PersonSchema(Schema):
    fname = fields.Str()
    lname = fields.Str()

    @post_load
    def make_person(self, data):
        return Person(**data)


class App(object):
    def __init__(self, appid, channel, person):
        self.appid = appid
        self.channel = channel
        self.person = person
        self.created_at = dt.datetime.now()

class AppSchema(Schema):
    appid = fields.Str()
    channel = fields.Str()
    person = fields.Nested(PersonSchema)
    created_at = fields.DateTime()

    @post_load
    def make_app(self, data):
        return App(**data)

json_data = """{
    "appid": "2309wfjwef",
    "channel": "retail",
    "person": {
        "fname": "John",
        "lname": "Doe"
        }
}"""

app_data = json.loads(json_data)

schema = AppSchema()
app = schema.load(app_data)

print(app.data.person.fname)

Upvotes: 2

Related Questions