Package: jdataclass
jdataclass — Custom dataclass Serialization/Deserializationjdataclass is a Python module that extends the functionality of Python's native dataclasses by allowing custom mappings for reading from dataclass objects and writing to other objects during serialization and deserialization. With jdataclass, you can easily control how data is mapped between your dataclass and external data sources like JSON, databases, or other data formats.Install the packageYou can install jdataclass using pip:pipinstalljdataclassUsageHere's a guide on how to use jdataclass in your Python projects.Defining a DataclassCreate a dataclass as you normally would, and use jfield and jproperty annotations to specify custom mappings for your fields and properties.fromdataclassesimportdataclass,fieldfromjdataclassimportjfield@dataclassclassPerson:first_name:str=jfield(path="firstName")last_name:str=jfield(path="lastName")age:int=field(default=0)In the example above, we have defined a dataclass,Person. This dataclass usesjfieldannotations to specify custom field mappings.Serialization and DeserializationYou can now serialize and deserialize your dataclasses using jdataclass:Serialization (to JSON)>>>fromjdataclassimportasdict>>>person=Person(first_name="John",last_name="Doe",age=30)>>>asdict(person)# Serialize Person to JSON{'firstName':'John','lastName':'Doe','age':30}Deserialization (from JSON)>>>fromjdataclassimportcreate>>>json_data={..."firstName":"Jane",..."lastName":"Doe",..."age":25...}>>>create(Person,json_data)# Deserialize JSON to PersonPerson(first_name='Jane',last_name='Doe',age=25)Module contentsjdataclass provides several functions to work with custom mappings and dataclass objects:JField: Holds options for a field, used during serialization and deserialization.JFieldOptions: dataclass that is added to a field metadata and is used to create aJField.JProperty: A descriptor that replaces the getters and setters for a jproperty.jfield: Factory function to create a Field with the metadata that JField needs.jfields: Function that returns all jfields defined of a given class.jproperty: A decorator to specify custom property mappings for a dataclass property.jproperties: A function that returns all jproperties of a given class.create: Create a dataclass object from a dictionary of data using custom mappings.asdict: Serialize a dataclass object to a dictionary using custom mappings.convert: Converts one dataclass to another.ExamplesjpropertySometimes we need to map the result of a function to a field in the serialized object or have custom functionality when deserializing a field.For these scenarios jproperties allow us to define read-only or read-write operations that happen during deserialization and/or serialization.Consider the following JSON response from thePurview Azure SDK:{"friendlyName":"SubCollection","name":"000001","parentCollection":{"referenceName":"000000","type":"CollectionReference"}}The propertyparentCollectioncontains thenameof the parent collection and atypethat is fixed toCollectionReference.We can create a jdataclass that represents these mappings as follows:fromdataclassesimportdataclassfromjdataclassimportjfield,jpropertyfromtypingimportAny,Optional@dataclassclassPurviewCollection:name:strfriendly_name:str=jfield(path="friendlyName")parent_name:Optional[str]=jfield(path="parentCollection.referenceName",default=None,)@jproperty(path="parentCollection.type")defcollection_reference(self):return"CollectionReference"...Deserialization (from JSON)>>>fromjdataclassimportcreate>>>json_data={'name':'000001','friendlyName':'SubCollection','parentCollection':{'referenceName':'000000','type':'CollectionReference'}}>>>create(PurviewCollection,json_data)# Deserialize JSON to PurviewCollectionPurviewCollection(name='000001',friendly_name='SubCollection',parent_name='000000')Serialization (to JSON)>>>fromjdataclassimportasdict>>>collection=PurviewCollection(name='000001',friendly_name='SubCollection',parent_name='000000')>>>asdict(collection)# Serialize PurviewCollection to JSON{'name':'000001','friendlyName':'SubCollection','parentCollection':{'referenceName':'000000','type':'CollectionReference'}}Post serialization (__post_asdict__)Sometimes we need to interfere with the serialization process for custom post processing of the generated dict.Considering the previous example, assume a collection that has no parent. We would like the generated JSON to drop theparentCollectionproperty entirelly.
So, let's change the class a little and add a new method called__post_asdict__...@dataclassclassPurviewCollection:...def__post_asdict__(self,data:dict[str,Any])->dict[str,Any]:ifself.parent_nameisNone:deldata["parentCollection"]returndataSerialization (to JSON)>>>fromjdataclassimportasdict>>>collection=PurviewCollection(name='000000',friendly_name='Root',parent_name=None)>>>asdict(collection)# Serialize PurviewCollection to JSON{'name':'000000','friendlyName':'Root'}__post_asdict__method is called just after all fields and properties have been initialized.Post deserialization (__post_init__)Thedataclassnative__post_init__method can be used for post deserialization.Consider the previous example, collections from thePurviewapi are returned with only thereferenceNamefor the parent collection. Let's say we want to make the hierarchy explicit by combining collections byparent_name. We can accomplish this after serialization with somthing like the following:Response fromlist_collectionsjson_data={"value":[{"friendlyName":"Root","name":"000000",},{"friendlyName":"SubCollection","name":"000001","parentCollection":{"referenceName":"000000","type":"CollectionReference",},},],}...@dataclassclassPurviewCollection:...collections:list["PurviewCollection"]=jfield(init=False,default_factory=list,)parent:Optional["PurviewCollection"]=jfield(init=False,default=None,)...@dataclassclassPurviewResponse:root:Optional[PurviewCollection]=jfield(init=False)collections:list[PurviewCollection]=jfield(path="value",default_factory=list,)def__post_init__(self):by_name={c.name:cforcinself.collections}forcinself.collections:ifc.parent_nameand(parent:=by_name.get(c.parent_name)):c.parent=parentparent.collections.append(c)else:self.root=cDeserialization (from JSON)>>>fromjdataclassimportcreate>>>response=create(PurviewResponse,json_data)# Deserialize JSON to PurviewResponse>>>response.rootPurviewCollection(name='000000',friendly_name='Root',collections=[PurviewCollection(name='000001',friendly_name='SubCollection',parent_name='000000')],parent_name=None)ConvertSometimes we need to convert a dataclass that maps an external api to an internal mapper to save the data in a different format.Consider the previous example, now we would like to store the contents of aPurviewCollectionas a different format like the following:@dataclassclassLocalCollection:name:strfriendly_name:str=jfield(path="friendlyName")collections:list["LocalCollection"]=jfield(default_factory=list)parent:Optional["LocalCollection"]=jfield(parent_ref=True,default=None,)@propertydefparent_name(self)->Optional[str]:returnself.parentandself.parent.nameWe can easily convert aPurviewCollectionto such class by using theconvertfunction. It will copy all fields defined in theLocalCollectionfrom the given instance ofPurviewCollection.>>>fromjdataclassimportconvert>>>response=create(PurviewResponse,json_data)# Deserialize JSON to PurviewResponse>>>collection=convert(response.root,LocalCollection)>>>collectionLocalCollection(name='000000',friendly_name='Root',collections=[LocalCollection(name='000001',friendly_name='SubCollection',collections=[],parent=...)],parent=None)>>>asdict(collection){'name':'000000','friendlyName':'Root','collections':[{'name':'000001','friendlyName':'SubCollection','collections':[]}]}Contributions and IssuesIf you encounter any issues, have questions, or want to contribute to jdataclass, please visit ourGitHub repository. We welcome your feedback and contributions.Licensejdataclass is licensed under the MIT License. See theLICENSEfile for more details.Thank you for using jdataclass! We hope this library simplifies the process of custom data mappings for your dataclass objects.