source: pyson/test/ObjectMapperTest.py@ 141

Last change on this file since 141 was 141, checked in by wouter, 3 years ago

#60 change packson3 to pyson to avoid suggesting we're implementing jackson

File size: 10.2 KB
Line 
1
2import re
3import unittest
4import sys, traceback
5from pyson.ObjectMapper import ObjectMapper
6from pyson.JsonSubTypes import JsonSubTypes
7from pyson.JsonTypeInfo import JsonTypeInfo
8from pyson.JsonTypeInfo import Id,As
9from typing import Dict,List,Set
10import json
11
12class Props:
13 '''
14 compound class with properties, used for testing
15 '''
16 def __init__(self, age:int, name:str):
17 if age<0:
18 raise ValueError("age must be >0, got "+str(age))
19 self._age=age
20 self._name=name;
21 def __str__(self):
22 return self._name+","+str(self._age)
23 def getage(self):
24 return self._age
25 def getname(self):
26 return self._name
27 def __eq__(self, other):
28 return isinstance(other, self.__class__) and \
29 self._name==other._name and self._age==other._age
30
31@JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
32class Simple:
33 def __init__(self, a:int):
34 self._a=a
35 def geta(self)->int:
36 return self._a
37 def __eq__(self, other):
38 return isinstance(other, self.__class__) and \
39 self._a==other._a
40 def __str__(self):
41 return self._name+","+str(self._a)
42
43
44class SimpleWithHash(Simple):
45 def __hash__(self):
46 return hash(self.geta())
47
48# define abstract root class
49# These need to be reachable globally for reference
50@JsonSubTypes(["test.ObjectMapperTest.Bear"])
51@JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
52class Animal:
53 pass
54
55
56class Bear(Animal):
57 def __init__(self, props:Props):
58 self._props=props
59
60 def __str__(self):
61 return "Bear["+str(self._props)+"]"
62
63 def getprops(self):
64 return self._props
65 def __eq__(self, other):
66 return isinstance(other, self.__class__) and \
67 self._props==other._props
68
69
70# our parser supports non-primitive keys but python dict dont.
71# therefore the following fails even before we can start testing our code...
72# we need to create a hashable dict to get around ths
73class mydict(dict):
74 def __hash__(self, *args, **kwargs):
75 return 1
76
77
78class ObjectMapperTest(unittest.TestCase):
79 def testPrimitives(self):
80 pyson=ObjectMapper()
81
82 res=pyson.parse(3, int)
83 self.assertEquals(3, res)
84
85
86 # this throws correct,
87 self.assertRaises(ValueError, lambda:pyson.parse(3, str))
88
89 # this throws correct,
90 self.assertRaises(ValueError, lambda:pyson.parse("ja", int))
91
92 #DEMO with nested classes of different types.
93 res=pyson.parse('three', str)
94 print(res, type(res))
95
96 pyson.parse(3.0, float)
97 pyson.parse(3.1, float)
98 pyson.parse(3j, complex)
99 pyson.parse(range(6), range)
100 pyson.parse(True, bool)
101 pyson.parse(False, bool)
102 pyson.parse(b"Hello", bytes)
103 pyson.parse(bytearray(b'\x00\x00\x00\x01'), bytearray)
104
105
106 def testProps(self):
107 pyson=ObjectMapper()
108 propsjson={'age': 10, 'name': 'pietje'}
109 props=Props(10, "pietje")
110 self.assertEquals(propsjson,pyson.toJson(props))
111 self.assertEquals(props, pyson.parse(propsjson, Props))
112
113 def testParseDeepError(self):
114 pyson=ObjectMapper()
115 propsjson={'age': 10, 'name': 12}
116 try:
117 pyson.parse(propsjson, Props)
118 raise AssertionError("parser did not throw")
119 except ValueError as e:
120 # we catch this to assure the exception contains
121 # both top error and details.
122 print("received error "+str(e))
123 self.assertTrue(str(e).find("Error parsing"))
124 self.assertTrue(str(e).find("ValueError"))
125 self.assertTrue(str(e).find("expected"))
126
127
128 def testEmpty(self):
129 pyson=ObjectMapper()
130
131 class EmptyClass:
132 def __init__(self):
133 pass
134 def __eq__(self, other):
135 return isinstance(other, self.__class__)
136
137 obj=EmptyClass()
138 print(pyson.toJson(obj))
139 res=pyson.parse({}, EmptyClass)
140 self.assertEqual(obj, res)
141
142 def testSubType(self):
143 pyson=ObjectMapper()
144
145 class Cat():
146 def __init__(self, props:Props):
147 self._props=props
148
149 def __str__(self):
150 return "Cat["+str(self._props)+"]"
151
152 def getprops(self):
153 return self._props
154
155 obj=Cat(Props(1,'bruno'))
156 print(pyson.toJson(obj))
157
158
159 bson={'props':{'age':1, 'name':'bruno'}}
160 res=pyson.parse(bson, Cat)
161 print(res, type(res))
162 self.assertEquals(type(res.getprops()), Props)
163
164
165
166 def testInheritance(self):
167 pyson=ObjectMapper()
168
169
170 obj=Bear(Props(1,'bruno'))
171 res=pyson.toJson(obj)
172 print("result:"+str(res))
173 bson={'Bear': {'props': {'age': 1, 'name': 'bruno'}}}
174 self.assertEquals(bson, res)
175
176 res=pyson.parse(bson, Animal)
177 print("Deserialized an Animal! -->"+str(res))
178 self. assertEqual(obj, res)
179
180 def testUntypedList(self):
181 class Prim:
182 def __init__(self, a:list):
183 self._a=a
184 def geta(self)->list:
185 return self._a
186
187 pyson=ObjectMapper()
188 obj=Prim([1,2])
189 objson = {'a':[1,2]}
190
191 self.assertEqual(objson, pyson.toJson(obj))
192
193 self.assertRaises(ValueError, lambda:pyson.parse(objson, Prim))
194
195 def testTypedList(self):
196 '''
197 deserializes typed list contained in another object
198 '''
199 class Prim:
200 def __init__(self, a:List[str]):
201 self._a=a
202 def geta(self)->List[str]:
203 return self._a
204 def __eq__(self, other):
205 return isinstance(other, self.__class__) and \
206 self._a==other._a
207
208 pyson=ObjectMapper()
209 obj=Prim(["x","y"])
210 objson = {'a':["x","y"]}
211
212 self.assertEqual(objson, pyson.toJson(obj))
213 self.assertEqual(obj, pyson.parse(objson, Prim))
214
215 def testTypedListDirect(self):
216 '''
217 deserializes typed list directly
218 '''
219
220 pyson=ObjectMapper()
221 obj=["x","y"]
222 objson = ["x","y"]
223
224 self.assertEqual(objson, pyson.toJson(obj))
225 self.assertEqual(obj, pyson.parse(objson, List[str]))
226
227 def testTypedListOfObjMissingAnnotation(self):
228 class Prim:
229 def __init__(self, a:int):
230 self._a=a
231 def geta(self)->int:
232 return self._a
233 def __eq__(self, other):
234 return isinstance(other, self.__class__) and \
235 self._a==other._a
236 pyson=ObjectMapper()
237 obj=[Prim(1),Prim(3)]
238 objson = [{"Prim":{'a':1}},{"Prim":{'a':3}}]
239 self.assertRaises(ValueError, lambda:pyson.toJson(obj))
240 # object misses annotation, therefore this will try to parse
241 # Prim objects without header here.
242 self.assertRaises(ValueError, lambda:pyson.parse(objson, List[Prim]))
243
244 def testTypedListOfObj(self):
245 @JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
246 class Prim:
247 def __init__(self, a:int):
248 self._a=a
249 def geta(self)->int:
250 return self._a
251 def __eq__(self, other):
252 return isinstance(other, self.__class__) and \
253 self._a==other._a
254
255 pyson=ObjectMapper()
256 obj=[Prim(1),Prim(3)]
257 objson = [{"Prim":{'a':1}},{"Prim":{'a':3}}]
258 self.assertEqual(objson, pyson.toJson(obj))
259 self.assertEqual(obj, pyson.parse(objson, List[Prim]))
260
261 def testTypedSetOfObj(self):
262 @JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
263 class Prim:
264 def __init__(self, a:int):
265 self._a=a
266 def geta(self)->int:
267 return self._a
268 def __eq__(self, other):
269 return isinstance(other, self.__class__) and \
270 self._a==other._a
271
272 pyson=ObjectMapper()
273 obj=set([SimpleWithHash(1),SimpleWithHash(3)])
274 objson = [{"SimpleWithHash":{'a':1}},{"SimpleWithHash":{'a':3}}]
275 self.assertEqual(objson, pyson.toJson(obj))
276 parsedobj=pyson.parse(objson, Set[SimpleWithHash])
277 self.assertEqual(obj, parsedobj)
278
279
280 def testExpectListButGiveDict(self):
281 @JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
282 class Prim:
283 def __init__(self, a:int):
284 self._a=a
285 def geta(self)->int:
286 return self._a
287 def __eq__(self, other):
288 return isinstance(other, self.__class__) and \
289 self._a==other._a
290
291 pyson=ObjectMapper()
292 objson = { 'a':{"Prim":{'a':1}},'c':{"Prim":{'a':3}}}
293 # we request List but obj is a dict.
294 self.assertRaises(ValueError,lambda:pyson.parse(objson, List[Prim]))
295
296 def testSerializeDict(self):
297
298 pyson=ObjectMapper()
299 obj={'a':Simple(1),'c':Simple(3)}
300 objson = { 'a':{"Simple":{'a':1}},'c':{"Simple":{'a':3}}}
301 self.assertEqual(objson, pyson.toJson(obj))
302
303
304 def testTypedDictOfObj(self):
305 pyson=ObjectMapper()
306 obj={'a':Simple(1),'c':Simple(3)}
307 objson = { 'a':{"Simple":{'a':1}},'c':{"Simple":{'a':3}}}
308 self.assertEqual(obj, pyson.parse(objson, Dict[str,Simple]))
309 print("deserialized obj"+str(objson)+"="+str(obj))
310
311 def testTypedDictSimpleKey(self):
312 pyson=ObjectMapper()
313
314 key=mydict()
315 key["Simple"]={'a':1}
316 # simple is not hashable
317 objson = { key : 'a' }
318 self.assertRaises(ValueError,lambda:pyson.parse(objson, Dict[Simple,str]))
319
320 def testTypedDictSimpleKeyHashable(self):
321 pyson=ObjectMapper()
322 # key is now not primitive!
323 obj={SimpleWithHash(1):'a'}
324
325 # simple is not hashable
326 key=mydict()
327 key["SimpleWithHash"]={'a':1}
328 # simple is not hashable
329 objson = { key : 'a' }
330 self.assertEqual(obj, pyson.parse(objson, Dict[SimpleWithHash,str]))
331
332
Note: See TracBrowser for help on using the repository browser.