muutils.json_serialize.serializable_field
extends dataclasses.Field
for use with SerializableDataclass
In particular, instead of using dataclasses.field
, use serializable_field
to define fields in a SerializableDataclass
.
You provide information on how the field should be serialized and loaded (as well as anything that goes into dataclasses.field
)
when you define the field, and the SerializableDataclass
will automatically use those functions.
1"""extends `dataclasses.Field` for use with `SerializableDataclass` 2 3In particular, instead of using `dataclasses.field`, use `serializable_field` to define fields in a `SerializableDataclass`. 4You provide information on how the field should be serialized and loaded (as well as anything that goes into `dataclasses.field`) 5when you define the field, and the `SerializableDataclass` will automatically use those functions. 6 7""" 8 9from __future__ import annotations 10 11import dataclasses 12import sys 13import types 14from typing import Any, Callable, Optional, Union, overload, TypeVar 15 16 17# pylint: disable=bad-mcs-classmethod-argument, too-many-arguments, protected-access 18 19 20class SerializableField(dataclasses.Field): 21 """extension of `dataclasses.Field` with additional serialization properties""" 22 23 __slots__ = ( 24 # from dataclasses.Field.__slots__ 25 "name", 26 "type", 27 "default", 28 "default_factory", 29 "repr", 30 "hash", 31 "init", 32 "compare", 33 "metadata", 34 "kw_only", 35 "_field_type", # Private: not to be used by user code. 36 # new ones 37 "serialize", 38 "serialization_fn", 39 "loading_fn", 40 "deserialize_fn", # new alternative to loading_fn 41 "assert_type", 42 "custom_typecheck_fn", 43 ) 44 45 def __init__( 46 self, 47 default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 48 default_factory: Union[ 49 Callable[[], Any], dataclasses._MISSING_TYPE 50 ] = dataclasses.MISSING, 51 init: bool = True, 52 repr: bool = True, 53 hash: Optional[bool] = None, 54 compare: bool = True, 55 # TODO: add field for custom comparator (such as serializing) 56 metadata: Optional[types.MappingProxyType] = None, 57 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 58 serialize: bool = True, 59 serialization_fn: Optional[Callable[[Any], Any]] = None, 60 loading_fn: Optional[Callable[[Any], Any]] = None, 61 deserialize_fn: Optional[Callable[[Any], Any]] = None, 62 assert_type: bool = True, 63 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 64 ): 65 # TODO: should we do this check, or assume the user knows what they are doing? 66 if init and not serialize: 67 raise ValueError("Cannot have init=True and serialize=False") 68 69 # need to assemble kwargs in this hacky way so as not to upset type checking 70 super_kwargs: dict[str, Any] = dict( 71 default=default, 72 default_factory=default_factory, 73 init=init, 74 repr=repr, 75 hash=hash, 76 compare=compare, 77 kw_only=kw_only, 78 ) 79 80 if metadata is not None: 81 super_kwargs["metadata"] = metadata 82 else: 83 super_kwargs["metadata"] = types.MappingProxyType({}) 84 85 # special check, kw_only is not supported in python <3.9 and `dataclasses.MISSING` is truthy 86 if sys.version_info < (3, 10): 87 if super_kwargs["kw_only"] == True: # noqa: E712 88 raise ValueError("kw_only is not supported in python >=3.9") 89 else: 90 del super_kwargs["kw_only"] 91 92 # actually init the super class 93 super().__init__(**super_kwargs) # type: ignore[call-arg] 94 95 # now init the new fields 96 self.serialize: bool = serialize 97 self.serialization_fn: Optional[Callable[[Any], Any]] = serialization_fn 98 99 if loading_fn is not None and deserialize_fn is not None: 100 raise ValueError( 101 "Cannot pass both loading_fn and deserialize_fn, pass only one. ", 102 "`loading_fn` is the older interface and takes the dict of the class, ", 103 "`deserialize_fn` is the new interface and takes only the field's value.", 104 ) 105 self.loading_fn: Optional[Callable[[Any], Any]] = loading_fn 106 self.deserialize_fn: Optional[Callable[[Any], Any]] = deserialize_fn 107 108 self.assert_type: bool = assert_type 109 self.custom_typecheck_fn: Optional[Callable[[type], bool]] = custom_typecheck_fn 110 111 @classmethod 112 def from_Field(cls, field: dataclasses.Field) -> "SerializableField": 113 """copy all values from a `dataclasses.Field` to new `SerializableField`""" 114 return cls( 115 default=field.default, 116 default_factory=field.default_factory, 117 init=field.init, 118 repr=field.repr, 119 hash=field.hash, 120 compare=field.compare, 121 metadata=field.metadata, 122 kw_only=getattr(field, "kw_only", dataclasses.MISSING), # for python <3.9 123 serialize=field.repr, # serialize if it's going to be repr'd 124 serialization_fn=None, 125 loading_fn=None, 126 deserialize_fn=None, 127 ) 128 129 130Sfield_T = TypeVar("Sfield_T") 131 132 133@overload 134def serializable_field( 135 *_args, 136 default_factory: Callable[[], Sfield_T], 137 default: dataclasses._MISSING_TYPE = dataclasses.MISSING, 138 init: bool = True, 139 repr: bool = True, 140 hash: Optional[bool] = None, 141 compare: bool = True, 142 metadata: Optional[types.MappingProxyType] = None, 143 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 144 serialize: bool = True, 145 serialization_fn: Optional[Callable[[Any], Any]] = None, 146 deserialize_fn: Optional[Callable[[Any], Any]] = None, 147 assert_type: bool = True, 148 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 149 **kwargs: Any, 150) -> Sfield_T: ... 151@overload 152def serializable_field( 153 *_args, 154 default: Sfield_T, 155 default_factory: dataclasses._MISSING_TYPE = dataclasses.MISSING, 156 init: bool = True, 157 repr: bool = True, 158 hash: Optional[bool] = None, 159 compare: bool = True, 160 metadata: Optional[types.MappingProxyType] = None, 161 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 162 serialize: bool = True, 163 serialization_fn: Optional[Callable[[Any], Any]] = None, 164 deserialize_fn: Optional[Callable[[Any], Any]] = None, 165 assert_type: bool = True, 166 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 167 **kwargs: Any, 168) -> Sfield_T: ... 169@overload 170def serializable_field( 171 *_args, 172 default: dataclasses._MISSING_TYPE = dataclasses.MISSING, 173 default_factory: dataclasses._MISSING_TYPE = dataclasses.MISSING, 174 init: bool = True, 175 repr: bool = True, 176 hash: Optional[bool] = None, 177 compare: bool = True, 178 metadata: Optional[types.MappingProxyType] = None, 179 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 180 serialize: bool = True, 181 serialization_fn: Optional[Callable[[Any], Any]] = None, 182 deserialize_fn: Optional[Callable[[Any], Any]] = None, 183 assert_type: bool = True, 184 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 185 **kwargs: Any, 186) -> Any: ... 187def serializable_field( 188 *_args, 189 default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 190 default_factory: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 191 init: bool = True, 192 repr: bool = True, 193 hash: Optional[bool] = None, 194 compare: bool = True, 195 metadata: Optional[types.MappingProxyType] = None, 196 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 197 serialize: bool = True, 198 serialization_fn: Optional[Callable[[Any], Any]] = None, 199 deserialize_fn: Optional[Callable[[Any], Any]] = None, 200 assert_type: bool = True, 201 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 202 **kwargs: Any, 203) -> Any: 204 """Create a new `SerializableField` 205 206 ``` 207 default: Sfield_T | dataclasses._MISSING_TYPE = dataclasses.MISSING, 208 default_factory: Callable[[], Sfield_T] 209 | dataclasses._MISSING_TYPE = dataclasses.MISSING, 210 init: bool = True, 211 repr: bool = True, 212 hash: Optional[bool] = None, 213 compare: bool = True, 214 metadata: types.MappingProxyType | None = None, 215 kw_only: bool | dataclasses._MISSING_TYPE = dataclasses.MISSING, 216 # ---------------------------------------------------------------------- 217 # new in `SerializableField`, not in `dataclasses.Field` 218 serialize: bool = True, 219 serialization_fn: Optional[Callable[[Any], Any]] = None, 220 loading_fn: Optional[Callable[[Any], Any]] = None, 221 deserialize_fn: Optional[Callable[[Any], Any]] = None, 222 assert_type: bool = True, 223 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 224 ``` 225 226 # new Parameters: 227 - `serialize`: whether to serialize this field when serializing the class' 228 - `serialization_fn`: function taking the instance of the field and returning a serializable object. If not provided, will iterate through the `SerializerHandler`s defined in `muutils.json_serialize.json_serialize` 229 - `loading_fn`: function taking the serialized object and returning the instance of the field. If not provided, will take object as-is. 230 - `deserialize_fn`: new alternative to `loading_fn`. takes only the field's value, not the whole class. if both `loading_fn` and `deserialize_fn` are provided, an error will be raised. 231 - `assert_type`: whether to assert the type of the field when loading. if `False`, will not check the type of the field. 232 - `custom_typecheck_fn`: function taking the type of the field and returning whether the type itself is valid. if not provided, will use the default type checking. 233 234 # Gotchas: 235 - `loading_fn` takes the dict of the **class**, not the field. if you wanted a `loading_fn` that does nothing, you'd write: 236 237 ```python 238 class MyClass: 239 my_field: int = serializable_field( 240 serialization_fn=lambda x: str(x), 241 loading_fn=lambda x["my_field"]: int(x) 242 ) 243 ``` 244 245 using `deserialize_fn` instead: 246 247 ```python 248 class MyClass: 249 my_field: int = serializable_field( 250 serialization_fn=lambda x: str(x), 251 deserialize_fn=lambda x: int(x) 252 ) 253 ``` 254 255 In the above code, `my_field` is an int but will be serialized as a string. 256 257 note that if not using ZANJ, and you have a class inside a container, you MUST provide 258 `serialization_fn` and `loading_fn` to serialize and load the container. 259 ZANJ will automatically do this for you. 260 261 # TODO: `custom_value_check_fn`: function taking the value of the field and returning whether the value itself is valid. if not provided, any value is valid as long as it passes the type test 262 """ 263 assert len(_args) == 0, f"unexpected positional arguments: {_args}" 264 return SerializableField( 265 default=default, 266 default_factory=default_factory, 267 init=init, 268 repr=repr, 269 hash=hash, 270 compare=compare, 271 metadata=metadata, 272 kw_only=kw_only, 273 serialize=serialize, 274 serialization_fn=serialization_fn, 275 deserialize_fn=deserialize_fn, 276 assert_type=assert_type, 277 custom_typecheck_fn=custom_typecheck_fn, 278 **kwargs, 279 )
21class SerializableField(dataclasses.Field): 22 """extension of `dataclasses.Field` with additional serialization properties""" 23 24 __slots__ = ( 25 # from dataclasses.Field.__slots__ 26 "name", 27 "type", 28 "default", 29 "default_factory", 30 "repr", 31 "hash", 32 "init", 33 "compare", 34 "metadata", 35 "kw_only", 36 "_field_type", # Private: not to be used by user code. 37 # new ones 38 "serialize", 39 "serialization_fn", 40 "loading_fn", 41 "deserialize_fn", # new alternative to loading_fn 42 "assert_type", 43 "custom_typecheck_fn", 44 ) 45 46 def __init__( 47 self, 48 default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 49 default_factory: Union[ 50 Callable[[], Any], dataclasses._MISSING_TYPE 51 ] = dataclasses.MISSING, 52 init: bool = True, 53 repr: bool = True, 54 hash: Optional[bool] = None, 55 compare: bool = True, 56 # TODO: add field for custom comparator (such as serializing) 57 metadata: Optional[types.MappingProxyType] = None, 58 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 59 serialize: bool = True, 60 serialization_fn: Optional[Callable[[Any], Any]] = None, 61 loading_fn: Optional[Callable[[Any], Any]] = None, 62 deserialize_fn: Optional[Callable[[Any], Any]] = None, 63 assert_type: bool = True, 64 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 65 ): 66 # TODO: should we do this check, or assume the user knows what they are doing? 67 if init and not serialize: 68 raise ValueError("Cannot have init=True and serialize=False") 69 70 # need to assemble kwargs in this hacky way so as not to upset type checking 71 super_kwargs: dict[str, Any] = dict( 72 default=default, 73 default_factory=default_factory, 74 init=init, 75 repr=repr, 76 hash=hash, 77 compare=compare, 78 kw_only=kw_only, 79 ) 80 81 if metadata is not None: 82 super_kwargs["metadata"] = metadata 83 else: 84 super_kwargs["metadata"] = types.MappingProxyType({}) 85 86 # special check, kw_only is not supported in python <3.9 and `dataclasses.MISSING` is truthy 87 if sys.version_info < (3, 10): 88 if super_kwargs["kw_only"] == True: # noqa: E712 89 raise ValueError("kw_only is not supported in python >=3.9") 90 else: 91 del super_kwargs["kw_only"] 92 93 # actually init the super class 94 super().__init__(**super_kwargs) # type: ignore[call-arg] 95 96 # now init the new fields 97 self.serialize: bool = serialize 98 self.serialization_fn: Optional[Callable[[Any], Any]] = serialization_fn 99 100 if loading_fn is not None and deserialize_fn is not None: 101 raise ValueError( 102 "Cannot pass both loading_fn and deserialize_fn, pass only one. ", 103 "`loading_fn` is the older interface and takes the dict of the class, ", 104 "`deserialize_fn` is the new interface and takes only the field's value.", 105 ) 106 self.loading_fn: Optional[Callable[[Any], Any]] = loading_fn 107 self.deserialize_fn: Optional[Callable[[Any], Any]] = deserialize_fn 108 109 self.assert_type: bool = assert_type 110 self.custom_typecheck_fn: Optional[Callable[[type], bool]] = custom_typecheck_fn 111 112 @classmethod 113 def from_Field(cls, field: dataclasses.Field) -> "SerializableField": 114 """copy all values from a `dataclasses.Field` to new `SerializableField`""" 115 return cls( 116 default=field.default, 117 default_factory=field.default_factory, 118 init=field.init, 119 repr=field.repr, 120 hash=field.hash, 121 compare=field.compare, 122 metadata=field.metadata, 123 kw_only=getattr(field, "kw_only", dataclasses.MISSING), # for python <3.9 124 serialize=field.repr, # serialize if it's going to be repr'd 125 serialization_fn=None, 126 loading_fn=None, 127 deserialize_fn=None, 128 )
extension of dataclasses.Field
with additional serialization properties
46 def __init__( 47 self, 48 default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 49 default_factory: Union[ 50 Callable[[], Any], dataclasses._MISSING_TYPE 51 ] = dataclasses.MISSING, 52 init: bool = True, 53 repr: bool = True, 54 hash: Optional[bool] = None, 55 compare: bool = True, 56 # TODO: add field for custom comparator (such as serializing) 57 metadata: Optional[types.MappingProxyType] = None, 58 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 59 serialize: bool = True, 60 serialization_fn: Optional[Callable[[Any], Any]] = None, 61 loading_fn: Optional[Callable[[Any], Any]] = None, 62 deserialize_fn: Optional[Callable[[Any], Any]] = None, 63 assert_type: bool = True, 64 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 65 ): 66 # TODO: should we do this check, or assume the user knows what they are doing? 67 if init and not serialize: 68 raise ValueError("Cannot have init=True and serialize=False") 69 70 # need to assemble kwargs in this hacky way so as not to upset type checking 71 super_kwargs: dict[str, Any] = dict( 72 default=default, 73 default_factory=default_factory, 74 init=init, 75 repr=repr, 76 hash=hash, 77 compare=compare, 78 kw_only=kw_only, 79 ) 80 81 if metadata is not None: 82 super_kwargs["metadata"] = metadata 83 else: 84 super_kwargs["metadata"] = types.MappingProxyType({}) 85 86 # special check, kw_only is not supported in python <3.9 and `dataclasses.MISSING` is truthy 87 if sys.version_info < (3, 10): 88 if super_kwargs["kw_only"] == True: # noqa: E712 89 raise ValueError("kw_only is not supported in python >=3.9") 90 else: 91 del super_kwargs["kw_only"] 92 93 # actually init the super class 94 super().__init__(**super_kwargs) # type: ignore[call-arg] 95 96 # now init the new fields 97 self.serialize: bool = serialize 98 self.serialization_fn: Optional[Callable[[Any], Any]] = serialization_fn 99 100 if loading_fn is not None and deserialize_fn is not None: 101 raise ValueError( 102 "Cannot pass both loading_fn and deserialize_fn, pass only one. ", 103 "`loading_fn` is the older interface and takes the dict of the class, ", 104 "`deserialize_fn` is the new interface and takes only the field's value.", 105 ) 106 self.loading_fn: Optional[Callable[[Any], Any]] = loading_fn 107 self.deserialize_fn: Optional[Callable[[Any], Any]] = deserialize_fn 108 109 self.assert_type: bool = assert_type 110 self.custom_typecheck_fn: Optional[Callable[[type], bool]] = custom_typecheck_fn
112 @classmethod 113 def from_Field(cls, field: dataclasses.Field) -> "SerializableField": 114 """copy all values from a `dataclasses.Field` to new `SerializableField`""" 115 return cls( 116 default=field.default, 117 default_factory=field.default_factory, 118 init=field.init, 119 repr=field.repr, 120 hash=field.hash, 121 compare=field.compare, 122 metadata=field.metadata, 123 kw_only=getattr(field, "kw_only", dataclasses.MISSING), # for python <3.9 124 serialize=field.repr, # serialize if it's going to be repr'd 125 serialization_fn=None, 126 loading_fn=None, 127 deserialize_fn=None, 128 )
copy all values from a dataclasses.Field
to new SerializableField
188def serializable_field( 189 *_args, 190 default: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 191 default_factory: Union[Any, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 192 init: bool = True, 193 repr: bool = True, 194 hash: Optional[bool] = None, 195 compare: bool = True, 196 metadata: Optional[types.MappingProxyType] = None, 197 kw_only: Union[bool, dataclasses._MISSING_TYPE] = dataclasses.MISSING, 198 serialize: bool = True, 199 serialization_fn: Optional[Callable[[Any], Any]] = None, 200 deserialize_fn: Optional[Callable[[Any], Any]] = None, 201 assert_type: bool = True, 202 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 203 **kwargs: Any, 204) -> Any: 205 """Create a new `SerializableField` 206 207 ``` 208 default: Sfield_T | dataclasses._MISSING_TYPE = dataclasses.MISSING, 209 default_factory: Callable[[], Sfield_T] 210 | dataclasses._MISSING_TYPE = dataclasses.MISSING, 211 init: bool = True, 212 repr: bool = True, 213 hash: Optional[bool] = None, 214 compare: bool = True, 215 metadata: types.MappingProxyType | None = None, 216 kw_only: bool | dataclasses._MISSING_TYPE = dataclasses.MISSING, 217 # ---------------------------------------------------------------------- 218 # new in `SerializableField`, not in `dataclasses.Field` 219 serialize: bool = True, 220 serialization_fn: Optional[Callable[[Any], Any]] = None, 221 loading_fn: Optional[Callable[[Any], Any]] = None, 222 deserialize_fn: Optional[Callable[[Any], Any]] = None, 223 assert_type: bool = True, 224 custom_typecheck_fn: Optional[Callable[[type], bool]] = None, 225 ``` 226 227 # new Parameters: 228 - `serialize`: whether to serialize this field when serializing the class' 229 - `serialization_fn`: function taking the instance of the field and returning a serializable object. If not provided, will iterate through the `SerializerHandler`s defined in `muutils.json_serialize.json_serialize` 230 - `loading_fn`: function taking the serialized object and returning the instance of the field. If not provided, will take object as-is. 231 - `deserialize_fn`: new alternative to `loading_fn`. takes only the field's value, not the whole class. if both `loading_fn` and `deserialize_fn` are provided, an error will be raised. 232 - `assert_type`: whether to assert the type of the field when loading. if `False`, will not check the type of the field. 233 - `custom_typecheck_fn`: function taking the type of the field and returning whether the type itself is valid. if not provided, will use the default type checking. 234 235 # Gotchas: 236 - `loading_fn` takes the dict of the **class**, not the field. if you wanted a `loading_fn` that does nothing, you'd write: 237 238 ```python 239 class MyClass: 240 my_field: int = serializable_field( 241 serialization_fn=lambda x: str(x), 242 loading_fn=lambda x["my_field"]: int(x) 243 ) 244 ``` 245 246 using `deserialize_fn` instead: 247 248 ```python 249 class MyClass: 250 my_field: int = serializable_field( 251 serialization_fn=lambda x: str(x), 252 deserialize_fn=lambda x: int(x) 253 ) 254 ``` 255 256 In the above code, `my_field` is an int but will be serialized as a string. 257 258 note that if not using ZANJ, and you have a class inside a container, you MUST provide 259 `serialization_fn` and `loading_fn` to serialize and load the container. 260 ZANJ will automatically do this for you. 261 262 # TODO: `custom_value_check_fn`: function taking the value of the field and returning whether the value itself is valid. if not provided, any value is valid as long as it passes the type test 263 """ 264 assert len(_args) == 0, f"unexpected positional arguments: {_args}" 265 return SerializableField( 266 default=default, 267 default_factory=default_factory, 268 init=init, 269 repr=repr, 270 hash=hash, 271 compare=compare, 272 metadata=metadata, 273 kw_only=kw_only, 274 serialize=serialize, 275 serialization_fn=serialization_fn, 276 deserialize_fn=deserialize_fn, 277 assert_type=assert_type, 278 custom_typecheck_fn=custom_typecheck_fn, 279 **kwargs, 280 )
Create a new SerializableField
default: Sfield_T | dataclasses._MISSING_TYPE = dataclasses.MISSING,
default_factory: Callable[[], Sfield_T]
| dataclasses._MISSING_TYPE = dataclasses.MISSING,
init: bool = True,
repr: bool = True,
hash: Optional[bool] = None,
compare: bool = True,
metadata: types.MappingProxyType | None = None,
kw_only: bool | dataclasses._MISSING_TYPE = dataclasses.MISSING,
# ----------------------------------------------------------------------
# new in `SerializableField`, not in `dataclasses.Field`
serialize: bool = True,
serialization_fn: Optional[Callable[[Any], Any]] = None,
loading_fn: Optional[Callable[[Any], Any]] = None,
deserialize_fn: Optional[Callable[[Any], Any]] = None,
assert_type: bool = True,
custom_typecheck_fn: Optional[Callable[[type], bool]] = None,
new Parameters:
serialize
: whether to serialize this field when serializing the class'serialization_fn
: function taking the instance of the field and returning a serializable object. If not provided, will iterate through theSerializerHandler
s defined inmuutils.json_serialize.json_serialize
loading_fn
: function taking the serialized object and returning the instance of the field. If not provided, will take object as-is.deserialize_fn
: new alternative toloading_fn
. takes only the field's value, not the whole class. if bothloading_fn
anddeserialize_fn
are provided, an error will be raised.assert_type
: whether to assert the type of the field when loading. ifFalse
, will not check the type of the field.custom_typecheck_fn
: function taking the type of the field and returning whether the type itself is valid. if not provided, will use the default type checking.
Gotchas:
loading_fn
takes the dict of the class, not the field. if you wanted aloading_fn
that does nothing, you'd write:
class MyClass:
my_field: int = serializable_field(
serialization_fn=lambda x: str(x),
loading_fn=lambda x["my_field"]: int(x)
)
using deserialize_fn
instead:
class MyClass:
my_field: int = serializable_field(
serialization_fn=lambda x: str(x),
deserialize_fn=lambda x: int(x)
)
In the above code, my_field
is an int but will be serialized as a string.
note that if not using ZANJ, and you have a class inside a container, you MUST provide
serialization_fn
and loading_fn
to serialize and load the container.
ZANJ will automatically do this for you.