source: pyson/test/DeserializerTest.py@ 854

Last change on this file since 854 was 837, checked in by wouter, 6 months ago

#298 added test checking that deserializing a list with bad elements (wrong class) is caught

File size: 8.4 KB
Line 
1from __future__ import annotations
2from abc import ABC
3from datetime import datetime
4from decimal import Decimal
5import json
6import re
7import sys, traceback
8from typing import Dict, List, Set, Any, Union, Optional, Collection
9import unittest
10from uuid import uuid4, UUID
11
12from pyson.Deserializer import Deserializer
13from pyson.Serializer import Serializer
14from pyson.JsonSerialize import JsonSerialize
15from pyson.JsonDeserialize import JsonDeserialize
16from pyson.JsonGetter import JsonGetter
17from pyson.JsonSubTypes import JsonSubTypes
18from pyson.JsonTypeInfo import Id, As
19from pyson.JsonTypeInfo import JsonTypeInfo
20from pyson.JsonValue import JsonValue
21from pyson.ObjectMapper import ObjectMapper
22from pickle import NONE
23from test.MyDeserializer import ValueDeserializer2
24
25# deserializer DROPS FIRST CHAR from string and assumes rest is an int.
26# Then returns Simple(int)
27class ValueDeserializer(Deserializer):
28 def __hash__(self):
29 return hash(self.geta())
30 def deserialize(self, data:object, clas: object)-> object:
31 if type(data)!=str:
32 raise ValueError("Expected str starting with '$', got "+str(data))
33 return Simple(int(data[1:]))
34
35# serializes Simple object, just prefixing its value (as string) with "$"
36class ValueSerializer(Serializer):
37 def serialize(self, obj:object)-> object:
38 if not isinstance(obj, Simple):
39 raise ValueError("Expected Dimple object")
40 return "$" + str(obj.geta())
41
42class Basic:
43 def __init__(self, v:float):
44 self._v=v
45 def __eq__(self, other):
46 return isinstance(other, self.__class__) and \
47 self._v==other._v
48 def getV(self):
49 return self._v
50 def __repr__(self):
51 return "Basic:"+str(self._v)
52 def __hash__(self):
53 return hash(self._v)
54
55@JsonDeserialize(ValueDeserializer)
56@JsonSerialize(ValueSerializer)
57class Simple:
58 def __init__(self, a:int):
59 self._a=a
60 def geta(self)->int:
61 return self._a
62 def __eq__(self, other):
63 return isinstance(other, self.__class__) and \
64 self._a==other._a
65 def __repr__(self):
66 return "Simple:"+str(self._a)
67 def __hash__(self):
68 return hash(self._a)
69
70
71
72@JsonDeserialize(ValueDeserializer2)
73class Simple2:
74 def __init__(self, a:int):
75 self._a=a
76 def geta(self)->int:
77 return self._a
78 def __eq__(self, other):
79 return isinstance(other, self.__class__) and \
80 self._a==other._a
81 def __repr__(self):
82 return self._name+","+str(self._a)
83
84#None cancels out the existing deserializer.
85# parsing with str should now fail.
86@JsonDeserialize(None)
87class Simple3 (Simple):
88 pass
89
90
91class MyList:
92 def __init__(self, data: List[Simple] ):
93 self.data=data
94 def getData(self)->List[Simple]:
95 return self.data
96
97class BasicDict:
98 def __init__(self, data: Dict[Basic,Basic] ):
99 self.data=data
100 def getData(self)->Dict[Basic,Basic]:
101 return self.data
102 def __repr__(self):
103 return "BasicDict:"+str(self.data)
104 def __eq__(self, other):
105 return isinstance(other, self.__class__) and \
106 self.data==other.data
107
108
109class SimpleDict:
110 def __init__(self, data: Dict[Simple,Simple] ):
111 self.data=data
112 def getData(self)->Dict[Simple,Simple]:
113 return self.data
114 def __eq__(self, other):
115 return isinstance(other, self.__class__) and \
116 self.data==other.data
117 def __repr__(self):
118 return "SimpleDict:"+str(self.data)
119
120
121class MyOptionalString:
122 def __init__(self, s:Optional[str]):
123 self.data:Optional[str] = s
124 def getData(self)->Optional[str]:
125 return self.data
126 def __eq__(self, other):
127 return isinstance(other, self.__class__) and \
128 self.data==other.data
129 def __repr__(self):
130 return "MyOptionalString:"+str(self.data)
131
132
133@JsonSubTypes(['test.DeserializerTest.A','test.DeserializerTest.B'])
134@JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
135class Root:
136 def __init__(self, data: int ):
137 self.data:int=data
138 def getData(self)->int:
139 return self.data
140 def __eq__(self, other):
141 return isinstance(other, self.__class__) and \
142 self.data==other.data
143 def __repr__(self):
144 return type(self).__name__ + "/" + str(self.data)
145 def __hash__(self):
146 return 0
147
148
149class A(Root):
150 pass
151
152class B(Root):
153 pass
154
155
156
157class DeserializerTest(unittest.TestCase):
158 '''
159 Test a lot of back-and-forth cases.
160 FIXME Can we make this a parameterized test?
161 '''
162 pyson=ObjectMapper()
163 mixedList:List[Root] = [A(1), B(1)]
164 mixedlistjson:List = [{'A': {'data': 1}}, {'B': {'data': 1}}]
165
166 mixedDict:Dict[Root,int] = {A(1):1, B(1):2}
167 mixedDictJson:dict = {'{"A": {"data": 1}}': 1, '{"B": {"data": 1}}': 2}
168
169
170 def testDeserialize(self):
171 objson= "$12"
172 self.assertEqual(Simple(12), self.pyson.parse(objson, Simple))
173
174 def testExternalDeserialize2(self):
175 objson= "$13"
176 self.assertEqual(13, self.pyson.parse(objson, Simple2))
177
178
179 def testExternalDeserialize3(self):
180 objson= "$13"
181 self.assertRaises(ValueError, lambda:self.pyson.parse(objson, Simple3))
182
183 def testDeserializeMyList(self):
184 print(self.pyson.toJson(MyList([12,13])))
185
186 # the json we provide is NOT the proper json but a STRING.
187 # This triggers a fallback mechanism that tries to parse the string as json.
188 objson={"data": ["$12", "$13"]}
189 res = self.pyson.parse(objson, MyList)
190 print(res)
191 self.assertEqual([Simple(12),Simple(13)], res.data)
192
193 def testDeserializeCollection(self):
194 objson=[1,2,3]
195 # this checks that pyson can parse a list as Collection.
196 # The result is a simple list like in java/jackson.
197 # Collection without typing info means Collectino[Any]
198 res = self.pyson.parse(objson, Collection)
199 print(res)
200 self.assertEqual([1,2,3], res)
201
202
203 def testSerializeBasicDict(self):
204 '''
205 Basic object keys. Special (de)serializer should kick in #190
206 '''
207 d= BasicDict( { Basic(1.):Basic(2.), Basic(3.): Basic(4.) } )
208 objson={"data": {"{\"v\": 1.0}": {"v": 2.0}, "{\"v\": 3.0}": {"v": 4.0}}}
209 dump=json.dumps(self.pyson.toJson(d));
210 print(dump)
211 # self.assertEqual(objson, dump);
212 res=self.pyson.parse(objson, BasicDict)
213 print("res="+str(res))
214 self.assertEqual(d, res)
215
216
217 def testSerializeSimpleDictCustomSerializer(self):
218 d= SimpleDict( { Simple(1):Simple(2), Simple(3): Simple(4) } )
219
220 # The keys need extra quotes, they are deserialied by
221 # our special deserializer that parses the string as json,
222 # and json requires double quotes around its strings.
223 objson = {"data": {'"$1"': "$2", '"$3"': "$4"}}
224
225 obj=self.pyson.toJson(d)
226 print(json.dumps(obj))
227 self.assertEqual(objson, obj)
228
229 res = self.pyson.parse(objson, SimpleDict)
230 print("res="+str(res))
231 self.assertEqual(d, res)
232
233 def testDeserializeOptString(self):
234 objson:dict={'s':None}
235 res = self.pyson.parse(objson, MyOptionalString)
236 print(res)
237 self.assertEqual(MyOptionalString(None), res)
238
239 objson={'s':"something"}
240 res = self.pyson.parse(objson, MyOptionalString)
241 print(res)
242 self.assertEqual(MyOptionalString("something"), res)
243
244 def testSerializeMixedList(self):
245 # see #296
246 res=self.pyson.toJson(self.mixedList)
247 print(res)
248 self.assertEqual(self.mixedlistjson, res)
249
250 def testDeserializeMixedList(self):
251 # see #296
252 res=self.pyson.parse(self.mixedlistjson, List[Root])
253 print(res)
254 self.assertEqual(self.mixedList,res)
255
256 def testDeserializeBadList(self):
257 # see #298. This SHOULD fail because B is not subtype of A
258 self.assertRaises(ValueError, lambda:self.pyson.parse(self.mixedlistjson, List[A]))
259
260
261 def testSerializeMixedDict(self):
262 # see #296
263 res=self.pyson.toJson(self.mixedDict)
264 print(res)
265 self.assertEqual(self.mixedDictJson, res)
266
267 def testDeserializeMixedDict(self):
268 # see #296
269 print("testDeserializeMixedDict")
270 res=self.pyson.parse(self.mixedDictJson, Dict[Root,int])
271 print(res)
272 self.assertEqual(self.mixedDict,res)
273
274
Note: See TracBrowser for help on using the repository browser.