from abc import ABC from datetime import datetime from decimal import Decimal import json import re import sys, traceback from typing import Dict, List, Set, Any, Union, Optional, Collection import unittest from uuid import uuid4, UUID from pyson.Deserializer import Deserializer from pyson.Serializer import Serializer from pyson.JsonSerialize import JsonSerialize from pyson.JsonDeserialize import JsonDeserialize from pyson.JsonGetter import JsonGetter from pyson.JsonSubTypes import JsonSubTypes from pyson.JsonTypeInfo import Id, As from pyson.JsonTypeInfo import JsonTypeInfo from pyson.JsonValue import JsonValue from pyson.ObjectMapper import ObjectMapper from pickle import NONE from test.MyDeserializer import ValueDeserializer2 # deserializer DROPS FIRST CHAR from string and assumes rest is an int. # Then returns Simple(int) class ValueDeserializer(Deserializer): def __hash__(self): return hash(self.geta()) def deserialize(self, data:object, clas: object)-> object: if type(data)!=str: raise ValueError("Expected str starting with '$', got "+str(data)) return Simple(int(data[1:])) # serializes Simple object, just prefixing its value (as string) with "$" class ValueSerializer(Serializer): def serialize(self, obj:object)-> object: if not isinstance(obj, Simple): raise ValueError("Expected Dimple object") return "$" + str(obj.geta()) class Basic: def __init__(self, v:float): self._v=v def __eq__(self, other): return isinstance(other, self.__class__) and \ self._v==other._v def getV(self): return self._v def __repr__(self): return "Basic:"+str(self._v) def __hash__(self): return hash(self._v) @JsonDeserialize(ValueDeserializer) @JsonSerialize(ValueSerializer) 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 __repr__(self): return "Simple:"+str(self._a) def __hash__(self): return hash(self._a) @JsonDeserialize(ValueDeserializer2) class Simple2: 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 __repr__(self): return self._name+","+str(self._a) #None cancels out the existing deserializer. # parsing with str should now fail. @JsonDeserialize(None) class Simple3 (Simple): pass class MyList: def __init__(self, data: List[Simple] ): self.data=data def getData(self)->List[Simple]: return self.data class BasicDict: def __init__(self, data: Dict[Basic,Basic] ): self.data=data def getData(self)->Dict[Basic,Basic]: return self.data def __repr__(self): return "BasicDict:"+str(self.data) def __eq__(self, other): return isinstance(other, self.__class__) and \ self.data==other.data class SimpleDict: def __init__(self, data: Dict[Simple,Simple] ): self.data=data def getData(self)->Dict[Simple,Simple]: return self.data def __eq__(self, other): return isinstance(other, self.__class__) and \ self.data==other.data def __repr__(self): return "SimpleDict:"+str(self.data) class MyOptionalString: def __init__(self, s:Optional[str]): self.data:Optional[str] = s def getData(self)->Optional[str]: return self.data def __eq__(self, other): return isinstance(other, self.__class__) and \ self.data==other.data def __repr__(self): return "MyOptionalString:"+str(self.data) class SimpleA(Simple): pass class SimpleB(Simple): pass class DeserializerTest(unittest.TestCase): ''' Test a lot of back-and-forth cases. FIXME Can we make this a parameterized test? ''' pyson=ObjectMapper() def testDeserialize(self): objson= "$12" self.assertEqual(Simple(12), self.pyson.parse(objson, Simple)) def testExternalDeserialize2(self): objson= "$13" self.assertEqual(13, self.pyson.parse(objson, Simple2)) def testExternalDeserialize3(self): objson= "$13" self.assertRaises(ValueError, lambda:self.pyson.parse(objson, Simple3)) def testDeserializeMyList(self): print(self.pyson.toJson(MyList([12,13]))) # the json we provide is NOT the proper json but a STRING. # This triggers a fallback mechanism that tries to parse the string as json. objson={"data": ["$12", "$13"]} res = self.pyson.parse(objson, MyList) print(res) self.assertEqual([Simple(12),Simple(13)], res.data) def testDeserializeCollection(self): objson=[1,2,3] # this checks that pyson can parse a list as Collection. # The result is a simple list like in java/jackson. # Collection without typing info means Collectino[Any] res = self.pyson.parse(objson, Collection) print(res) self.assertEqual([1,2,3], res) def testSerializeBasicDict(self): ''' Basic object keys. Special (de)serializer should kick in #190 ''' d= BasicDict( { Basic(1.):Basic(2.), Basic(3.): Basic(4.) } ) objson={"data": {"{\"v\": 1.0}": {"v": 2.0}, "{\"v\": 3.0}": {"v": 4.0}}} dump=json.dumps(self.pyson.toJson(d)); print(dump) # self.assertEqual(objson, dump); res=self.pyson.parse(objson, BasicDict) print("res="+str(res)) self.assertEqual(d, res) def testSerializeSimpleDictCustomSerializer(self): d= SimpleDict( { Simple(1):Simple(2), Simple(3): Simple(4) } ) # The keys need extra quotes, they are deserialied by # our special deserializer that parses the string as json, # and json requires double quotes around its strings. objson = {"data": {'"$1"': "$2", '"$3"': "$4"}} obj=self.pyson.toJson(d) print(json.dumps(obj)) self.assertEqual(objson, obj) res = self.pyson.parse(objson, SimpleDict) print("res="+str(res)) self.assertEqual(d, res) def testDeserializeOptString(self): objson:dict={'s':None} res = self.pyson.parse(objson, MyOptionalString) print(res) self.assertEqual(MyOptionalString(None), res) objson={'s':"something"} res = self.pyson.parse(objson, MyOptionalString) print(res) self.assertEqual(MyOptionalString("something"), res) def testSerializeMixedList(self): # see #296 list:List[Simple] = [SimpleA(1), SimpleB(1)] print(self.pyson.toJson(list))