marshmallow-polyfieldを使ってoneOf的な構造のdataを扱う
はじめに
例えば、以下のよう1つのfieldに複数の形状の値が入ることがある。そして、その形状を決めるためにtype
などfieldを含まれているJSONがあるとする。
以下の様な感じ(下の例では、personとgroupという2つの形状がobに入る可能性がある)。
{ "ob": { "age": 20, "name": "foo" }, "type": "person" }
もしくはこう。
{ "ob": { "name": "A", "members": [ { "age": 20, "name": "foo" } ] }, "type": "group" }
それぞれ、typeで判別できるけれど。これを良い感じにmarshmallowでserialize,deserializeしたいという話し。
準備
事前に以下が必要。marshmallow-polyfieldを使う。
$ pip install marshmallow-polyfield
方法
以下の様な感じ。
from marshmallow import Schema, fields from marshmallow_polyfield import PolyField class Person(Schema): name = fields.String(required=True) age = fields.Integer(required=True) class Group(Schema): name = fields.String(required=True) members = fields.List(fields.Nested(Person()), required=True) def selector_for_deserialize(d, parent): if parent.get("type") == "group": return Group() else: return Person() def selector_for_serialize(ob, parent): if "members" in ob: parent["type"] = "group" return Group() else: parent["type"] = "person" return Person() class S(Schema): type = fields.String(required=True) ob = PolyField( serialization_schema_selector=selector_for_serialize, deserialization_schema_selector=selector_for_deserialize, required=True ) print(S().load({"ob": {"name": "foo", "age": 20}, "type": "person"})) print(S().load({"ob": {"name": "A", "members": [{"name": "foo", "age": 20}]}, "type": "group"})) print(S().dump({"ob": {"name": "foo", "age": 20}})) print(S().dump({"ob": {"name": "A", "members": [{"name": "foo", "age": 20}]}})) # UnmarshalResult(data={'ob': {'name': 'foo', 'age': 20}, 'type': 'person'}, errors={}) # UnmarshalResult(data={'ob': {'name': 'A', 'members': [{'name': 'foo', 'age': 20}]}, 'type': 'group'}, errors={}) # MarshalResult(data={'ob': {'name': 'foo', 'age': 20}, 'type': 'person'}, errors={}) # MarshalResult(data={'ob': {'name': 'A', 'members': [{'name': 'foo', 'age': 20}]}, 'type': 'group'}, errors={})