1 | import json |
2 | import time |
3 | |
4 | enum JobTitle { |
5 | manager |
6 | executive |
7 | worker |
8 | } |
9 | |
10 | struct Employee { |
11 | name string |
12 | age int |
13 | salary f32 |
14 | title JobTitle |
15 | } |
16 | |
17 | fn test_simple() { |
18 | x := Employee{'Peter', 28, 95000.5, .worker} |
19 | s := json.encode(x) |
20 | // eprintln('Employee x: $s') |
21 | assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":"worker"}' |
22 | y := json.decode(Employee, s)! |
23 | // eprintln('Employee y: $y') |
24 | assert y.name == 'Peter' |
25 | assert y.age == 28 |
26 | assert y.salary == 95000.5 |
27 | assert y.title == .worker |
28 | } |
29 | |
30 | const currency_id = 'cconst' |
31 | |
32 | struct Price { |
33 | net f64 |
34 | currency_id string [json: currencyId] = currency_id |
35 | } |
36 | |
37 | fn test_field_with_default_expr() { |
38 | data := '[{"net":1},{"net":2,"currencyId":"cjson"}]' |
39 | prices := json.decode([]Price, data)! |
40 | assert prices == [Price{ |
41 | net: 1 |
42 | currency_id: 'cconst' |
43 | }, Price{ |
44 | net: 2 |
45 | currency_id: 'cjson' |
46 | }] |
47 | } |
48 | |
49 | fn test_decode_top_level_array() { |
50 | s := '[{"name":"Peter", "age": 29}, {"name":"Bob", "age":31}]' |
51 | x := json.decode([]Employee, s) or { panic(err) } |
52 | assert x.len == 2 |
53 | assert x[0].name == 'Peter' |
54 | assert x[0].age == 29 |
55 | assert x[1].name == 'Bob' |
56 | assert x[1].age == 31 |
57 | } |
58 | |
59 | struct Human { |
60 | name string |
61 | } |
62 | |
63 | struct Item { |
64 | tag string |
65 | } |
66 | |
67 | enum Animal { |
68 | dog // Will be encoded as `0` |
69 | cat |
70 | } |
71 | |
72 | type Entity = Animal | Human | Item | string | time.Time |
73 | |
74 | struct SomeGame { |
75 | title string |
76 | player Entity |
77 | other []Entity |
78 | } |
79 | |
80 | fn test_encode_decode_sumtype() { |
81 | t := time.now() |
82 | game := SomeGame{ |
83 | title: 'Super Mega Game' |
84 | player: Human{'Monke'} |
85 | other: [ |
86 | Entity(Item{'Pen'}), |
87 | Item{'Cookie'}, |
88 | Animal.cat, |
89 | 'Stool', |
90 | t, |
91 | ] |
92 | } |
93 | // eprintln('Game: $game') |
94 | |
95 | enc := json.encode(game) |
96 | // eprintln('Encoded Game: $enc') |
97 | |
98 | assert enc == '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},"cat","Stool",{"_type":"Time","value":${t.unix_time()}}]}' |
99 | |
100 | dec := json.decode(SomeGame, enc)! |
101 | // eprintln('Decoded Game: $dec') |
102 | |
103 | assert game.title == dec.title |
104 | assert game.player == dec.player |
105 | assert (game.other[2] as Animal) == .cat |
106 | assert dec.other[2] == Entity('cat') |
107 | assert (game.other[4] as time.Time).unix_time() == (dec.other[4] as time.Time).unix_time() |
108 | } |
109 | |
110 | fn bar[T](payload string) !Bar { // ?T doesn't work currently |
111 | result := json.decode(T, payload)! |
112 | return result |
113 | } |
114 | |
115 | struct Bar { |
116 | x string |
117 | } |
118 | |
119 | fn test_generic() { |
120 | result := bar[Bar]('{"x":"test"}') or { Bar{} } |
121 | assert result.x == 'test' |
122 | } |
123 | |
124 | struct User2 { |
125 | age int |
126 | nums []int |
127 | reg_date time.Time |
128 | } |
129 | |
130 | struct User { |
131 | age int |
132 | nums []int |
133 | last_name string [json: lastName] |
134 | is_registered bool [json: IsRegistered] |
135 | typ int [json: 'type'] |
136 | pets string [json: 'pet_animals'; raw] |
137 | } |
138 | |
139 | fn test_parse_user() { |
140 | s := '{"age": 10, "nums": [1,2,3], "type": 1, "lastName": "Johnson", "IsRegistered": true, "pet_animals": {"name": "Bob", "animal": "Dog"}}' |
141 | u2 := json.decode(User2, s)! |
142 | // println(u2) |
143 | u := json.decode(User, s)! |
144 | // println(u) |
145 | assert u.age == 10 |
146 | assert u.last_name == 'Johnson' |
147 | assert u.is_registered == true |
148 | assert u.nums.len == 3 |
149 | assert u.nums[0] == 1 |
150 | assert u.nums[1] == 2 |
151 | assert u.nums[2] == 3 |
152 | assert u.typ == 1 |
153 | assert u.pets == '{"name":"Bob","animal":"Dog"}' |
154 | } |
155 | |
156 | fn test_encode_decode_time() { |
157 | user := User2{ |
158 | age: 25 |
159 | reg_date: time.new_time(year: 2020, month: 12, day: 22, hour: 7, minute: 23) |
160 | } |
161 | s := json.encode(user) |
162 | // println(s) |
163 | assert s.contains('"reg_date":1608621780') |
164 | user2 := json.decode(User2, s)! |
165 | assert user2.reg_date.str() == '2020-12-22 07:23:00' |
166 | // println(user2) |
167 | // println(user2.reg_date) |
168 | } |
169 | |
170 | fn (mut u User) foo() string { |
171 | return json.encode(u) |
172 | } |
173 | |
174 | fn test_encode_user() { |
175 | mut usr := User{ |
176 | age: 10 |
177 | nums: [1, 2, 3] |
178 | last_name: 'Johnson' |
179 | is_registered: true |
180 | typ: 0 |
181 | pets: 'foo' |
182 | } |
183 | expected := '{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"foo"}' |
184 | out := json.encode(usr) |
185 | // println(out) |
186 | assert out == expected |
187 | // Test json.encode on mutable pointers |
188 | assert usr.foo() == expected |
189 | } |
190 | |
191 | struct Color { |
192 | space string |
193 | point string [raw] |
194 | } |
195 | |
196 | fn test_raw_json_field() { |
197 | color := json.decode(Color, '{"space": "YCbCr", "point": {"Y": 123}}') or { |
198 | // println('text') |
199 | return |
200 | } |
201 | assert color.point == '{"Y":123}' |
202 | assert color.space == 'YCbCr' |
203 | } |
204 | |
205 | fn test_bad_raw_json_field() { |
206 | color := json.decode(Color, '{"space": "YCbCr"}') or { |
207 | // println('text') |
208 | return |
209 | } |
210 | assert color.point == '' |
211 | assert color.space == 'YCbCr' |
212 | } |
213 | |
214 | struct City { |
215 | name string |
216 | } |
217 | |
218 | struct Country { |
219 | cities []City |
220 | name string |
221 | } |
222 | |
223 | fn test_struct_in_struct() { |
224 | country := json.decode(Country, '{ "name": "UK", "cities": [{"name":"London"}, {"name":"Manchester"}]}')! |
225 | assert country.name == 'UK' |
226 | assert country.cities.len == 2 |
227 | assert country.cities[0].name == 'London' |
228 | assert country.cities[1].name == 'Manchester' |
229 | // println(country.cities) |
230 | } |
231 | |
232 | fn test_encode_map() { |
233 | expected := '{"one":1,"two":2,"three":3,"four":4}' |
234 | numbers := { |
235 | 'one': 1 |
236 | 'two': 2 |
237 | 'three': 3 |
238 | 'four': 4 |
239 | } |
240 | out := json.encode(numbers) |
241 | // println(out) |
242 | assert out == expected |
243 | } |
244 | |
245 | fn test_parse_map() { |
246 | expected := { |
247 | 'one': 1 |
248 | 'two': 2 |
249 | 'three': 3 |
250 | 'four': 4 |
251 | } |
252 | out := json.decode(map[string]int, '{"one":1,"two":2,"three":3,"four":4}')! |
253 | // println(out) |
254 | assert out == expected |
255 | } |
256 | |
257 | struct Data { |
258 | countries []Country |
259 | users map[string]User |
260 | extra map[string]map[string]int |
261 | } |
262 | |
263 | fn test_nested_type() { |
264 | data_expected := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":[{"name":"Donlon"},{"name":"Termanches"}],"name":"KU"}],"users":{"Foo":{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},"Boo":{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}},"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' |
265 | data := Data{ |
266 | countries: [ |
267 | Country{ |
268 | name: 'UK' |
269 | cities: [City{'London'}, City{'Manchester'}] |
270 | }, |
271 | Country{ |
272 | name: 'KU' |
273 | cities: [City{'Donlon'}, City{'Termanches'}] |
274 | }, |
275 | ] |
276 | users: { |
277 | 'Foo': User{ |
278 | age: 10 |
279 | nums: [1, 2, 3] |
280 | last_name: 'Johnson' |
281 | is_registered: true |
282 | typ: 0 |
283 | pets: 'little foo' |
284 | } |
285 | 'Boo': User{ |
286 | age: 20 |
287 | nums: [5, 3, 1] |
288 | last_name: 'Smith' |
289 | is_registered: false |
290 | typ: 4 |
291 | pets: 'little boo' |
292 | } |
293 | } |
294 | extra: { |
295 | '2': { |
296 | 'n1': 2 |
297 | 'n2': 4 |
298 | 'n3': 8 |
299 | 'n4': 16 |
300 | } |
301 | '3': { |
302 | 'n1': 3 |
303 | 'n2': 9 |
304 | 'n3': 27 |
305 | 'n4': 81 |
306 | } |
307 | } |
308 | } |
309 | out := json.encode(data) |
310 | // println(out) |
311 | assert out == data_expected |
312 | data2 := json.decode(Data, data_expected)! |
313 | assert data2.countries.len == data.countries.len |
314 | for i in 0 .. 1 { |
315 | assert data2.countries[i].name == data.countries[i].name |
316 | assert data2.countries[i].cities.len == data.countries[i].cities.len |
317 | for j in 0 .. 1 { |
318 | assert data2.countries[i].cities[j].name == data.countries[i].cities[j].name |
319 | } |
320 | } |
321 | for key, user in data.users { |
322 | assert data2.users[key].age == user.age |
323 | assert data2.users[key].nums == user.nums |
324 | assert data2.users[key].last_name == user.last_name |
325 | assert data2.users[key].is_registered == user.is_registered |
326 | assert data2.users[key].typ == user.typ |
327 | // assert data2.users[key].pets == user.pets // TODO FIX |
328 | } |
329 | for k, v in data.extra { |
330 | for k2, v2 in v { |
331 | assert data2.extra[k][k2] == v2 |
332 | } |
333 | } |
334 | } |
335 | |
336 | struct Foo[T] { |
337 | pub: |
338 | name string |
339 | data T |
340 | } |
341 | |
342 | fn test_generic_struct() { |
343 | foo_int := Foo[int]{'bar', 12} |
344 | foo_enc := json.encode(foo_int) |
345 | assert foo_enc == '{"name":"bar","data":12}' |
346 | foo_dec := json.decode(Foo[int], foo_enc)! |
347 | assert foo_dec.name == 'bar' |
348 | assert foo_dec.data == 12 |
349 | } |
350 | |
351 | fn test_errors() { |
352 | invalid_array := fn () { |
353 | data := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":{"name":"Donlon"},"name":"KU"}],"users":{"Foo":{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},"Boo":{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}},"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' |
354 | json.decode(Data, data) or { |
355 | // println(err) |
356 | assert err.msg().starts_with('Json element is not an array:') |
357 | return |
358 | } |
359 | assert false |
360 | } |
361 | invalid_object := fn () { |
362 | data := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":[{"name":"Donlon"},{"name":"Termanches"}],"name":"KU"}],"users":[{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}],"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' |
363 | json.decode(Data, data) or { |
364 | // println(err) |
365 | assert err.msg().starts_with('Json element is not an object:') |
366 | return |
367 | } |
368 | assert false |
369 | } |
370 | invalid_array() |
371 | invalid_object() |
372 | } |
373 | |
374 | type ID = string |
375 | type GG = int |
376 | |
377 | struct Message { |
378 | id ID |
379 | ij GG |
380 | } |
381 | |
382 | fn test_decode_alias_struct() { |
383 | msg := json.decode(Message, '{"id": "118499178790780929"}')! |
384 | // hacky way of comparing aliased strings |
385 | assert msg.id.str() == '118499178790780929' |
386 | assert msg.ij.str() == '0' |
387 | } |
388 | |
389 | fn test_encode_alias_struct() { |
390 | expected := '{"id":"118499178790780929","ij":999998888}' |
391 | msg := Message{'118499178790780929', 999998888} |
392 | out := json.encode(msg) |
393 | assert out == expected |
394 | } |
395 | |
396 | struct List { |
397 | id int |
398 | items []string |
399 | } |
400 | |
401 | fn test_list() { |
402 | list := json.decode(List, '{"id": 1, "items": ["1", "2"]}')! |
403 | assert list.id == 1 |
404 | assert list.items == ['1', '2'] |
405 | } |
406 | |
407 | fn test_list_no_id() { |
408 | list := json.decode(List, '{"items": ["1", "2"]}')! |
409 | assert list.id == 0 |
410 | assert list.items == ['1', '2'] |
411 | } |
412 | |
413 | fn test_list_no_items() { |
414 | list := json.decode(List, '{"id": 1}')! |
415 | assert list.id == 1 |
416 | assert list.items == [] |
417 | } |
418 | |
419 | struct Info { |
420 | id int |
421 | items []string |
422 | maps map[string]string |
423 | } |
424 | |
425 | fn test_decode_null_object() { |
426 | info := json.decode(Info, '{"id": 22, "items": null, "maps": null}')! |
427 | assert info.id == 22 |
428 | assert '${info.items}' == '[]' |
429 | assert '${info.maps}' == '{}' |
430 | } |
431 | |
432 | fn test_decode_missing_maps_field() { |
433 | info := json.decode(Info, '{"id": 22, "items": null}')! |
434 | assert info.id == 22 |
435 | assert '${info.items}' == '[]' |
436 | assert '${info.maps}' == '{}' |
437 | } |
438 | |
439 | struct Foo2 { |
440 | name string |
441 | } |
442 | |
443 | fn test_pretty() { |
444 | foo := Foo2{'Bob'} |
445 | assert json.encode_pretty(foo) == '{ |
446 | "name": "Bob" |
447 | }' |
448 | } |
449 | |
450 | struct Foo3 { |
451 | name string |
452 | age int [omitempty] |
453 | } |
454 | |
455 | fn test_omit_empty() { |
456 | foo := Foo3{'Bob', 0} |
457 | assert json.encode_pretty(foo) == '{ |
458 | "name": "Bob" |
459 | }' |
460 | // println('omitempty:') |
461 | // println(json.encode_pretty(foo)) |
462 | } |
463 | |
464 | struct Asdasd { |
465 | data GamePacketData |
466 | } |
467 | |
468 | type GamePacketData = GPEquipItem | GPScale |
469 | |
470 | struct GPScale { |
471 | value f32 |
472 | } |
473 | |
474 | struct GPEquipItem { |
475 | name string |
476 | } |
477 | |
478 | fn create_game_packet(data &GamePacketData) string { |
479 | return json.encode(data) |
480 | } |
481 | |
482 | fn test_encode_sumtype_defined_ahead() { |
483 | ret := create_game_packet(&GamePacketData(GPScale{})) |
484 | // println(ret) |
485 | assert ret == '{"value":0,"_type":"GPScale"}' |
486 | } |
487 | |
488 | struct StByteArray { |
489 | ba []byte |
490 | } |
491 | |
492 | fn test_byte_array() { |
493 | assert json.encode(StByteArray{ ba: [byte(1), 2, 3, 4, 5] }) == '{"ba":[1,2,3,4,5]}' |
494 | } |
495 | |
496 | struct Aa { |
497 | sub AliasType |
498 | } |
499 | |
500 | struct Bb { |
501 | a int |
502 | } |
503 | |
504 | type AliasType = Bb |
505 | |
506 | fn test_encode_alias_field() { |
507 | s := json.encode(Aa{ |
508 | sub: Bb{ |
509 | a: 1 |
510 | } |
511 | }) |
512 | println(s) |
513 | assert s == '{"sub":{"a":1}}' |
514 | } |
515 | |
516 | // |
517 | |
518 | struct APrice {} |
519 | |
520 | struct Association { |
521 | association &Association = unsafe { nil } |
522 | price APrice |
523 | } |
524 | |
525 | fn test_encoding_struct_with_pointers() { |
526 | value := Association{ |
527 | association: &Association{ |
528 | price: APrice{} |
529 | } |
530 | price: APrice{} |
531 | } |
532 | assert json.encode(value) == '{"association":{"price":{}},"price":{}}' |
533 | } |