Version 28 (modified by wouter, 4 years ago) ( diff )

--

Pyson

Pyson converts between dicts/lists and python3 objects. It uses some annotations inspired by jackson.

install:

pip install https://tracinsy.ewi.tudelft.nl/pubtrac/Utilities/export/153/pyson/dist/pyson-1.0.0.tar.gz

or from your setup.py

    install_requires=[ "pyson@pip install https://tracinsy.ewi.tudelft.nl/pubtrac/Utilities/export/142/pyson/dist/pyson-1.0.0.tar.gz"],

NOTICE you may want to use the latest version. Check packson3/dist for the latest.

WARNING: there exists another other python library named "pyson", which is an entirely different project.

The basic version determines the types for (de)serialization from the __init__ function in the involved classes. Polymorphism is supported, so derived classes can (de)serialized from a superclass.

Basic Mechanism

The basic mechanism for pyson is to look at the __init__ function of the class under serialization. The arguments in the __init__ function are matched to the json fields. The arguments in the __init__ must be fully typed, and these types are used to determine how to interpret the json content.

Deserialization

For Deserialization of a json object, it goes like this

  • if the target class has @JsonSubtypes,
    • check which the subclasses are and what their ID is.
    • Determine the actual class contained in the json
    • "unwrap" the json, so that we have the remaining json to deserialize the actual class
  • If it's not a JsonSubtypes, then the actual class is the requested target class
  • now that the actual class is known,
    • the parameter names and param-classes from the __init__ function of the actual class are taken
    • for each of the parameters, recursively deserialize the json value for that parameter, using the param-class as targetclass.
    • call the constructor of the target class, using the parsed json for each parameter

Serialization

Serialization of an object is much more straightforward.

  • Create a json dict, with keys the arguments of the init function of the object and the value the serialized value returned by the getter (also considering @JsonGetter)
  • If the object is an instance of a class with @JsonSubtypes, add/wrap the json with class info according to the @JsonTypeInfo

Examples

See pyson/test/ObjectMapperTest.py for many examples.

A simple example, deserializng a dict with objects

from pyson.ObjectMapper import ObjectMapper
from pyson.JsonTypeInfo import JsonTypeInfo
from pyson.JsonTypeInfo import Id,As
from typing import Dict

@JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
class Simple:
    def __init__(self, a:int):
        self._a=a
    def geta(self)->int:
        return self._a
    def __eq__(self, other):
        return isinstance(other, self.__class__) and \
            self._a==other._a
    def __str__(self):
        return self._name+","+str(self._a)

pyson=ObjectMapper()
objson = { 'a':{"Simple":{'a':1}},'c':{"Simple":{'a':3}}}
obj=pyson.parse(objson, Dict[str,Simple])
obj['a'].geta()

A complex example showing many things at once

from pyson.ObjectMapper import ObjectMapper
from pyson.JsonSubTypes import JsonSubTypes
from pyson.JsonTypeInfo import JsonTypeInfo
from pyson.JsonTypeInfo import Id,As
from typing import Dict,List,Set
import json

class Props:
    '''
    compound class with properties, used for testing 
    '''
    def __init__(self, age:int, name:str):
        if age<0:
            raise ValueError("age must be >0, got "+str(age))
        self._age=age
        self._name=name;
    def __str__(self):
        return self._name+","+str(self._age)
    def getage(self):
        return self._age
    def getname(self):
        return self._name
    def __eq__(self, other):
        return isinstance(other, self.__class__) and \
            self._name==other._name and self._age==other._age


@JsonSubTypes(["__main__.Bear"])
@JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
class Animal:
    pass
    
    
class Bear(Animal):
    def __init__(self, props:Props):
        self._props=props
            
    def __str__(self):
        return "Bear["+str(self._props)+"]"
        
    def getprops(self):
        return self._props
    def __eq__(self, other):
        return isinstance(other, self.__class__) and \
            self._props==other._props

pyson=ObjectMapper()


obj=Bear(Props(1,'bruno'))
res=pyson.toJson(obj)
print("result:"+str(res))
bson={'Bear': {'props': {'age': 1, 'name': 'bruno'}}}
res=pyson.parse(bson, Animal)
print("Deserialized an Animal! -->"+str(res))

NOTICE: our code allows you to use objects as keys, as python does allow this. However json requires strings as keys.

And an example using @JsonGetter

from pyson.ObjectMapper import ObjectMapper
from pyson.JsonGetter import JsonGetter
class Getter:
    def __init__(self, a:int):
        self._a=a
    @JsonGetter("a")
    def getValue(self):
        return self._a

getter=Getter(17)
pyson=ObjectMapper()
pyson.toJson(getter)

Add/Extend annotations at runtime

You can also add/extend existing annotations at runtime. You just need to update the class and function attributes. For now, check the source code for details.

Note: See TracWiki for help on using the wiki.