aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/json
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/json')
-rw-r--r--v_windows/v/vlib/json/json_decode_test.v46
-rw-r--r--v_windows/v/vlib/json/json_primitives.v204
-rw-r--r--v_windows/v/vlib/json/json_test.v368
3 files changed, 618 insertions, 0 deletions
diff --git a/v_windows/v/vlib/json/json_decode_test.v b/v_windows/v/vlib/json/json_decode_test.v
new file mode 100644
index 0000000..5b21838
--- /dev/null
+++ b/v_windows/v/vlib/json/json_decode_test.v
@@ -0,0 +1,46 @@
+import json
+
+struct TestTwin {
+ id int
+ seed string
+ pubkey string
+}
+
+struct TestTwins {
+mut:
+ twins []TestTwin [required]
+}
+
+fn test_json_decode_fails_to_decode_unrecognised_array_of_dicts() {
+ data := '[{"twins":[{"id":123,"seed":"abcde","pubkey":"xyzasd"},{"id":456,"seed":"dfgdfgdfgd","pubkey":"skjldskljh45sdf"}]}]'
+ json.decode(TestTwins, data) or {
+ assert err.msg == "expected field 'twins' is missing"
+ return
+ }
+ assert false
+}
+
+fn test_json_decode_works_with_a_dict_of_arrays() {
+ data := '{"twins":[{"id":123,"seed":"abcde","pubkey":"xyzasd"},{"id":456,"seed":"dfgdfgdfgd","pubkey":"skjldskljh45sdf"}]}'
+ res := json.decode(TestTwins, data) or {
+ assert false
+ exit(1)
+ }
+ assert res.twins[0].id == 123
+ assert res.twins[0].seed == 'abcde'
+ assert res.twins[0].pubkey == 'xyzasd'
+ assert res.twins[1].id == 456
+ assert res.twins[1].seed == 'dfgdfgdfgd'
+ assert res.twins[1].pubkey == 'skjldskljh45sdf'
+}
+
+struct Mount {
+ size u64
+}
+
+fn test_decode_u64() ? {
+ data := '{"size": 10737418240}'
+ m := json.decode(Mount, data) ?
+ assert m.size == 10737418240
+ println(m)
+}
diff --git a/v_windows/v/vlib/json/json_primitives.v b/v_windows/v/vlib/json/json_primitives.v
new file mode 100644
index 0000000..2e635e5
--- /dev/null
+++ b/v_windows/v/vlib/json/json_primitives.v
@@ -0,0 +1,204 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module json
+
+#flag -I @VEXEROOT/thirdparty/cJSON
+#flag @VEXEROOT/thirdparty/cJSON/cJSON.o
+#include "cJSON.h"
+#define js_get(object, key) cJSON_GetObjectItemCaseSensitive((object), (key))
+
+struct C.cJSON {
+ valueint int
+ valuedouble f32
+ valuestring &char
+}
+
+fn C.cJSON_IsTrue(&C.cJSON) bool
+
+fn C.cJSON_CreateNumber(int) &C.cJSON
+
+fn C.cJSON_CreateBool(bool) &C.cJSON
+
+fn C.cJSON_CreateString(&char) &C.cJSON
+
+fn C.cJSON_Parse(&char) &C.cJSON
+
+fn C.cJSON_PrintUnformatted(&C.cJSON) &char
+
+fn C.cJSON_Print(&C.cJSON) &char
+
+pub fn decode(typ voidptr, s string) ?voidptr {
+ // compiler implementation
+ return 0
+}
+
+pub fn encode(x voidptr) string {
+ // compiler implementation
+ return ''
+}
+
+pub fn encode_pretty(x voidptr) string {
+ // compiler implementation
+ return ''
+}
+
+fn decode_int(root &C.cJSON) int {
+ if isnil(root) {
+ return 0
+ }
+ return root.valueint
+}
+
+fn decode_i8(root &C.cJSON) i8 {
+ if isnil(root) {
+ return i8(0)
+ }
+ return i8(root.valueint)
+}
+
+fn decode_i16(root &C.cJSON) i16 {
+ if isnil(root) {
+ return i16(0)
+ }
+ return i16(root.valueint)
+}
+
+fn decode_i64(root &C.cJSON) i64 {
+ if isnil(root) {
+ return i64(0)
+ }
+ return i64(root.valuedouble) // i64 is double in C
+}
+
+fn decode_byte(root &C.cJSON) byte {
+ if isnil(root) {
+ return byte(0)
+ }
+ return byte(root.valueint)
+}
+
+fn decode_u16(root &C.cJSON) u16 {
+ if isnil(root) {
+ return u16(0)
+ }
+ return u16(root.valueint)
+}
+
+fn decode_u32(root &C.cJSON) u32 {
+ if isnil(root) {
+ return u32(0)
+ }
+ return u32(root.valueint)
+}
+
+fn decode_u64(root &C.cJSON) u64 {
+ if isnil(root) {
+ return u64(0)
+ }
+ return u64(root.valuedouble)
+}
+
+fn decode_f32(root &C.cJSON) f32 {
+ if isnil(root) {
+ return f32(0)
+ }
+ return root.valuedouble
+}
+
+fn decode_f64(root &C.cJSON) f64 {
+ if isnil(root) {
+ return f64(0)
+ }
+ return f64(root.valuedouble)
+}
+
+fn decode_string(root &C.cJSON) string {
+ if isnil(root) {
+ return ''
+ }
+ if isnil(root.valuestring) {
+ return ''
+ }
+ // println('decode string valuestring="$root.valuestring"')
+ // return tos(root.valuestring, _strlen(root.valuestring))
+ return unsafe { tos_clone(&byte(root.valuestring)) } // , _strlen(root.valuestring))
+}
+
+fn decode_bool(root &C.cJSON) bool {
+ if isnil(root) {
+ return false
+ }
+ return C.cJSON_IsTrue(root)
+}
+
+// ///////////////////
+fn encode_int(val int) &C.cJSON {
+ return C.cJSON_CreateNumber(val)
+}
+
+fn encode_i8(val i8) &C.cJSON {
+ return C.cJSON_CreateNumber(val)
+}
+
+fn encode_i16(val i16) &C.cJSON {
+ return C.cJSON_CreateNumber(val)
+}
+
+fn encode_i64(val i64) &C.cJSON {
+ return C.cJSON_CreateNumber(val)
+}
+
+fn encode_byte(val byte) &C.cJSON {
+ return C.cJSON_CreateNumber(val)
+}
+
+fn encode_u16(val u16) &C.cJSON {
+ return C.cJSON_CreateNumber(val)
+}
+
+fn encode_u32(val u32) &C.cJSON {
+ return C.cJSON_CreateNumber(val)
+}
+
+fn encode_u64(val u64) &C.cJSON {
+ return C.cJSON_CreateNumber(val)
+}
+
+fn encode_f32(val f32) &C.cJSON {
+ return C.cJSON_CreateNumber(val)
+}
+
+fn encode_f64(val f64) &C.cJSON {
+ return C.cJSON_CreateNumber(val)
+}
+
+fn encode_bool(val bool) &C.cJSON {
+ return C.cJSON_CreateBool(val)
+}
+
+fn encode_string(val string) &C.cJSON {
+ return C.cJSON_CreateString(&char(val.str))
+}
+
+// ///////////////////////
+// user := decode_User(json_parse(js_string_var))
+fn json_parse(s string) &C.cJSON {
+ return C.cJSON_Parse(&char(s.str))
+}
+
+// json_string := json_print(encode_User(user))
+fn json_print(json &C.cJSON) string {
+ s := C.cJSON_PrintUnformatted(json)
+ return unsafe { tos(&byte(s), C.strlen(&char(s))) }
+}
+
+fn json_print_pretty(json &C.cJSON) string {
+ s := C.cJSON_Print(json)
+ return unsafe { tos(&byte(s), C.strlen(&char(s))) }
+}
+
+// / cjson wrappers
+// fn json_array_for_each(val, root &C.cJSON) {
+// #cJSON_ArrayForEach (val ,root)
+// }
diff --git a/v_windows/v/vlib/json/json_test.v b/v_windows/v/vlib/json/json_test.v
new file mode 100644
index 0000000..ca6b2ac
--- /dev/null
+++ b/v_windows/v/vlib/json/json_test.v
@@ -0,0 +1,368 @@
+import json
+import time
+
+enum JobTitle {
+ manager
+ executive
+ worker
+}
+
+struct Employee {
+ name string
+ age int
+ salary f32
+ title JobTitle
+}
+
+fn test_simple() ? {
+ x := Employee{'Peter', 28, 95000.5, .worker}
+ s := json.encode(x)
+ eprintln('Employee x: $s')
+ assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":2}'
+ y := json.decode(Employee, s) ?
+ eprintln('Employee y: $y')
+ assert y.name == 'Peter'
+ assert y.age == 28
+ assert y.salary == 95000.5
+ assert y.title == .worker
+}
+
+fn test_decode_top_level_array() {
+ s := '[{"name":"Peter", "age": 29}, {"name":"Bob", "age":31}]'
+ x := json.decode([]Employee, s) or { panic(err) }
+ assert x.len == 2
+ assert x[0].name == 'Peter'
+ assert x[0].age == 29
+ assert x[1].name == 'Bob'
+ assert x[1].age == 31
+}
+
+fn bar<T>(payload string) ?Bar { // ?T doesn't work currently
+ result := json.decode(T, payload) ?
+ return result
+}
+
+struct Bar {
+ x string
+}
+
+fn test_generic() {
+ result := bar<Bar>('{"x":"test"}') or { Bar{} }
+ assert result.x == 'test'
+}
+
+struct User2 {
+ age int
+ nums []int
+ reg_date time.Time
+}
+
+struct User {
+ age int
+ nums []int
+ last_name string [json: lastName]
+ is_registered bool [json: IsRegistered]
+ typ int [json: 'type']
+ pets string [json: 'pet_animals'; raw]
+}
+
+fn test_parse_user() ? {
+ s := '{"age": 10, "nums": [1,2,3], "type": 1, "lastName": "Johnson", "IsRegistered": true, "pet_animals": {"name": "Bob", "animal": "Dog"}}'
+ u2 := json.decode(User2, s) ?
+ println(u2)
+ u := json.decode(User, s) ?
+ println(u)
+ assert u.age == 10
+ assert u.last_name == 'Johnson'
+ assert u.is_registered == true
+ assert u.nums.len == 3
+ assert u.nums[0] == 1
+ assert u.nums[1] == 2
+ assert u.nums[2] == 3
+ assert u.typ == 1
+ assert u.pets == '{"name":"Bob","animal":"Dog"}'
+}
+
+fn test_encode_decode_time() ? {
+ user := User2{
+ age: 25
+ reg_date: time.new_time(year: 2020, month: 12, day: 22, hour: 7, minute: 23)
+ }
+ s := json.encode(user)
+ println(s)
+ assert s.contains('"reg_date":1608621780')
+ user2 := json.decode(User2, s) ?
+ assert user2.reg_date.str() == '2020-12-22 07:23:00'
+ println(user2)
+ println(user2.reg_date)
+}
+
+fn (mut u User) foo() string {
+ return json.encode(u)
+}
+
+fn test_encode_user() {
+ mut usr := User{
+ age: 10
+ nums: [1, 2, 3]
+ last_name: 'Johnson'
+ is_registered: true
+ typ: 0
+ pets: 'foo'
+ }
+ expected := '{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"foo"}'
+ out := json.encode(usr)
+ println(out)
+ assert out == expected
+ // Test json.encode on mutable pointers
+ assert usr.foo() == expected
+}
+
+struct Color {
+ space string
+ point string [raw]
+}
+
+fn test_raw_json_field() {
+ color := json.decode(Color, '{"space": "YCbCr", "point": {"Y": 123}}') or {
+ println('text')
+ return
+ }
+ assert color.point == '{"Y":123}'
+ assert color.space == 'YCbCr'
+}
+
+fn test_bad_raw_json_field() {
+ color := json.decode(Color, '{"space": "YCbCr"}') or {
+ println('text')
+ return
+ }
+ assert color.point == ''
+ assert color.space == 'YCbCr'
+}
+
+struct City {
+ name string
+}
+
+struct Country {
+ cities []City
+ name string
+}
+
+fn test_struct_in_struct() ? {
+ country := json.decode(Country, '{ "name": "UK", "cities": [{"name":"London"}, {"name":"Manchester"}]}') ?
+ assert country.name == 'UK'
+ assert country.cities.len == 2
+ assert country.cities[0].name == 'London'
+ assert country.cities[1].name == 'Manchester'
+ println(country.cities)
+}
+
+fn test_encode_map() {
+ expected := '{"one":1,"two":2,"three":3,"four":4}'
+ numbers := {
+ 'one': 1
+ 'two': 2
+ 'three': 3
+ 'four': 4
+ }
+ out := json.encode(numbers)
+ println(out)
+ assert out == expected
+}
+
+fn test_parse_map() ? {
+ expected := {
+ 'one': 1
+ 'two': 2
+ 'three': 3
+ 'four': 4
+ }
+ out := json.decode(map[string]int, '{"one":1,"two":2,"three":3,"four":4}') ?
+ println(out)
+ assert out == expected
+}
+
+struct Data {
+ countries []Country
+ users map[string]User
+ extra map[string]map[string]int
+}
+
+fn test_nested_type() ? {
+ 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}}}'
+ data := Data{
+ countries: [
+ Country{
+ name: 'UK'
+ cities: [City{'London'}, City{'Manchester'}]
+ },
+ Country{
+ name: 'KU'
+ cities: [City{'Donlon'}, City{'Termanches'}]
+ },
+ ]
+ users: {
+ 'Foo': User{
+ age: 10
+ nums: [1, 2, 3]
+ last_name: 'Johnson'
+ is_registered: true
+ typ: 0
+ pets: 'little foo'
+ }
+ 'Boo': User{
+ age: 20
+ nums: [5, 3, 1]
+ last_name: 'Smith'
+ is_registered: false
+ typ: 4
+ pets: 'little boo'
+ }
+ }
+ extra: {
+ '2': {
+ 'n1': 2
+ 'n2': 4
+ 'n3': 8
+ 'n4': 16
+ }
+ '3': {
+ 'n1': 3
+ 'n2': 9
+ 'n3': 27
+ 'n4': 81
+ }
+ }
+ }
+ out := json.encode(data)
+ println(out)
+ assert out == data_expected
+ data2 := json.decode(Data, data_expected) ?
+ assert data2.countries.len == data.countries.len
+ for i in 0 .. 1 {
+ assert data2.countries[i].name == data.countries[i].name
+ assert data2.countries[i].cities.len == data.countries[i].cities.len
+ for j in 0 .. 1 {
+ assert data2.countries[i].cities[j].name == data.countries[i].cities[j].name
+ }
+ }
+ for key, user in data.users {
+ assert data2.users[key].age == user.age
+ assert data2.users[key].nums == user.nums
+ assert data2.users[key].last_name == user.last_name
+ assert data2.users[key].is_registered == user.is_registered
+ assert data2.users[key].typ == user.typ
+ // assert data2.users[key].pets == user.pets // TODO FIX
+ }
+ for k, v in data.extra {
+ for k2, v2 in v {
+ assert data2.extra[k][k2] == v2
+ }
+ }
+}
+
+struct Foo<T> {
+pub:
+ name string
+ data T
+}
+
+fn test_generic_struct() ? {
+ foo_int := Foo<int>{'bar', 12}
+ foo_enc := json.encode(foo_int)
+ assert foo_enc == '{"name":"bar","data":12}'
+ foo_dec := json.decode(Foo<int>, foo_enc) ?
+ assert foo_dec.name == 'bar'
+ assert foo_dec.data == 12
+}
+
+fn test_errors() {
+ invalid_array := fn () {
+ 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}}}'
+ json.decode(Data, data) or {
+ println(err)
+ assert err.msg.starts_with('Json element is not an array:')
+ return
+ }
+ assert false
+ }
+ invalid_object := fn () {
+ 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}}}'
+ json.decode(Data, data) or {
+ println(err)
+ assert err.msg.starts_with('Json element is not an object:')
+ return
+ }
+ assert false
+ }
+ invalid_array()
+ invalid_object()
+}
+
+type ID = string
+
+struct Message {
+ id ID
+}
+
+fn test_decode_alias_struct() ? {
+ msg := json.decode(Message, '{"id": "118499178790780929"}') ?
+ // hacky way of comparing aliased strings
+ assert msg.id.str() == '118499178790780929'
+}
+
+fn test_encode_alias_struct() {
+ expected := '{"id":"118499178790780929"}'
+ msg := Message{'118499178790780929'}
+ out := json.encode(msg)
+ assert out == expected
+}
+
+struct List {
+ id int
+ items []string
+}
+
+fn test_list() ? {
+ list := json.decode(List, '{"id": 1, "items": ["1", "2"]}') ?
+ assert list.id == 1
+ assert list.items == ['1', '2']
+}
+
+fn test_list_no_id() ? {
+ list := json.decode(List, '{"items": ["1", "2"]}') ?
+ assert list.id == 0
+ assert list.items == ['1', '2']
+}
+
+fn test_list_no_items() ? {
+ list := json.decode(List, '{"id": 1}') ?
+ assert list.id == 1
+ assert list.items == []
+}
+
+struct Info {
+ id int
+ items []string
+ maps map[string]string
+}
+
+fn test_decode_null_object() ? {
+ info := json.decode(Info, '{"id": 22, "items": null, "maps": null}') ?
+ assert info.id == 22
+ assert '$info.items' == '[]'
+ assert '$info.maps' == '{}'
+}
+
+struct Foo2 {
+ name string
+}
+
+fn test_pretty() {
+ foo := Foo2{'Bob'}
+ assert json.encode_pretty(foo) == '{
+ "name": "Bob"
+}'
+}