5

I'm quite new to marshmallow but my question refers to the issue of handling dict-like objects. There are no workable examples in the Marshmallow documentation. I came across with a simple example here in stack overflow Original question and this is the original code for the answer suppose this should be quite simple

from marshmallow import Schema, fields, post_load, pprint

class UserSchema(Schema):
    name = fields.String()
    email = fields.Email()
    friends = fields.List(fields.String())

class AddressBookSchema(Schema):
    contacts =fields.Dict(keys=fields.String(),values=fields.Nested(UserSchema))

@post_load
def trans_friends(self, item):
    for name in item['contacts']:
        item['contacts'][name]['friends'] = [item['contacts'][n] for n in item['contacts'][name]['friends']]


data = """
   {"contacts": { 
        "Steve": {
            "name": "Steve",
            "email": "steve@example.com",
            "friends": ["Mike"]
        },
        "Mike": {
            "name": "Mike",
            "email": "mike@example.com",
            "friends": []
        }
   }
}
"""

deserialized_data = AddressBookSchema().loads(data)
pprint(deserialized_data)

However, when I run the code I get the following NoneType value

`None`

The input hasn't been marshalled.

I'm using the latest beta version of marshmallow 3.0.0b20. I can't find a way to make this work even it looks so simple. The information seems to indicate that nested dictionaries are being worked by the framework.

Currently I'm working in a cataloging application for flask where I'm receiving JSON messages where I can't really specify the schema beforehand. My specific problem is the following:

data = """
   {"book": { 
        "title": {
            "english": "Don Quixiote",
            "spanish": "Don Quijote"
            },
        "author": {
            "first_name": "Miguel",
            "last_name": "Cervantes de Saavedra"
            }
        },
    "book": { 
        "title": {
            "english": "20000 Leagues Under The Sea",
            "french": "20000 Lieues Sous Le Mer",
            "japanese": "海の下で20000リーグ",
            "spanish": "20000 Leguas Bajo El Mar",
            "german": "20000 Meilen unter dem Meeresspiegel",
            "russian": "20000 лиг под водой"
            },
        "author": {
            "first_name": "Jules",
            "last_name": "Verne"
            }
        }
    }

This is just toy data but exemplifies that the keys in the dictionaries are not fixed, they change in number and text.

So the questions are why am I getting the validation error in a simple already worked example and if it's possible to use the marshmallow framework to validate my data,

Thanks

Mikhail_Sam
  • 10,602
  • 11
  • 66
  • 102
GLR
  • 157
  • 2
  • 7
  • the code in https://stackoverflow.com/a/52812320/4201810 should able to run, your code above is not the same copy. – georgexsh Nov 12 '18 at 06:34
  • No, it returns a NoneType object, it doesn't marshall the input. The issue is either returns NoneType or marks a validation error. – GLR Nov 12 '18 at 19:59
  • exact copy should run, yours is not. – georgexsh Nov 12 '18 at 23:30
  • @GLR is right, that code returns `None` for 3.0.0b20. I'll suggest opening an issue in the project repo and using the latest "stable" version `2.16.3` (your snippet works in stable). – el.atomo Nov 13 '18 at 11:29
  • @el.atomo Thanks for the confirmation. There was an opened question similar to mine already opened [Issue #1047](https://github.com/marshmallow-code/marshmallow/issues/1047). I just complimented with the code above. – GLR Nov 15 '18 at 01:00

1 Answers1

6

There are two issues in your code.

The first is the indentation of the post_load decorator. You introduced it when copying the code here, but you don't have it in the code you're running, otherwise you wouldn't get None.

The second is due to a documented change in marshmallow 3. pre/post_load/dump functions are expected to return the value rather than mutate it.

Here's a working version. I also reworked the decorator:

from marshmallow import Schema, fields, post_load, pprint

class UserSchema(Schema):
    name = fields.String()
    email = fields.Email()
    friends = fields.List(fields.String())

class AddressBookSchema(Schema):
    contacts = fields.Dict(keys=fields.String(),values=fields.Nested(UserSchema))

    @post_load
    def trans_friends(self, item):
        for contact in item['contacts'].values():
            contact['friends'] = [item['contacts'][n] for n in contact['friends']]
        return item


data = """
    {
        "contacts": { 
            "Steve": {
                "name": "Steve",
                "email": "steve@example.com",
                "friends": ["Mike"]
            },
            "Mike": {
                "name": "Mike",
                "email": "mike@example.com",
                "friends": []
            }
        }
    }
"""

deserialized_data = AddressBookSchema().loads(data)
pprint(deserialized_data)

And finally, the Dict in marshmallow 2 doesn't have key/value validation feature, so it will just silently ignore the keys and values argument and perform no validation.

Jérôme
  • 13,328
  • 7
  • 56
  • 106
  • This "data" structure doesn't make sense to me. Why would you have "data =" rather than a "contacts=" and take out the outer most level of the dictionary leaving "Steve" and "Mike" as the first keys of the dictionary. Would you then have a problem with parsing the schema without fixed key names as some sort of anchor in the Schema class? – Back2Basics Aug 02 '20 at 10:05
  • @Back2Basics I'm afraid you can't do that. This is discussed in https://github.com/marshmallow-code/marshmallow/issues/972. – Jérôme Aug 09 '20 at 23:10