Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,347 @@
|
||||
import x.json2 as json
|
||||
|
||||
const sample_data = {
|
||||
'u8': json.Any(u8(1))
|
||||
'u16': json.Any(u16(2))
|
||||
'u32': json.Any(u32(3))
|
||||
'u64': json.Any(u64(4))
|
||||
'i8': json.Any(i8(5))
|
||||
'i16': json.Any(i16(6))
|
||||
'i32': json.Any(i32(7))
|
||||
'int': json.Any(int(8))
|
||||
'i64': json.Any(i64(9))
|
||||
'f32': json.Any(f32(2.3))
|
||||
'f64': json.Any(f64(1.283))
|
||||
'bool': json.Any(false)
|
||||
'str': json.Any('test')
|
||||
'null': json.Any(json.null)
|
||||
'arr': json.Any([json.Any('lol')])
|
||||
'obj': json.Any({
|
||||
'foo': json.Any(10)
|
||||
})
|
||||
}
|
||||
|
||||
fn is_null(f json.Any) bool {
|
||||
match f {
|
||||
json.Null { return true }
|
||||
else { return false }
|
||||
}
|
||||
}
|
||||
|
||||
fn test_f32() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.f32() == 1.0
|
||||
assert sample_data['u16'] or { 0 }.f32() == 2.0
|
||||
assert sample_data['u32'] or { 0 }.f32() == 3.0
|
||||
assert sample_data['u64'] or { 0 }.f32() == 4.0
|
||||
assert sample_data['i8'] or { 0 }.f32() == 5.0
|
||||
assert sample_data['i16'] or { 0 }.f32() == 6.0
|
||||
assert sample_data['i32'] or { 0 }.f32() == 7.0
|
||||
assert sample_data['int'] or { 0 }.f32() == 8.0
|
||||
assert sample_data['i64'] or { 0 }.f32() == 9.0
|
||||
assert sample_data['f32'] or { 0 }.f32() == 2.3
|
||||
assert sample_data['f64'] or { 0 }.f32() == 1.2829999923706055
|
||||
// invalid conversions
|
||||
assert sample_data['bool'] or { 0 }.f32() == 0.0
|
||||
assert sample_data['str'] or { 0 }.f32() == 0.0
|
||||
assert sample_data['null'] or { 0 }.f32() == 0.0
|
||||
assert sample_data['arr'] or { 0 }.f32() == 0.0
|
||||
assert sample_data['obj'] or { 0 }.f32() == 0.0
|
||||
}
|
||||
|
||||
fn test_f64() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.f64() == 1.0
|
||||
assert sample_data['u16'] or { 0 }.f64() == 2.0
|
||||
assert sample_data['u32'] or { 0 }.f64() == 3.0
|
||||
assert sample_data['u64'] or { 0 }.f64() == 4.0
|
||||
assert sample_data['i8'] or { 0 }.f64() == 5.0
|
||||
assert sample_data['i16'] or { 0 }.f64() == 6.0
|
||||
assert sample_data['i32'] or { 0 }.f64() == 7.0
|
||||
assert sample_data['int'] or { 0 }.f64() == 8.0
|
||||
assert sample_data['i64'] or { 0 }.f64() == 9.0
|
||||
assert sample_data['f32'] or { 0 }.f64() == 2.299999952316284
|
||||
assert sample_data['f64'] or { 0 }.f64() == 1.283
|
||||
// invalid conversions
|
||||
assert sample_data['bool'] or { 0 }.f64() == 0.0
|
||||
assert sample_data['str'] or { 0 }.f64() == 0.0
|
||||
assert sample_data['null'] or { 0 }.f64() == 0.0
|
||||
assert sample_data['arr'] or { 0 }.f64() == 0.0
|
||||
assert sample_data['obj'] or { 0 }.f64() == 0.0
|
||||
}
|
||||
|
||||
fn test_i8() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.i8() == 1
|
||||
assert sample_data['u16'] or { 0 }.i8() == 2
|
||||
assert sample_data['u32'] or { 0 }.i8() == 3
|
||||
assert sample_data['u64'] or { 0 }.i8() == 4
|
||||
assert sample_data['i8'] or { 0 }.i8() == 5
|
||||
assert sample_data['i16'] or { 0 }.i8() == 6
|
||||
assert sample_data['i32'] or { 0 }.i8() == 7
|
||||
assert sample_data['int'] or { 0 }.i8() == 8
|
||||
assert sample_data['i64'] or { 0 }.i8() == 9
|
||||
assert sample_data['f32'] or { 0 }.i8() == 2
|
||||
assert sample_data['f64'] or { 0 }.i8() == 1
|
||||
assert json.Any(true).i8() == 1
|
||||
assert json.Any('123').i8() == 123
|
||||
// invalid conversions
|
||||
assert sample_data['null'] or { 0 }.i8() == 0
|
||||
assert sample_data['arr'] or { 0 }.i8() == 0
|
||||
assert sample_data['obj'] or { 0 }.i8() == 0
|
||||
}
|
||||
|
||||
fn test_i16() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.i16() == 1
|
||||
assert sample_data['u16'] or { 0 }.i16() == 2
|
||||
assert sample_data['u32'] or { 0 }.i16() == 3
|
||||
assert sample_data['u64'] or { 0 }.i16() == 4
|
||||
assert sample_data['i8'] or { 0 }.i16() == 5
|
||||
assert sample_data['i16'] or { 0 }.i16() == 6
|
||||
assert sample_data['i32'] or { 0 }.i16() == 7
|
||||
assert sample_data['int'] or { 0 }.i16() == 8
|
||||
assert sample_data['i64'] or { 0 }.i16() == 9
|
||||
assert sample_data['f32'] or { 0 }.i16() == 2
|
||||
assert sample_data['f64'] or { 0 }.i16() == 1
|
||||
assert json.Any(true).i16() == 1
|
||||
assert json.Any('123').i16() == 123
|
||||
// invalid conversions
|
||||
assert sample_data['null'] or { 0 }.i16() == 0
|
||||
assert sample_data['arr'] or { 0 }.i16() == 0
|
||||
assert sample_data['obj'] or { 0 }.i16() == 0
|
||||
}
|
||||
|
||||
fn test_i32() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.i32() == 1
|
||||
assert sample_data['u16'] or { 0 }.i32() == 2
|
||||
assert sample_data['u32'] or { 0 }.i32() == 3
|
||||
assert sample_data['u64'] or { 0 }.i32() == 4
|
||||
assert sample_data['i8'] or { 0 }.i32() == 5
|
||||
assert sample_data['i16'] or { 0 }.i32() == 6
|
||||
assert sample_data['i32'] or { 0 }.i32() == 7
|
||||
assert sample_data['int'] or { 0 }.i32() == 8
|
||||
assert sample_data['i64'] or { 0 }.i32() == 9
|
||||
assert sample_data['f32'] or { 0 }.i32() == 2
|
||||
assert sample_data['f64'] or { 0 }.i32() == 1
|
||||
assert json.Any(true).i32() == 1
|
||||
assert json.Any('123').i32() == 123
|
||||
// invalid conversions
|
||||
assert sample_data['null'] or { 0 }.i32() == 0
|
||||
assert sample_data['arr'] or { 0 }.i32() == 0
|
||||
assert sample_data['obj'] or { 0 }.i32() == 0
|
||||
}
|
||||
|
||||
fn test_int() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.int() == 1
|
||||
assert sample_data['u16'] or { 0 }.int() == 2
|
||||
assert sample_data['u32'] or { 0 }.int() == 3
|
||||
assert sample_data['u64'] or { 0 }.int() == 4
|
||||
assert sample_data['i8'] or { 0 }.int() == 5
|
||||
assert sample_data['i16'] or { 0 }.int() == 6
|
||||
assert sample_data['i32'] or { 0 }.int() == 7
|
||||
assert sample_data['int'] or { 0 }.int() == 8
|
||||
assert sample_data['i64'] or { 0 }.int() == 9
|
||||
assert sample_data['f32'] or { 0 }.int() == 2
|
||||
assert sample_data['f64'] or { 0 }.int() == 1
|
||||
assert json.Any(true).int() == 1
|
||||
assert json.Any('123').int() == 123
|
||||
// invalid conversions
|
||||
assert sample_data['null'] or { 0 }.int() == 0
|
||||
assert sample_data['arr'] or { 0 }.int() == 0
|
||||
assert sample_data['obj'] or { 0 }.int() == 0
|
||||
}
|
||||
|
||||
fn test_i64() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.i64() == 1
|
||||
assert sample_data['u16'] or { 0 }.i64() == 2
|
||||
assert sample_data['u32'] or { 0 }.i64() == 3
|
||||
assert sample_data['u64'] or { 0 }.i64() == 4
|
||||
assert sample_data['i8'] or { 0 }.i64() == 5
|
||||
assert sample_data['i16'] or { 0 }.i64() == 6
|
||||
assert sample_data['i32'] or { 0 }.i64() == 7
|
||||
assert sample_data['int'] or { 0 }.i64() == 8
|
||||
assert sample_data['i64'] or { 0 }.i64() == 9
|
||||
assert sample_data['f32'] or { 0 }.i64() == 2
|
||||
assert sample_data['f64'] or { 0 }.i64() == 1
|
||||
assert json.Any(true).i64() == 1
|
||||
assert json.Any('123').i64() == 123
|
||||
// invalid conversions
|
||||
assert sample_data['null'] or { 0 }.i64() == 0
|
||||
assert sample_data['arr'] or { 0 }.i64() == 0
|
||||
assert sample_data['obj'] or { 0 }.i64() == 0
|
||||
}
|
||||
|
||||
fn test_u8() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.u8() == 1
|
||||
assert sample_data['u16'] or { 0 }.u8() == 2
|
||||
assert sample_data['u32'] or { 0 }.u8() == 3
|
||||
assert sample_data['u64'] or { 0 }.u8() == 4
|
||||
assert sample_data['i8'] or { 0 }.u8() == 5
|
||||
assert sample_data['i16'] or { 0 }.u8() == 6
|
||||
assert sample_data['i32'] or { 0 }.u8() == 7
|
||||
assert sample_data['int'] or { 0 }.u8() == 8
|
||||
assert sample_data['i64'] or { 0 }.u8() == 9
|
||||
assert sample_data['f32'] or { 0 }.u8() == 2
|
||||
assert sample_data['f64'] or { 0 }.u8() == 1
|
||||
assert json.Any(true).u8() == 1
|
||||
assert json.Any('123').u8() == 123
|
||||
// invalid conversions
|
||||
assert sample_data['null'] or { 0 }.u8() == 0
|
||||
assert sample_data['arr'] or { 0 }.u8() == 0
|
||||
assert sample_data['obj'] or { 0 }.u8() == 0
|
||||
}
|
||||
|
||||
fn test_u16() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.u16() == 1
|
||||
assert sample_data['u16'] or { 0 }.u16() == 2
|
||||
assert sample_data['u32'] or { 0 }.u16() == 3
|
||||
assert sample_data['u64'] or { 0 }.u16() == 4
|
||||
assert sample_data['i8'] or { 0 }.u16() == 5
|
||||
assert sample_data['i16'] or { 0 }.u16() == 6
|
||||
assert sample_data['i32'] or { 0 }.u16() == 7
|
||||
assert sample_data['int'] or { 0 }.u16() == 8
|
||||
assert sample_data['i64'] or { 0 }.u16() == 9
|
||||
assert sample_data['f32'] or { 0 }.u16() == 2
|
||||
assert sample_data['f64'] or { 0 }.u16() == 1
|
||||
assert json.Any(true).u16() == 1
|
||||
assert json.Any('123').u16() == 123
|
||||
// invalid conversions
|
||||
assert sample_data['null'] or { 0 }.u16() == 0
|
||||
assert sample_data['arr'] or { 0 }.u16() == 0
|
||||
assert sample_data['obj'] or { 0 }.u16() == 0
|
||||
}
|
||||
|
||||
fn test_u32() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.u32() == 1
|
||||
assert sample_data['u16'] or { 0 }.u32() == 2
|
||||
assert sample_data['u32'] or { 0 }.u32() == 3
|
||||
assert sample_data['u64'] or { 0 }.u32() == 4
|
||||
assert sample_data['i8'] or { 0 }.u32() == 5
|
||||
assert sample_data['i16'] or { 0 }.u32() == 6
|
||||
assert sample_data['i32'] or { 0 }.u32() == 7
|
||||
assert sample_data['int'] or { 0 }.u32() == 8
|
||||
assert sample_data['i64'] or { 0 }.u32() == 9
|
||||
assert sample_data['f32'] or { 0 }.u32() == 2
|
||||
assert sample_data['f64'] or { 0 }.u32() == 1
|
||||
assert json.Any(true).u32() == 1
|
||||
assert json.Any('123').u32() == 123
|
||||
// invalid conversions
|
||||
assert sample_data['null'] or { 0 }.u32() == 0
|
||||
assert sample_data['arr'] or { 0 }.u32() == 0
|
||||
assert sample_data['obj'] or { 0 }.u32() == 0
|
||||
}
|
||||
|
||||
fn test_u64() {
|
||||
// valid conversions
|
||||
assert sample_data['u8'] or { 0 }.u64() == 1
|
||||
assert sample_data['u16'] or { 0 }.u64() == 2
|
||||
assert sample_data['u32'] or { 0 }.u64() == 3
|
||||
assert sample_data['u64'] or { 0 }.u64() == 4
|
||||
assert sample_data['i8'] or { 0 }.u64() == 5
|
||||
assert sample_data['i16'] or { 0 }.u64() == 6
|
||||
assert sample_data['i32'] or { 0 }.u64() == 7
|
||||
assert sample_data['int'] or { 0 }.u64() == 8
|
||||
assert sample_data['i64'] or { 0 }.u64() == 9
|
||||
assert sample_data['f32'] or { 0 }.u64() == 2
|
||||
assert sample_data['f64'] or { 0 }.u64() == 1
|
||||
assert json.Any(true).u64() == 1
|
||||
assert json.Any('123').u64() == 123
|
||||
// invalid conversions
|
||||
assert sample_data['null'] or { 0 }.u64() == 0
|
||||
assert sample_data['arr'] or { 0 }.u64() == 0
|
||||
assert sample_data['obj'] or { 0 }.u64() == 0
|
||||
}
|
||||
|
||||
fn test_as_map() {
|
||||
assert sample_data['u8'] or { 0 }.as_map()['0'] or { 0 }.u8() == 1
|
||||
assert sample_data['u16'] or { 0 }.as_map()['0'] or { 0 }.u16() == 2
|
||||
assert sample_data['u32'] or { 0 }.as_map()['0'] or { 0 }.u32() == 3
|
||||
assert sample_data['u64'] or { 0 }.as_map()['0'] or { 0 }.u64() == 4
|
||||
assert sample_data['i8'] or { 0 }.as_map()['0'] or { 0 }.i8() == 5
|
||||
assert sample_data['i16'] or { 0 }.as_map()['0'] or { 0 }.i16() == 6
|
||||
assert sample_data['i32'] or { 0 }.as_map()['0'] or { 0 }.i32() == 7
|
||||
assert sample_data['int'] or { 0 }.as_map()['0'] or { 0 }.int() == 8
|
||||
assert sample_data['i64'] or { 0 }.as_map()['0'] or { 0 }.i64() == 9
|
||||
assert sample_data['f32'] or { 0 }.as_map()['0'] or { 0 }.f32() == 2.3
|
||||
assert sample_data['f64'] or { 0 }.as_map()['0'] or { 0 }.f64() == 1.283
|
||||
assert sample_data['bool'] or { 0 }.as_map()['0'] or { 0 }.bool() == false
|
||||
assert sample_data['str'] or { 0 }.as_map()['0'] or { 0 }.str() == 'test'
|
||||
assert is_null(sample_data['null'] or { 0 }.as_map()['0'] or { 0 }) == true
|
||||
assert sample_data['arr'] or { 0 }.as_map()['0'] or { 0 }.str() == 'lol'
|
||||
assert sample_data['obj'] or { 0 }.as_map()['foo'] or { 0 }.int() == 10
|
||||
}
|
||||
|
||||
fn test_as_map_of_strings() {
|
||||
assert sample_data['obj']!.as_map() == {
|
||||
'foo': json.Any(10)
|
||||
}
|
||||
assert sample_data['obj']!.as_map_of_strings() == {
|
||||
'foo': '10'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_as_array() {
|
||||
assert sample_data['u8'] or { 0 }.as_array()[0].u8() == 1
|
||||
assert sample_data['u16'] or { 0 }.as_array()[0].u16() == 2
|
||||
assert sample_data['u32'] or { 0 }.as_array()[0].u32() == 3
|
||||
assert sample_data['u64'] or { 0 }.as_array()[0].u64() == 4
|
||||
assert sample_data['i8'] or { 0 }.as_array()[0].i8() == 5
|
||||
assert sample_data['i16'] or { 0 }.as_array()[0].i16() == 6
|
||||
assert sample_data['i32'] or { 0 }.as_array()[0].i32() == 7
|
||||
assert sample_data['int'] or { 0 }.as_array()[0].int() == 8
|
||||
assert sample_data['i64'] or { 0 }.as_array()[0].i64() == 9
|
||||
assert sample_data['f32'] or { 0 }.as_array()[0].f32() == 2.3
|
||||
assert sample_data['f64'] or { 0 }.as_array()[0].f64() == 1.283
|
||||
assert sample_data['bool'] or { 0 }.as_array()[0].bool() == false
|
||||
assert sample_data['str'] or { 0 }.as_array()[0].str() == 'test'
|
||||
assert is_null(sample_data['null'] or { 0 }.as_array()[0]) == true
|
||||
assert sample_data['arr'] or { 0 }.as_array()[0].str() == 'lol'
|
||||
assert sample_data['obj'] or { 0 }.as_array()[0].int() == 10
|
||||
}
|
||||
|
||||
fn test_bool() {
|
||||
// valid conversions
|
||||
assert sample_data['bool'] or { 0 }.bool() == false
|
||||
assert json.Any('true').bool() == true
|
||||
assert sample_data['u8'] or { 0 }.bool() == true
|
||||
assert sample_data['u16'] or { 0 }.bool() == true
|
||||
assert sample_data['u32'] or { 0 }.bool() == true
|
||||
assert sample_data['u64'] or { 0 }.bool() == true
|
||||
assert sample_data['i8'] or { 0 }.bool() == true
|
||||
assert sample_data['i16'] or { 0 }.bool() == true
|
||||
assert sample_data['i32'] or { 0 }.bool() == true
|
||||
assert sample_data['int'] or { 0 }.bool() == true
|
||||
assert sample_data['i64'] or { 0 }.bool() == true
|
||||
assert sample_data['f32'] or { 0 }.bool() == true
|
||||
assert sample_data['f64'] or { 0 }.bool() == true
|
||||
// invalid conversions
|
||||
assert sample_data['null'] or { 0 }.bool() == false
|
||||
assert sample_data['arr'] or { 0 }.bool() == false
|
||||
assert sample_data['obj'] or { 0 }.bool() == false
|
||||
}
|
||||
|
||||
fn test_str() {
|
||||
assert sample_data['u8'] or { 0 }.str() == '1'
|
||||
assert sample_data['u16'] or { 0 }.str() == '2'
|
||||
assert sample_data['u32'] or { 0 }.str() == '3'
|
||||
assert sample_data['u64'] or { 0 }.str() == '4'
|
||||
assert sample_data['i8'] or { 0 }.str() == '5'
|
||||
assert sample_data['i16'] or { 0 }.str() == '6'
|
||||
assert sample_data['i32'] or { 0 }.str() == '7'
|
||||
assert sample_data['int'] or { 0 }.str() == '8'
|
||||
assert sample_data['i64'] or { 0 }.str() == '9'
|
||||
assert sample_data['f32'] or { 0 }.str() == '2.3'
|
||||
assert sample_data['f64'] or { 0 }.str() == '1.283'
|
||||
assert sample_data['bool'] or { 0 }.str() == 'false'
|
||||
assert sample_data['str'] or { 0 }.str() == 'test'
|
||||
assert sample_data['null'] or { 0 }.str() == 'null'
|
||||
assert sample_data['arr'] or { 'not lol' }.str() == '["lol"]'
|
||||
assert sample_data.str() == '{"u8":1,"u16":2,"u32":3,"u64":4,"i8":5,"i16":6,"i32":7,"int":8,"i64":9,"f32":2.3,"f64":1.283,"bool":false,"str":"test","null":null,"arr":["lol"],"obj":{"foo":10}}'
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct StruWithJsonAttribute {
|
||||
a int
|
||||
name2 string @[json: 'name']
|
||||
b int
|
||||
}
|
||||
|
||||
struct StruWithSkipAttribute {
|
||||
a int
|
||||
name ?string @[skip]
|
||||
b int
|
||||
}
|
||||
|
||||
struct StruWithJsonSkipAttribute {
|
||||
a int
|
||||
name ?string @[json: '-']
|
||||
b int
|
||||
}
|
||||
|
||||
struct StruWithOmitemptyAttribute {
|
||||
a int
|
||||
name ?string @[omitempty]
|
||||
b int
|
||||
}
|
||||
|
||||
struct StruWithRawAttribute {
|
||||
a int
|
||||
name string @[raw]
|
||||
object string @[raw]
|
||||
b int
|
||||
}
|
||||
|
||||
struct StruWithOptionalRawAttribute {
|
||||
data ?string @[raw]
|
||||
}
|
||||
|
||||
struct StruWithRequiredAttribute {
|
||||
a int
|
||||
name string @[required]
|
||||
skip_and_required ?string @[required; skip]
|
||||
b int
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
a int @[required]
|
||||
}
|
||||
|
||||
struct JsonNullAttrBar {
|
||||
name ?string @[json_null]
|
||||
}
|
||||
|
||||
struct JsonNullAttrFoo {
|
||||
name ?string @[json_null]
|
||||
age ?int @[json_null]
|
||||
text ?string
|
||||
other ?JsonNullAttrBar
|
||||
other2 ?JsonNullAttrBar @[json_null]
|
||||
}
|
||||
|
||||
fn test_last_field_requiered() {
|
||||
assert json.decode[Foo]('{"a":0}')! == Foo{
|
||||
a: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn test_json_null_attribute() {
|
||||
assert json.encode(JsonNullAttrFoo{}) == '{"name":null,"age":null,"other2":null}'
|
||||
assert json.encode(JsonNullAttrFoo{ name: '' }) == '{"name":"","age":null,"other2":null}'
|
||||
assert json.encode(JsonNullAttrFoo{ age: 10 }) == '{"name":null,"age":10,"other2":null}'
|
||||
assert json.encode(JsonNullAttrFoo{
|
||||
age: 10
|
||||
other2: JsonNullAttrBar{
|
||||
name: none
|
||||
}
|
||||
}) == '{"name":null,"age":10,"other2":{"name":null}}'
|
||||
assert json.decode[JsonNullAttrFoo](json.encode(JsonNullAttrFoo{}))! == JsonNullAttrFoo{}
|
||||
}
|
||||
|
||||
fn test_skip_and_rename_attributes() {
|
||||
assert json.decode[StruWithJsonAttribute]('{"name": "hola1", "a": 2, "b": 3}')! == StruWithJsonAttribute{
|
||||
a: 2
|
||||
name2: 'hola1'
|
||||
b: 3
|
||||
}, '`json` attribute not working'
|
||||
|
||||
assert json.decode[StruWithSkipAttribute]('{"name": "hola2", "a": 2, "b": 3}')! == StruWithSkipAttribute{
|
||||
a: 2
|
||||
name: none
|
||||
b: 3
|
||||
}, '`skip` attribute not working'
|
||||
|
||||
assert json.decode[StruWithJsonSkipAttribute]('{"name": "hola3", "a": 2, "b": 3}')! == StruWithJsonSkipAttribute{
|
||||
a: 2
|
||||
name: none
|
||||
b: 3
|
||||
}, " `json: '-'` skip attribute not working"
|
||||
|
||||
assert json.decode[StruWithOmitemptyAttribute]('{"name": "", "a": 2, "b": 3}')! == StruWithOmitemptyAttribute{
|
||||
a: 2
|
||||
name: none
|
||||
b: 3
|
||||
}, '`omitempty` attribute not working'
|
||||
|
||||
assert json.decode[StruWithOmitemptyAttribute]('{"name": "hola", "a": 2, "b": 3}')! == StruWithOmitemptyAttribute{
|
||||
a: 2
|
||||
name: 'hola'
|
||||
b: 3
|
||||
}, '`omitempty` attribute not working'
|
||||
}
|
||||
|
||||
fn test_raw_attribute() {
|
||||
assert json.decode[StruWithRawAttribute]('{"name": "hola", "a": 2, "object": {"c": 4, "d": 5}, "b": 3}')! == StruWithRawAttribute{
|
||||
a: 2
|
||||
name: '"hola"'
|
||||
object: '{"c": 4, "d": 5}'
|
||||
b: 3
|
||||
}, '`raw` attribute not working'
|
||||
}
|
||||
|
||||
fn test_optional_raw_attribute_preserves_source_bytes() {
|
||||
raw_json := '{
|
||||
"data": { "test": 1 }
|
||||
}'
|
||||
dto := json.decode[StruWithOptionalRawAttribute](raw_json)!
|
||||
assert dto.data? == '{ "test": 1 }'
|
||||
}
|
||||
|
||||
fn test_required_attribute() {
|
||||
assert json.decode[StruWithRequiredAttribute]('{"name": "hola", "a": 2, "skip_and_required": "hola", "b": 3}')! == StruWithRequiredAttribute{
|
||||
a: 2
|
||||
name: 'hola'
|
||||
skip_and_required: none
|
||||
b: 3
|
||||
}, '`required` attribute not working'
|
||||
|
||||
mut has_error := false
|
||||
|
||||
json.decode[StruWithRequiredAttribute]('{"name": "hola", "a": 2, "b": 3}') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 31
|
||||
assert err.message == 'Data: missing required field `skip_and_required`'
|
||||
}
|
||||
has_error = true
|
||||
}
|
||||
|
||||
assert has_error, '`required` attribute not working. It should have failed'
|
||||
has_error = false
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// vtest build: !sanitize-memory-gcc && !sanitize-address-gcc && !sanitize-address-clang
|
||||
// vtest vflags: -autofree
|
||||
import x.json2
|
||||
|
||||
struct Config {
|
||||
bbb bool
|
||||
}
|
||||
|
||||
fn test_compilation_with_autofree() {
|
||||
cfg := Config{}
|
||||
s := json2.encode(cfg, prettify: true)
|
||||
assert s == '{\n "bbb": false\n}'
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
import x.json2
|
||||
import json as old_json
|
||||
import benchmark
|
||||
import time
|
||||
|
||||
// ./v wipe-cache && ./v -prod -cc gcc crun vlib/x/json2/tests/bench.v
|
||||
const max_iterations = 1_000_000
|
||||
// const max_iterations = 10 // trying figure out it is slower in small loop. I guess it is `fulfill_nodes` related. Any suggestion?
|
||||
|
||||
pub struct Stru {
|
||||
val int
|
||||
val2 string
|
||||
val3 Stru2
|
||||
}
|
||||
|
||||
pub struct Stru2 {
|
||||
a int
|
||||
churrasco string
|
||||
}
|
||||
|
||||
type SumTypes = Stru | bool | int | string | time.Time
|
||||
type StringAlias = string
|
||||
type IntAlias = int
|
||||
|
||||
enum Enum {
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e = 99
|
||||
f
|
||||
}
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
}
|
||||
|
||||
struct StructTypeOption[T] {
|
||||
mut:
|
||||
val ?T
|
||||
}
|
||||
|
||||
struct StructTypePointer[T] {
|
||||
mut:
|
||||
val &T
|
||||
}
|
||||
|
||||
fn main() {
|
||||
json_data := '{"_type": "Stru", "val": 1, "val2": "lala", "val3": {"a": 2, "churrasco": "leleu"}}'
|
||||
json_data1 := '{"val": "2"}'
|
||||
json_data2 := '{"val": 2}'
|
||||
|
||||
println('Starting benchmark...')
|
||||
println('max_iterations: ${max_iterations}')
|
||||
println('\n***Structure and maps***')
|
||||
|
||||
mut b := benchmark.start()
|
||||
|
||||
// Stru **********************************************************
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[Stru](json_data)!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[Stru](json_data)!')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := old_json.decode(Stru, json_data)!
|
||||
}
|
||||
|
||||
b.measure('old_json.decode(Stru, json_data)!\n')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[SumTypes](json_data)!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[SumTypes](json_data)!')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := old_json.decode(SumTypes, json_data)!
|
||||
}
|
||||
|
||||
b.measure('old_json.decode(SumTypes, json_data)!\n')
|
||||
|
||||
// StructType[string] **********************************************************
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[StructType[string]](json_data1)!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[StructType[string]](json_data1)!')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := old_json.decode(StructType[string], json_data1)!
|
||||
}
|
||||
|
||||
b.measure('old_json.decode(StructType[string], json_data1)!\n')
|
||||
|
||||
// StructTypeOption[string] **********************************************************
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[StructTypeOption[string]](json_data1)!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[StructTypeOption[string]](json_data1)!')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := old_json.decode(StructTypeOption[string], json_data1)!
|
||||
}
|
||||
|
||||
b.measure('old_json.decode(StructTypeOption[string], json_data1)!\n')
|
||||
|
||||
// StructType[int] **********************************************************
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[StructType[int]](json_data2)!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[StructType[int]](json_data2)!')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := old_json.decode(StructType[int], json_data2)!
|
||||
}
|
||||
|
||||
b.measure('old_json.decode(StructType[int], json_data2)!\n')
|
||||
|
||||
// map[string]string **********************************************************
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[map[string]string](json_data1)!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[map[string]string](json_data1)!')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := old_json.decode(map[string]string, json_data1)!
|
||||
}
|
||||
|
||||
b.measure('old_json.decode(map[string]string, json_data1)!\n')
|
||||
|
||||
// array **********************************************************
|
||||
|
||||
println('\n***arrays***')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[[]int]('[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]')!
|
||||
}
|
||||
|
||||
b.measure("json2.decode[[]int]('[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]')!")
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := old_json.decode([]int, '[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]')!
|
||||
}
|
||||
|
||||
b.measure("old_json.decode([]int, '[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]')!\n")
|
||||
|
||||
println('\n***simple types***')
|
||||
|
||||
// int **********************************************************
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[int]('2')!
|
||||
}
|
||||
|
||||
b.measure("json2.decode[int]('2')!")
|
||||
|
||||
// bool **********************************************************
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[bool]('true')!
|
||||
}
|
||||
|
||||
b.measure("json2.decode[bool]('true')!")
|
||||
|
||||
// time.Time **********************************************************
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[time.Time]('"2022-03-11T13:54:25"')!
|
||||
}
|
||||
|
||||
b.measure("json2.decode[time.Time]('2022-03-11T13:54:25')!")
|
||||
|
||||
// string **********************************************************
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[string]('"abcdefghijklimnopqrstuv"')!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[string](\'"abcdefghijklimnopqrstuv"\')!')
|
||||
|
||||
// alias **********************************************************
|
||||
|
||||
println('\n***alias***')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[IntAlias]('2')!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[IntAlias](2)!')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[StringAlias]('"abcdefghijklimnopqrstuv"')!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[StringAlias](\'"abcdefghijklimnopqrstuv"\')!')
|
||||
|
||||
println('\n***Sumtypes***')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[SumTypes]('2')!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[SumTypes](2)!')
|
||||
|
||||
for i := 0; i < max_iterations; i++ {
|
||||
_ := json2.decode[SumTypes]('"abcdefghijklimnopqrstuv"')!
|
||||
}
|
||||
|
||||
b.measure('json2.decode[SumTypes](\'"abcdefghijklimnopqrstuv"\')!')
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
import x.json2 as json
|
||||
|
||||
fn test_check_if_json_match() {
|
||||
// /* Test wrong string values */
|
||||
mut has_error := false
|
||||
|
||||
json.decode[string]('{"key": "value"}') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected string, but got object'
|
||||
}
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
has_error = false
|
||||
|
||||
json.decode[map[string]string]('"value"') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected object, but got string'
|
||||
}
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
has_error = false
|
||||
|
||||
json.decode[[]int]('{"key": "value"}') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected array, but got object'
|
||||
}
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
has_error = false
|
||||
|
||||
json.decode[string]('[1, 2, 3]') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected string, but got array'
|
||||
}
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
has_error = false
|
||||
|
||||
json.decode[int]('{"key": "value"}') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected number, but got object'
|
||||
}
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
has_error = false
|
||||
|
||||
json.decode[bool]('{"key": "value"}') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected boolean, but got object'
|
||||
}
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
has_error = false
|
||||
|
||||
// /* Right string values */
|
||||
json.decode[string]('"value"') or { assert false }
|
||||
|
||||
json.decode[map[string]string]('{"key": "value"}') or { assert false }
|
||||
|
||||
json.decode[[]int]('[1, 2, 3]') or { assert false }
|
||||
|
||||
json.decode[string]('"string"') or { assert false }
|
||||
|
||||
json.decode[int]('123') or { assert false }
|
||||
|
||||
json.decode[bool]('true') or { assert false }
|
||||
|
||||
json.decode[bool]('false') or { assert false }
|
||||
}
|
||||
|
||||
fn test_check_json_format() {
|
||||
json_and_error_message := [
|
||||
{
|
||||
'json': ']'
|
||||
'error': 'Syntax: unknown value kind'
|
||||
},
|
||||
{
|
||||
'json': '}'
|
||||
'error': 'Syntax: unknown value kind'
|
||||
},
|
||||
{
|
||||
'json': 'truely'
|
||||
'error': 'Syntax: invalid value. Unexpected character after boolean end'
|
||||
},
|
||||
{
|
||||
'json': '0[1]'
|
||||
'error': 'Syntax: invalid value. Unexpected character after number end'
|
||||
},
|
||||
{
|
||||
'json': '[1, 2, g3]'
|
||||
'error': 'Syntax: unknown value kind'
|
||||
},
|
||||
{
|
||||
'json': '[1, 2,, 3]'
|
||||
'error': 'Syntax: unknown value kind'
|
||||
},
|
||||
{
|
||||
'json': '{"key": 123'
|
||||
'error': 'Syntax: Expecting object key' // improve message
|
||||
},
|
||||
{
|
||||
'json': '{"key": 123,'
|
||||
'error': 'Syntax: EOF: expected object key'
|
||||
},
|
||||
{
|
||||
'json': '{"key": 123, "key2": 456,}'
|
||||
'error': 'Syntax: Cannot use `,`, before `}`'
|
||||
},
|
||||
{
|
||||
'json': '[[1, 2, 3], [4, 5, 6],]'
|
||||
'error': 'Syntax: Cannot use `,`, before `]`'
|
||||
},
|
||||
{
|
||||
'json': ' '
|
||||
'error': 'Syntax: EOF: empty json'
|
||||
},
|
||||
{
|
||||
'json': '"'
|
||||
'error': 'Syntax: EOF: string not closed'
|
||||
},
|
||||
{
|
||||
'json': '"not closed'
|
||||
'error': 'Syntax: EOF: string not closed'
|
||||
},
|
||||
{
|
||||
'json': '"\\"'
|
||||
'error': 'Syntax: EOF: string not closed'
|
||||
},
|
||||
{
|
||||
'json': '"\\u8"'
|
||||
'error': 'Syntax: short unicode escape sequence \\u8'
|
||||
},
|
||||
{
|
||||
'json': '['
|
||||
'error': 'Syntax: EOF: expected array end'
|
||||
},
|
||||
{
|
||||
'json': '[ '
|
||||
'error': 'Syntax: EOF: expected array end'
|
||||
},
|
||||
{
|
||||
'json': '{'
|
||||
'error': 'Syntax: EOF: expected object end'
|
||||
},
|
||||
{
|
||||
'json': '{ '
|
||||
'error': 'Syntax: EOF: expected object end'
|
||||
},
|
||||
{
|
||||
'json': '{"key": "value" '
|
||||
'error': 'Syntax: EOF: expected object end'
|
||||
},
|
||||
]
|
||||
|
||||
for json_and_error in json_and_error_message {
|
||||
mut has_error := false
|
||||
|
||||
json.decode[json.Any](json_and_error['json']) or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.message == json_and_error['error']
|
||||
}
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error ${json_and_error['error']}'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct AnyStruct[T] {
|
||||
val T
|
||||
}
|
||||
|
||||
struct OptAnyStruct[T] {
|
||||
val ?T
|
||||
}
|
||||
|
||||
/*struct OptAnyArrStruct {
|
||||
val []?json.Any
|
||||
}*/
|
||||
|
||||
fn test_values() {
|
||||
assert json.decode[AnyStruct[json.Any]]('{"val":5}')!.val.int() == 5
|
||||
assert json.decode[OptAnyStruct[json.Any]]('{}')!.val == none
|
||||
assert json.decode[AnyStruct[[]json.Any]]('{"val":[5,10]}')!.val.map(it.int()) == [
|
||||
5,
|
||||
10,
|
||||
]
|
||||
// assert json.decode[OptAnyArrStruct]('{"val":[5,null,10]}')!.val == [?json.Any(5),json.Null{},10] skipped because test still fails even though they're the same
|
||||
|
||||
assert json.encode[AnyStruct[json.Any]](AnyStruct[json.Any]{json.Any(5)}) == '{"val":5}'
|
||||
assert json.encode[OptAnyStruct[json.Any]](OptAnyStruct[json.Any]{none}) == '{}'
|
||||
assert json.encode[AnyStruct[[]json.Any]](AnyStruct[[]json.Any]{[json.Any(5), 10]}) == '{"val":[5,10]}'
|
||||
// assert json.encode[OptAnyArrStruct](OptAnyArrStruct{[?json.Any(5),none,10]}) == '{"val":[5,null,10]}' encode_array has not implemented optional arrays yet
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import x.json2 as json
|
||||
|
||||
type Issue24127Any = json.Any
|
||||
type Issue24127Map = map[string]Issue24127Any
|
||||
|
||||
const issue_24127_json_inputs = [
|
||||
'{}',
|
||||
'{"a":"b"}',
|
||||
'{"a":1}',
|
||||
'{"a":3.14}',
|
||||
'{"a":{"b":"c"}}',
|
||||
'{"a":true}',
|
||||
]
|
||||
|
||||
fn test_decode_alias_of_any_sumtype() {
|
||||
for input in issue_24127_json_inputs {
|
||||
direct := json.decode[json.Any](input)!
|
||||
aliased := json.decode[Issue24127Any](input)!
|
||||
assert json.encode(direct) == json.encode(aliased)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_decode_map_with_any_alias_value() {
|
||||
for input in issue_24127_json_inputs {
|
||||
direct := json.decode[map[string]json.Any](input)!
|
||||
aliased := json.decode[Issue24127Map](input)!
|
||||
assert json.encode(direct) == json.encode(aliased)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
module main
|
||||
|
||||
import json
|
||||
import x.json2
|
||||
|
||||
pub struct Data {
|
||||
name string
|
||||
data [][]f64
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
json_data := '{"name":"test","data":[[1,2,3],[4,5,6]]}'
|
||||
info := json.decode(Data, json_data)!
|
||||
info2 := json2.decode[Data](json_data)!
|
||||
assert info == info2
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
}
|
||||
|
||||
fn test_array_of_strings() {
|
||||
assert json.decode[[]int]('[1, 2, 3]')! == [1, 2, 3]
|
||||
|
||||
assert json.decode[[]string]('["a", "b", "c"]')! == ['a', 'b', 'c']
|
||||
|
||||
assert json.decode[[]bool]('[true, false, true]')! == [true, false, true]
|
||||
|
||||
assert json.decode[[]f64]('[1.1, 2.2, 3.3]')! == [1.1, 2.2, 3.3]
|
||||
|
||||
// nested arrays
|
||||
assert json.decode[[][]int]('[[1, 22], [333, 4444]]')! == [
|
||||
[1, 22],
|
||||
[333, 4444],
|
||||
]
|
||||
|
||||
assert json.decode[[][]string]('[["a", "b"], ["c", "d"]]')! == [
|
||||
['a', 'b'],
|
||||
['c', 'd'],
|
||||
]
|
||||
|
||||
assert json.decode[[][]bool]('[[true, false], [false, true]]')! == [
|
||||
[true, false],
|
||||
[false, true],
|
||||
]
|
||||
}
|
||||
|
||||
fn test_array_of_struct() {
|
||||
assert json.decode[[]StructType[int]]('[{"val": 1}, {"val": 2}, {"val": 3}, {"val": 4}]')! == [
|
||||
StructType{
|
||||
val: 1
|
||||
},
|
||||
StructType{
|
||||
val: 2
|
||||
},
|
||||
StructType{
|
||||
val: 3
|
||||
},
|
||||
StructType{
|
||||
val: 4
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import x.json2 as json
|
||||
|
||||
// Tests for strict mode: quoted strings are NOT decoded as numbers (JSON spec compliance).
|
||||
// Tests for default mode: quoted strings ARE accepted as numbers for compatibility.
|
||||
|
||||
// ===== STRICT MODE TESTS =====
|
||||
|
||||
fn test_strict_string_not_decoded_as_int() {
|
||||
json.decode[int]('"0"', strict: true) or {
|
||||
assert err is json.JsonDecodeError
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.message == 'Data: Expected number, but got string'
|
||||
}
|
||||
return
|
||||
}
|
||||
assert false, 'Expected JsonDecodeError for quoted number string in strict mode'
|
||||
}
|
||||
|
||||
fn test_strict_string_not_decoded_as_int_positive() {
|
||||
json.decode[int]('"100"', strict: true) or {
|
||||
assert err is json.JsonDecodeError
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.message == 'Data: Expected number, but got string'
|
||||
}
|
||||
return
|
||||
}
|
||||
assert false, 'Expected JsonDecodeError for quoted number string in strict mode'
|
||||
}
|
||||
|
||||
fn test_strict_string_not_decoded_as_float() {
|
||||
json.decode[f64]('"-23.6e1"', strict: true) or {
|
||||
assert err is json.JsonDecodeError
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.message == 'Data: Expected number, but got string'
|
||||
}
|
||||
return
|
||||
}
|
||||
assert false, 'Expected JsonDecodeError for quoted number string in strict mode'
|
||||
}
|
||||
|
||||
fn test_strict_string_in_array_not_decoded_as_int() {
|
||||
json.decode[[]int]('["100", 99, "98", 97]', strict: true) or {
|
||||
assert err is json.JsonDecodeError
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.message == 'Data: Expected number, but got string'
|
||||
}
|
||||
return
|
||||
}
|
||||
assert false, 'Expected JsonDecodeError for quoted number string in array in strict mode'
|
||||
}
|
||||
|
||||
// ===== DEFAULT MODE TESTS =====
|
||||
|
||||
fn test_default_string_decoded_as_int() {
|
||||
assert json.decode[int]('"0"')! == 0
|
||||
assert json.decode[int]('"100"')! == 100
|
||||
assert json.decode[int]('"-50"')! == -50
|
||||
}
|
||||
|
||||
fn test_default_string_decoded_as_float() {
|
||||
assert json.decode[f64]('"-23.6e1"')! == -236.0
|
||||
assert json.decode[f64]('"3.14"')! == 3.14
|
||||
}
|
||||
|
||||
fn test_default_string_in_array_decoded_as_int() {
|
||||
assert json.decode[[]int]('["100", "99", "98", "97"]')! == [100, 99, 98, 97]
|
||||
assert json.decode[[]int]('["100", 99, "98", 97]')! == [100, 99, 98, 97]
|
||||
}
|
||||
|
||||
// ===== COMMON TESTS (both modes) =====
|
||||
|
||||
fn test_valid_unquoted_numbers() {
|
||||
assert json.decode[int]('0')! == 0
|
||||
assert json.decode[int]('100')! == 100
|
||||
assert json.decode[int]('-50')! == -50
|
||||
assert json.decode[f64]('-23.6e1')! == -236.0
|
||||
assert json.decode[[]int]('[100, 99, 98, 97]')! == [100, 99, 98, 97]
|
||||
|
||||
// Strict mode also works for unquoted numbers
|
||||
assert json.decode[int]('0', strict: true)! == 0
|
||||
assert json.decode[int]('100', strict: true)! == 100
|
||||
}
|
||||
|
||||
fn test_invalid_number_string_fails() {
|
||||
json.decode[int]('"not_a_number"') or { return }
|
||||
assert false, 'Expected error for invalid number string'
|
||||
}
|
||||
|
||||
fn test_leading_plus_in_string() {
|
||||
assert json.decode[int]('"+100"')! == 100
|
||||
|
||||
json.decode[int]('"+100"', strict: true) or {
|
||||
assert err is json.JsonDecodeError
|
||||
return
|
||||
}
|
||||
assert false, 'Expected error in strict mode'
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import x.json2 as json
|
||||
import math.big
|
||||
|
||||
struct MyString implements json.StringDecoder, json.NumberDecoder, json.BooleanDecoder, json.NullDecoder {
|
||||
mut:
|
||||
data string
|
||||
}
|
||||
|
||||
pub fn (mut ms MyString) from_json_string(raw_string string) ! {
|
||||
ms.data = raw_string
|
||||
}
|
||||
|
||||
pub fn (mut ms MyString) from_json_number(raw_number string) ! {
|
||||
mut first := true
|
||||
|
||||
for digit in raw_number {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
ms.data += '-'
|
||||
}
|
||||
|
||||
ms.data += match digit {
|
||||
`-` { 'minus' }
|
||||
`.` { 'dot' }
|
||||
`e`, `E` { 'e' }
|
||||
`0` { 'zero' }
|
||||
`1` { 'one' }
|
||||
`2` { 'two' }
|
||||
`3` { 'three' }
|
||||
`4` { 'four' }
|
||||
`5` { 'five' }
|
||||
`6` { 'six' }
|
||||
`7` { 'seven' }
|
||||
`8` { 'eight' }
|
||||
`9` { 'nine' }
|
||||
else { 'none' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut ms MyString) from_json_boolean(boolean_value bool) {
|
||||
ms.data = if boolean_value { 'yes' } else { 'no' }
|
||||
}
|
||||
|
||||
pub fn (mut ms MyString) from_json_null() {
|
||||
ms.data = 'default value'
|
||||
}
|
||||
|
||||
struct NoCustom {
|
||||
a int
|
||||
b string
|
||||
}
|
||||
|
||||
type MyString2 = string
|
||||
|
||||
pub fn (mut ms MyString2) from_json_string(raw_string string) ! {
|
||||
ms = raw_string.replace('-', '')
|
||||
}
|
||||
|
||||
fn test_custom() {
|
||||
assert json.decode[NoCustom]('{"a": 99, "b": "hi"}')! == NoCustom{
|
||||
a: 99
|
||||
b: 'hi'
|
||||
}
|
||||
|
||||
assert json.decode[[]MyString]('["hi", -9.8e7, true, null]')! == [
|
||||
MyString{
|
||||
data: 'hi'
|
||||
},
|
||||
MyString{
|
||||
data: 'minus-nine-dot-eight-e-seven'
|
||||
},
|
||||
MyString{
|
||||
data: 'yes'
|
||||
},
|
||||
MyString{
|
||||
data: 'default value'
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn test_null() {
|
||||
assert json.decode[json.Any]('null]')! == json.Any(json.null)
|
||||
assert json.decode[json.Any]('{"hi": 90, "bye": ["lol", -1, null]}')!.str() == '{"hi":90,"bye":["lol",-1,null]}'
|
||||
}
|
||||
|
||||
fn test_big() {
|
||||
assert json.decode[big.Integer]('0')!.str() == '0'
|
||||
|
||||
assert json.decode[big.Integer]('12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890')!.str() == '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'
|
||||
|
||||
assert json.decode[big.Integer]('-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890')!.str() == '-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'
|
||||
}
|
||||
|
||||
fn test_alias() {
|
||||
assert json.decode[MyString2]('"h-e---l-lo"')! == 'hello'
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import json as stdjson
|
||||
import x.json2
|
||||
|
||||
struct Issue24950GateRecord {
|
||||
exit string
|
||||
}
|
||||
|
||||
struct Issue24950RecordBase {
|
||||
gate &Issue24950GateRecord = unsafe { nil } @[omitempty]
|
||||
}
|
||||
|
||||
struct Issue24950Record {
|
||||
Issue24950RecordBase
|
||||
}
|
||||
|
||||
struct Issue24950Wrapper {
|
||||
gate &Issue24950GateRecord = unsafe { nil }
|
||||
}
|
||||
|
||||
struct Issue25614Node {
|
||||
mut:
|
||||
value int
|
||||
children []&Issue25614Node
|
||||
}
|
||||
|
||||
struct OptionalPointerRegressionMessage {
|
||||
mut:
|
||||
id int
|
||||
text string
|
||||
reply_to ?&OptionalPointerRegressionMessage
|
||||
}
|
||||
|
||||
fn test_decode_struct_with_reference_field() {
|
||||
decoded := json2.decode[Issue24950Wrapper]('{"gate":{"exit":"north"}}')!
|
||||
|
||||
assert decoded.gate != unsafe { nil }
|
||||
assert decoded.gate.exit == 'north'
|
||||
}
|
||||
|
||||
fn test_decode_embedded_struct_with_reference_field() {
|
||||
empty_std := stdjson.decode(Issue24950Record, '{}')!
|
||||
empty_json2 := json2.decode[Issue24950Record]('{}')!
|
||||
|
||||
assert empty_std.Issue24950RecordBase.gate == unsafe { nil }
|
||||
assert empty_json2.Issue24950RecordBase.gate == unsafe { nil }
|
||||
|
||||
populated_std := stdjson.decode(Issue24950Record, '{"gate":{"exit":"north"}}')!
|
||||
populated_json2 := json2.decode[Issue24950Record]('{"gate":{"exit":"north"}}')!
|
||||
|
||||
assert populated_std.Issue24950RecordBase.gate != unsafe { nil }
|
||||
assert populated_json2.Issue24950RecordBase.gate != unsafe { nil }
|
||||
assert populated_std.Issue24950RecordBase.gate.exit == 'north'
|
||||
assert populated_json2.Issue24950RecordBase.gate.exit == 'north'
|
||||
}
|
||||
|
||||
fn test_decode_top_level_reference_to_primitive() {
|
||||
decoded := json2.decode[&int]('1')!
|
||||
|
||||
assert decoded != unsafe { nil }
|
||||
assert *decoded == 1
|
||||
}
|
||||
|
||||
fn test_decode_top_level_reference_to_primitive_null() {
|
||||
decoded := json2.decode[&int]('null')!
|
||||
|
||||
assert decoded == unsafe { nil }
|
||||
}
|
||||
|
||||
fn test_decode_top_level_reference_to_struct() {
|
||||
decoded := json2.decode[&Issue25614Node]('{"value":1}')!
|
||||
|
||||
assert decoded != unsafe { nil }
|
||||
assert decoded.value == 1
|
||||
}
|
||||
|
||||
fn test_decode_struct_with_array_of_references() {
|
||||
decoded := json2.decode[Issue25614Node]('{"children":[{"value":1},{"value":2}]}')!
|
||||
|
||||
assert decoded.children.len == 2
|
||||
assert decoded.children[0] != unsafe { nil }
|
||||
assert decoded.children[1] != unsafe { nil }
|
||||
assert decoded.children[0].value == 1
|
||||
assert decoded.children[1].value == 2
|
||||
}
|
||||
|
||||
fn test_decode_optional_reference_field() {
|
||||
message :=
|
||||
json2.decode[OptionalPointerRegressionMessage]('{"id":1,"text":"Hello","reply_to":{"id":2,"text":"Hi","reply_to":null}}')!
|
||||
|
||||
reply := message.reply_to or {
|
||||
assert false
|
||||
return
|
||||
}
|
||||
|
||||
assert reply.id == 2
|
||||
assert reply.text == 'Hi'
|
||||
assert reply.reply_to == none
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import x.json2 as json
|
||||
|
||||
enum Bar {
|
||||
a
|
||||
b @[json: 'BBB']
|
||||
c = 10
|
||||
e @[badattr: 'bad'; foobar]
|
||||
}
|
||||
|
||||
type BarAlias = Bar
|
||||
|
||||
fn test_number_decode() {
|
||||
assert json.decode[Bar]('0')! == Bar.a
|
||||
assert json.decode[Bar]('1')! == Bar.b
|
||||
assert json.decode[Bar]('10')! == Bar.c
|
||||
|
||||
assert json.decode[BarAlias]('0')! == Bar.a
|
||||
assert json.decode[BarAlias]('1')! == Bar.b
|
||||
assert json.decode[BarAlias]('10')! == Bar.c
|
||||
}
|
||||
|
||||
fn test_number_decode_fails() {
|
||||
if _ := json.decode[Bar]('2') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Number value: `2` does not match any field in enum: &Bar'
|
||||
}
|
||||
}
|
||||
|
||||
if _ := json.decode[BarAlias]('2') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Number value: `2` does not match any field in enum: &BarAlias'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_string_decode() {
|
||||
assert json.decode[Bar]('"a"')! == Bar.a
|
||||
assert json.decode[Bar]('"BBB"')! == Bar.b
|
||||
assert json.decode[Bar]('"c"')! == Bar.c
|
||||
|
||||
assert json.decode[BarAlias]('"a"')! == Bar.a
|
||||
assert json.decode[BarAlias]('"BBB"')! == Bar.b
|
||||
assert json.decode[BarAlias]('"c"')! == Bar.c
|
||||
}
|
||||
|
||||
fn test_string_decode_uses_only_json_attr() {
|
||||
assert json.decode[Bar]('"e"')! == Bar.e
|
||||
assert json.decode[BarAlias]('"e"')! == Bar.e
|
||||
for badval in ['"badattr: bad"', '"bad"', '"foobar"'] {
|
||||
if _ := json.decode[Bar](badval) {
|
||||
assert false, '${badval} must not be decoded to Bar'
|
||||
} else {
|
||||
assert true
|
||||
}
|
||||
if _ := json.decode[BarAlias](badval) {
|
||||
assert false, '${badval} must not be decoded to BarAlias'
|
||||
} else {
|
||||
assert true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_string_decode_fails() {
|
||||
if _ := json.decode[Bar]('"d"') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: String value: `d` does not match any field in enum: &Bar'
|
||||
}
|
||||
}
|
||||
|
||||
if _ := json.decode[BarAlias]('"d"') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: String value: `d` does not match any field in enum: &BarAlias'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_invalid_decode_fails() {
|
||||
if _ := json.decode[Bar]('true') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected number or string value for enum, got: boolean'
|
||||
}
|
||||
}
|
||||
|
||||
if _ := json.decode[BarAlias]('true') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected number or string value for enum, got: boolean'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_map_string_enum_decode() {
|
||||
m := json.decode[map[string]Bar]('{"foo": "a", "bar": "b", "baz": "c"}')!
|
||||
assert m['foo'] == .a
|
||||
assert m['bar'] == .b
|
||||
assert m['baz'] == .c
|
||||
|
||||
m2 := json.decode[map[string]Bar]('{"x": 0, "y": 1, "z": 10}')!
|
||||
assert m2['x'] == .a
|
||||
assert m2['y'] == .b
|
||||
assert m2['z'] == .c
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import x.json2
|
||||
|
||||
fn test_decode_escaped_string() {
|
||||
escaped_strings := ['test', 'test\\sd', 'test\nsd', '\ntest', 'test\\"', 'test\\', 'test\u1234ps',
|
||||
'test\u1234', '\u1234\\\t"', '', '\uff0f', 'test \uff0f test', '😀', 'text 😀 text']
|
||||
|
||||
json_string := json2.encode[[]string](escaped_strings)
|
||||
decoded_strings := json2.decode[[]string](json_string)!
|
||||
|
||||
assert escaped_strings == decoded_strings
|
||||
}
|
||||
|
||||
fn test_surrogate() {
|
||||
assert json2.decode[string](r'"\ud83d\ude00"')! == '😀'
|
||||
assert json2.decode[string](r'"\ud83d\ude00 text"')! == '😀 text'
|
||||
}
|
||||
|
||||
fn test_invalid_surrogate() {
|
||||
if x := json2.decode[string](r'"\ud83d"') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json2.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected a trail surrogate after a head surrogate, but got no valid escape sequence.'
|
||||
}
|
||||
}
|
||||
|
||||
if x := json2.decode[string](r'"\ud83d\n\n\n\n"') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json2.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected a trail surrogate after a head surrogate, but got no valid escape sequence.'
|
||||
}
|
||||
}
|
||||
|
||||
if x := json2.decode[string](r'"\ud83d\ud83d"') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json2.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Expected a trail surrogate after a head surrogate, but got D83D.'
|
||||
}
|
||||
}
|
||||
|
||||
if x := json2.decode[string](r'"\ude00\ud83d"') {
|
||||
assert false
|
||||
} else {
|
||||
if err is json2.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Data: Got trail surrogate: DE00 before head surrogate.'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import x.json2 as json
|
||||
|
||||
fn test_fixed_array() {
|
||||
mut expected := [3]int{}
|
||||
expected[0] = 1
|
||||
expected[1] = 2
|
||||
expected[2] = 3
|
||||
assert json.decode[[3]int]('[1, 2, 3]')! == expected
|
||||
}
|
||||
|
||||
fn test_fixed_array_to_few() {
|
||||
json.decode[[4]int]('[1, 2, 3]') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 8
|
||||
assert err.message == 'Data: Fixed size array expected 4 elements but got 3 elements'
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_fixed_array_to_many() {
|
||||
json.decode[[2]int]('[1, 2, 3]') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 8
|
||||
assert err.message == 'Data: Fixed size array expected 2 elements but got 3 elements'
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import json as old_json
|
||||
import x.json2 as json
|
||||
|
||||
struct Legacy64BitNumbers {
|
||||
signed i64
|
||||
unsigned u64
|
||||
}
|
||||
|
||||
fn test_decode_float_numbers_into_64bit_integer_fields_matches_json_module() {
|
||||
payload := '{"signed": -1756811041916.0, "unsigned": 1756811041916.0}'
|
||||
expected := old_json.decode(Legacy64BitNumbers, payload)!
|
||||
actual := json.decode[Legacy64BitNumbers](payload)!
|
||||
assert actual == expected
|
||||
}
|
||||
|
||||
fn test_decode_float_strings_into_64bit_integer_fields_in_default_mode() {
|
||||
payload := '{"signed": "-1756811041916.0", "unsigned": "1756811041916.0"}'
|
||||
actual := json.decode[Legacy64BitNumbers](payload)!
|
||||
assert actual.signed == i64(-1756811041916)
|
||||
assert actual.unsigned == u64(1756811041916)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import x.json2
|
||||
|
||||
fn decode_any_issue_24128(j string) !json2.Any {
|
||||
return json2.decode[json2.Any](j)
|
||||
}
|
||||
|
||||
fn decode_map_issue_24128(j string) !map[string]json2.Any {
|
||||
return json2.decode[map[string]json2.Any](j)
|
||||
}
|
||||
|
||||
fn test_decode_any_and_map_any_in_same_program() {
|
||||
for payload in [
|
||||
'{}',
|
||||
'{"a":"b"}',
|
||||
'{"a":1}',
|
||||
'{"a":3.14}',
|
||||
'{"a":{"b":"c"}}',
|
||||
'{"a":true}',
|
||||
] {
|
||||
decoded_any := decode_any_issue_24128(payload)!
|
||||
decoded_map := decode_map_issue_24128(payload)!
|
||||
|
||||
match decoded_any {
|
||||
map[string]json2.Any {
|
||||
assert decoded_any.str() == decoded_map.str()
|
||||
}
|
||||
else {
|
||||
assert false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_decode_map_any_keeps_numeric_values() {
|
||||
payload := {
|
||||
'sub': json2.Any('453636')
|
||||
'aud': json2.Any('Coachonko')
|
||||
'iss': json2.Any('Coachonko')
|
||||
'nbf': json2.Any(1688229809)
|
||||
'iat': json2.Any(1688229809)
|
||||
'jti': json2.Any('e0a26a81-9cc0-4f52-b672-4f6f3d3b44a5')
|
||||
'exp': json2.Any(1688273009)
|
||||
}
|
||||
encoded := json2.encode(payload)
|
||||
decoded := json2.decode[map[string]json2.Any](encoded)!
|
||||
|
||||
assert decoded.len == payload.len
|
||||
assert 'sub' in decoded
|
||||
assert 'aud' in decoded
|
||||
assert 'iss' in decoded
|
||||
assert 'nbf' in decoded
|
||||
assert 'iat' in decoded
|
||||
assert 'jti' in decoded
|
||||
assert 'exp' in decoded
|
||||
|
||||
assert decoded['sub']!.str() == '453636'
|
||||
assert decoded['aud']!.str() == 'Coachonko'
|
||||
assert decoded['iss']!.str() == 'Coachonko'
|
||||
assert decoded['jti']!.str() == 'e0a26a81-9cc0-4f52-b672-4f6f3d3b44a5'
|
||||
assert decoded['nbf']!.f64() == f64(1688229809)
|
||||
assert decoded['iat']!.f64() == f64(1688229809)
|
||||
assert decoded['exp']!.f64() == f64(1688273009)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import x.json2
|
||||
|
||||
struct CrossVerifyResult {
|
||||
confusion_matrix_map map[string]map[string]f64
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
x := json2.decode[CrossVerifyResult]('') or {
|
||||
assert err.msg().contains('1:1: Invalid json: empty string')
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import x.json2
|
||||
|
||||
const data = '
|
||||
{
|
||||
"comments": {
|
||||
"26788945": {
|
||||
"id": "26788945",
|
||||
"message": "some comment 1"
|
||||
},
|
||||
"26788946": {
|
||||
"id": "26788946",
|
||||
"message": "some comment 2"
|
||||
},
|
||||
"26788947": {
|
||||
"id": "26788947",
|
||||
"message": "some comment 3"
|
||||
}
|
||||
},
|
||||
"comments2": {
|
||||
"26788945": true,
|
||||
"26788946": false,
|
||||
"26788947": true
|
||||
},
|
||||
"comments3": {
|
||||
"26788945": 1,
|
||||
"26788946": 2,
|
||||
"26788947": 3
|
||||
}
|
||||
}
|
||||
'
|
||||
|
||||
pub struct Comment {
|
||||
id string
|
||||
message string
|
||||
}
|
||||
|
||||
struct Comments {
|
||||
mut:
|
||||
comments map[string]Comment
|
||||
comments2 map[string]bool
|
||||
comments3 map[string]int
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
mut root := json2.decode[Comments](data)!
|
||||
assert root.comments.len == 3
|
||||
assert root.comments['26788945']!.id == '26788945'
|
||||
assert root.comments['26788946']!.id == '26788946'
|
||||
assert root.comments['26788947']!.id == '26788947'
|
||||
|
||||
assert root.comments2['26788945']! == true
|
||||
assert root.comments2['26788946']! == false
|
||||
assert root.comments2['26788947']! == true
|
||||
|
||||
assert root.comments3['26788945']! == 1
|
||||
assert root.comments3['26788946']! == 2
|
||||
assert root.comments3['26788947']! == 3
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import x.json2
|
||||
|
||||
struct Dataset {
|
||||
values [][]string
|
||||
}
|
||||
|
||||
struct Dataset2 {
|
||||
values [][]u8
|
||||
}
|
||||
|
||||
fn test_string() {
|
||||
d := Dataset{
|
||||
values: [
|
||||
['a', 'b', 'c'],
|
||||
['d', 'e', 'f'],
|
||||
]
|
||||
}
|
||||
s := json2.encode(d)
|
||||
println(s)
|
||||
d2 := json2.decode[Dataset](s)!
|
||||
assert d2.str() == "Dataset{
|
||||
values: [['a', 'b', 'c'], ['d', 'e', 'f']]
|
||||
}"
|
||||
}
|
||||
|
||||
fn test_u8() {
|
||||
d := Dataset2{
|
||||
values: [
|
||||
[u8(1), 2, 3, 4, 5],
|
||||
[u8(2), 3, 4, 5, 6],
|
||||
]
|
||||
}
|
||||
s := json2.encode(d)
|
||||
println(s)
|
||||
d2 := json2.decode[Dataset2](s)!
|
||||
assert d2.str() == 'Dataset2{
|
||||
values: [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6]]
|
||||
}'
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import x.json2
|
||||
|
||||
// Test for issue #26503: Decoder incorrectly reads nested object fields
|
||||
// When a required field appears after unmatched fields with nested objects/arrays,
|
||||
// the decoder was incorrectly picking up values from nested structures.
|
||||
|
||||
struct Foo {
|
||||
id string @[required]
|
||||
title string @[required]
|
||||
}
|
||||
|
||||
fn test_decode_with_nested_objects_field_order() {
|
||||
// Test case 1: required fields appear before the nested array
|
||||
s1 := '{"id":"sss","title":"ttt","thumb":[{ "url":"i1.jpg","id":"000"}]}'
|
||||
f1 := json2.decode[Foo](s1)!
|
||||
|
||||
assert f1.id == 'sss', 'f1.id should be "sss" but got "${f1.id}"'
|
||||
assert f1.title == 'ttt', 'f1.title should be "ttt" but got "${f1.title}"'
|
||||
|
||||
// Test case 2: required fields appear after the nested array
|
||||
s2 := '{"title":"ttt","thumb":[{ "url":"i1.jpg","id":"000"}],"id":"sss"}'
|
||||
f2 := json2.decode[Foo](s2)!
|
||||
|
||||
assert f2.id == 'sss', 'f2.id should be "sss" but got "${f2.id}"'
|
||||
assert f2.title == 'ttt', 'f2.title should be "ttt" but got "${f2.title}"'
|
||||
|
||||
// Test case 3: nested array appears between required fields
|
||||
s3 := '{"id":"sss","thumb":[{ "url":"i1.jpg","id":"000"}],"title":"ttt"}'
|
||||
f3 := json2.decode[Foo](s3)!
|
||||
|
||||
assert f3.id == 'sss', 'f3.id should be "sss" but got "${f3.id}"'
|
||||
assert f3.title == 'ttt', 'f3.title should be "ttt" but got "${f3.title}"'
|
||||
}
|
||||
|
||||
fn test_decode_with_deeply_nested_objects() {
|
||||
// Test with deeply nested structures to ensure complete skipping
|
||||
s := '{"id":"outer","data":{"nested":{"deep":{"id":"inner","title":"inner_title"}}},"title":"outer_title"}'
|
||||
f := json2.decode[Foo](s)!
|
||||
|
||||
assert f.id == 'outer', 'id should be "outer" but got "${f.id}"'
|
||||
assert f.title == 'outer_title', 'title should be "outer_title" but got "${f.title}"'
|
||||
}
|
||||
|
||||
fn test_decode_with_multiple_nested_arrays() {
|
||||
// Test with multiple nested arrays to ensure complete skipping
|
||||
s := '{"id":"correct","items":[{"id":"a"},{"id":"b"}],"more":[{"id":"c"}],"title":"correct_title"}'
|
||||
f := json2.decode[Foo](s)!
|
||||
|
||||
assert f.id == 'correct', 'id should be "correct" but got "${f.id}"'
|
||||
assert f.title == 'correct_title', 'title should be "correct_title" but got "${f.title}"'
|
||||
}
|
||||
|
||||
struct BarWithOptional {
|
||||
id string @[required]
|
||||
title ?string
|
||||
extra string
|
||||
}
|
||||
|
||||
fn test_decode_optional_fields_with_nested() {
|
||||
// Test optional fields work correctly with nested structures
|
||||
s := '{"id":"main","nested":{"title":"nested_title"},"extra":"extra_value"}'
|
||||
b := json2.decode[BarWithOptional](s)!
|
||||
|
||||
assert b.id == 'main', 'id should be "main" but got "${b.id}"'
|
||||
assert b.extra == 'extra_value', 'extra should be "extra_value" but got "${b.extra}"'
|
||||
if title := b.title {
|
||||
assert false, 'title should be none but got "${title}"'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import x.json2 as json
|
||||
|
||||
fn test_number() {
|
||||
// Test u8
|
||||
assert json.decode[u8]('0')! == 0
|
||||
assert json.decode[u8]('1')! == 1
|
||||
assert json.decode[u8]('201')! == 201
|
||||
|
||||
// Test u16
|
||||
assert json.decode[u16]('0')! == 0
|
||||
assert json.decode[u16]('1')! == 1
|
||||
assert json.decode[u16]('201')! == 201
|
||||
|
||||
// Test u32
|
||||
assert json.decode[u32]('0')! == 0
|
||||
assert json.decode[u32]('1')! == 1
|
||||
assert json.decode[u32]('201')! == 201
|
||||
|
||||
// Test u64
|
||||
assert json.decode[u64]('0')! == 0
|
||||
assert json.decode[u64]('1')! == 1
|
||||
assert json.decode[u64]('201')! == 201
|
||||
|
||||
// Test i8
|
||||
assert json.decode[i8]('0')! == 0
|
||||
assert json.decode[i8]('1')! == 1
|
||||
assert json.decode[i8]('127')! == 127
|
||||
|
||||
assert json.decode[i8]('-1')! == -1
|
||||
assert json.decode[i8]('-127')! == -127
|
||||
|
||||
// Test i16
|
||||
assert json.decode[i16]('0')! == 0
|
||||
assert json.decode[i16]('1')! == 1
|
||||
assert json.decode[i16]('201')! == 201
|
||||
|
||||
assert json.decode[i16]('-1')! == -1
|
||||
assert json.decode[i16]('-201')! == -201
|
||||
|
||||
// Test int
|
||||
assert json.decode[int]('0')! == 0
|
||||
assert json.decode[int]('1')! == 1
|
||||
assert json.decode[int]('201')! == 201
|
||||
|
||||
assert json.decode[int]('-1')! == -1
|
||||
assert json.decode[int]('-201')! == -201
|
||||
|
||||
assert json.decode[int]('1234567890')! == 1234567890
|
||||
assert json.decode[int]('-1234567890')! == -1234567890
|
||||
|
||||
// Test i64
|
||||
assert json.decode[i64]('0')! == 0
|
||||
assert json.decode[i64]('1')! == 1
|
||||
assert json.decode[i64]('201')! == 201
|
||||
|
||||
assert json.decode[i64]('-1')! == -1
|
||||
assert json.decode[i64]('-201')! == -201
|
||||
|
||||
assert json.decode[i64]('1234567890')! == 1234567890
|
||||
assert json.decode[i64]('-1234567890')! == -1234567890
|
||||
|
||||
// Test f32
|
||||
assert json.decode[f32]('0')! == 0.0
|
||||
assert json.decode[f32]('1')! == 1.0
|
||||
assert json.decode[f32]('1.2')! == 1.2
|
||||
assert json.decode[f32]('-1.2')! == -1.2
|
||||
assert json.decode[f32]('201')! == 201.0
|
||||
|
||||
assert json.decode[f32]('-1')! == -1.0
|
||||
assert json.decode[f32]('-201')! == -201.0
|
||||
|
||||
assert json.decode[f32]('1234567890')! == 1234567890.0
|
||||
assert json.decode[f32]('-1234567890')! == -1234567890.0
|
||||
|
||||
// Test f64
|
||||
assert json.decode[f64]('0')! == 0.0
|
||||
assert json.decode[f64]('1')! == 1.0
|
||||
assert json.decode[f64]('1.2')! == 1.2
|
||||
assert json.decode[f64]('201')! == 201.0
|
||||
|
||||
assert json.decode[f64]('-1')! == -1.0
|
||||
assert json.decode[f64]('-1.2')! == -1.2
|
||||
assert json.decode[f64]('-201')! == -201.0
|
||||
|
||||
assert json.decode[f64]('1234567890')! == 1234567890.0
|
||||
assert json.decode[f64]('-1234567890')! == -1234567890.0
|
||||
|
||||
assert json.decode[f64]('1e10')! == 10000000000
|
||||
assert json.decode[f64]('1E10')! == 10000000000
|
||||
assert json.decode[f64]('1e+10')! == 10000000000
|
||||
assert json.decode[f64]('1e-10')! == 0.0000000001
|
||||
assert json.decode[f64]('-1e10')! == -10000000000
|
||||
assert json.decode[f64]('-1E-10')! == -0.0000000001
|
||||
assert json.decode[f64]('0.123e3')! - 123 < 0.0000001
|
||||
assert json.decode[f64]('10.5E+3')! == 10500
|
||||
|
||||
// Test Over/Underflow
|
||||
assert json.decode[i8]('127')! == 127
|
||||
assert json.decode[i8]('-128')! == -128
|
||||
|
||||
if x := json.decode[i8]('128') {
|
||||
assert false
|
||||
}
|
||||
if x := json.decode[i8]('130') {
|
||||
assert false
|
||||
}
|
||||
if x := json.decode[i8]('1000') {
|
||||
assert false
|
||||
}
|
||||
if x := json.decode[i8]('-129') {
|
||||
assert false
|
||||
}
|
||||
if x := json.decode[i8]('-130') {
|
||||
assert false
|
||||
}
|
||||
if x := json.decode[i8]('-1000') {
|
||||
assert false
|
||||
}
|
||||
}
|
||||
|
||||
fn test_boolean() {
|
||||
assert json.decode[bool]('false')! == false
|
||||
assert json.decode[bool]('true')! == true
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import x.json2 as json
|
||||
|
||||
pub struct Stru {
|
||||
val int
|
||||
val2 string
|
||||
val3 Stru2
|
||||
}
|
||||
|
||||
pub struct Stru2 {
|
||||
a int
|
||||
brazilian_steak string
|
||||
}
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
}
|
||||
|
||||
struct StructTypeOption[T] {
|
||||
mut:
|
||||
val ?T
|
||||
}
|
||||
|
||||
struct StructTypePointer[T] {
|
||||
mut:
|
||||
val &T
|
||||
}
|
||||
|
||||
fn test_array_of_strings() {
|
||||
// Structs
|
||||
assert json.decode[StructType[string]]('{"val": "2"}')! == StructType{
|
||||
val: '2'
|
||||
}
|
||||
assert json.decode[StructType[int]]('{"val": 2}')! == StructType{
|
||||
val: 2
|
||||
}
|
||||
|
||||
// maps
|
||||
assert json.decode[map[string]string]('{"val": "2"}')! == {
|
||||
'val': '2'
|
||||
}
|
||||
assert json.decode[map[string]int]('{"val": 2}')! == {
|
||||
'val': 2
|
||||
}
|
||||
|
||||
// nested map
|
||||
assert json.decode[map[string]map[string]string]('{"val": {"val2": "2"}}')! == {
|
||||
'val': {
|
||||
'val2': '2'
|
||||
}
|
||||
}
|
||||
|
||||
// nested struct
|
||||
assert json.decode[Stru]('{"val": 1, "val2": "lala", "val3": {"a": 2, "brazilian_steak": "leleu"}}')! == Stru{
|
||||
val: 1
|
||||
val2: 'lala'
|
||||
val3: Stru2{
|
||||
a: 2
|
||||
brazilian_steak: 'leleu'
|
||||
}
|
||||
}
|
||||
|
||||
// pretty print
|
||||
assert json.decode[Stru]('{
|
||||
"val": 1,
|
||||
"val2": "lala",
|
||||
"val3": {
|
||||
"a": 2,
|
||||
"brazilian_steak": "leleu"
|
||||
}
|
||||
}')! == Stru{
|
||||
val: 1
|
||||
val2: 'lala'
|
||||
val3: Stru2{
|
||||
a: 2
|
||||
brazilian_steak: 'leleu'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import x.json2
|
||||
import time
|
||||
|
||||
struct Person {
|
||||
mut:
|
||||
name string
|
||||
age ?int = 20
|
||||
birthday time.Time
|
||||
deathday ?time.Time
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
resp := '{"name": "Bob", "age": 20, "birthday": "2025-10-12 10:14:52"}'
|
||||
person := json2.decode[Person](resp)!
|
||||
assert '${person}' == "Person{
|
||||
name: 'Bob'
|
||||
age: Option(20)
|
||||
birthday: 2025-10-12 10:14:52
|
||||
deathday: Option(none)
|
||||
}"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import x.json2
|
||||
|
||||
struct F1 {
|
||||
f ?struct {
|
||||
a int
|
||||
}
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
j1 := json2.decode[F1]('{"f":{"a":1}}')!
|
||||
assert '${j1}' == 'F1{
|
||||
f: Option(struct {
|
||||
a: 1
|
||||
})
|
||||
}'
|
||||
|
||||
j2 := json2.decode[F1]('{}')!
|
||||
assert '${j2}' == 'F1{
|
||||
f: Option(none)
|
||||
}'
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import x.json2 as json
|
||||
|
||||
fn test_json_escape_low_chars() {
|
||||
assert json.decode[string](r'"\u001b"')! == '\u001b'
|
||||
assert json.decode[string](r'"\u000f"')! == '\u000f'
|
||||
assert json.decode[string](r'" "')! == '\u0020'
|
||||
assert json.decode[string](r'"\u0000"')! == '\u0000'
|
||||
}
|
||||
|
||||
fn test_json_string() {
|
||||
assert json.decode[string](r'"te\u2714st"')! == 'te✔st'
|
||||
assert json.decode[string](r'"te✔st"')! == 'te✔st'
|
||||
assert json.decode[string]('""')! == ''
|
||||
}
|
||||
|
||||
fn test_json_string_emoji() {
|
||||
assert json.decode[string](r'"🐈"')! == '🐈'
|
||||
assert json.decode[string](r'"💀"')! == '💀'
|
||||
assert json.decode[string](r'"🐈💀"')! == '🐈💀'
|
||||
}
|
||||
|
||||
fn test_json_string_non_ascii() {
|
||||
assert json.decode[string](r'"\u3072\u3089\u304c\u306a"')! == 'ひらがな'
|
||||
assert json.decode[string]('"a\\u3072b\\u3089c\\u304cd\\u306ae fgh"')! == 'aひbらcがdなe fgh'
|
||||
assert json.decode[string]('"\\u3072\\u3089\\u304c\\u306a"')! == 'ひらがな'
|
||||
}
|
||||
|
||||
fn test_utf8_strings_are_not_modified() {
|
||||
assert json.decode[string]('"ü"')! == 'ü'
|
||||
assert json.decode[string]('"Schilddrüsenerkrankungen"')! == 'Schilddrüsenerkrankungen'
|
||||
}
|
||||
|
||||
fn test_json_string_invalid_escapes() {
|
||||
mut has_error := false
|
||||
|
||||
json.decode[string](r'"\x"') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 3
|
||||
assert err.message == 'Syntax: unknown escape sequence'
|
||||
}
|
||||
has_error = true
|
||||
} // Invalid escape
|
||||
|
||||
assert has_error, 'Expected error'
|
||||
has_error = false
|
||||
|
||||
json.decode[string](r'"\u123"') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 3
|
||||
assert err.message == 'Syntax: short unicode escape sequence \\u123'
|
||||
}
|
||||
has_error = true
|
||||
} // Incomplete Unicode
|
||||
|
||||
assert has_error, 'Expected error'
|
||||
}
|
||||
|
||||
fn test_json_string_whitespace() {
|
||||
// Test strings with whitespace
|
||||
assert json.decode[string]('" "')! == ' '
|
||||
assert json.decode[string]('"\t\n\r"')! == '\t\n\r'
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
import x.json2 as json
|
||||
import time
|
||||
import math
|
||||
|
||||
const fixed_time = time.new(
|
||||
year: 2022
|
||||
month: 3
|
||||
day: 11
|
||||
hour: 13
|
||||
minute: 54
|
||||
second: 25
|
||||
)
|
||||
|
||||
type StringAlias = string
|
||||
type BoolAlias = bool
|
||||
type IntAlias = int
|
||||
type TimeAlias = time.Time
|
||||
type StructAlias = StructType[int]
|
||||
type EnumAlias = Enumerates
|
||||
|
||||
type SumTypes = StructType[string] | []SumTypes | []string | bool | string | time.Time | u32
|
||||
|
||||
enum Enumerates {
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e = 99
|
||||
f
|
||||
}
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
}
|
||||
|
||||
struct StructTypeOption[T] {
|
||||
mut:
|
||||
val ?T
|
||||
}
|
||||
|
||||
struct StructTypePointer[T] {
|
||||
mut:
|
||||
val &T
|
||||
}
|
||||
|
||||
fn test_types() {
|
||||
assert json.decode[StructType[string]]('{"val": ""}')!.val == ''
|
||||
|
||||
assert json.decode[StructType[string]]('{"val": "2"}')!.val == '2'
|
||||
|
||||
assert json.decode[StructType[int]]('{"val": 2}')!.val == 2
|
||||
|
||||
assert json.decode[StructType[map[string]string]]('{"val": {"val1": "test"}}')!.val['val1'] == 'test'
|
||||
|
||||
assert json.decode[StructType[Enumerates]]('{"val": 0}')!.val == Enumerates.a
|
||||
assert json.decode[StructType[Enumerates]]('{"val": 1}')!.val == Enumerates.b
|
||||
|
||||
assert json.decode[StructType[time.Time]]('{"val": "2022-03-11T13:54:25.000Z"}')!.val == fixed_time
|
||||
assert json.decode[StructType[time.Time]]('{"val": "2022-03-11T13:54:25.000Z"}')!.val.unix() == fixed_time.unix()
|
||||
}
|
||||
|
||||
fn test_option_types() {
|
||||
if x := json.decode[StructTypeOption[string]]('{}')!.val {
|
||||
assert false, 'Should return none'
|
||||
} else {
|
||||
assert err.msg() == ''
|
||||
assert true
|
||||
}
|
||||
|
||||
if x := json.decode[StructTypeOption[string]]('{"val": "2"}')!.val {
|
||||
assert x == '2'
|
||||
} else {
|
||||
assert false, 'Should not return none'
|
||||
}
|
||||
}
|
||||
|
||||
// Test structure for basic number types
|
||||
struct JsonNumbers {
|
||||
val_i8 i8
|
||||
val_i16 i16
|
||||
val_i32 i32
|
||||
val_i64 i64
|
||||
val_u8 u8
|
||||
val_u16 u16
|
||||
val_u32 u32
|
||||
val_u64 u64
|
||||
val_int int
|
||||
val_f32 f32
|
||||
val_f64 f64
|
||||
}
|
||||
|
||||
// Test structure for float precision
|
||||
struct JsonFloats {
|
||||
val_f32 f32
|
||||
val_f64 f64
|
||||
}
|
||||
|
||||
// Test basic functionality
|
||||
struct JsonU8 {
|
||||
val1 u8
|
||||
val2 u8
|
||||
}
|
||||
|
||||
fn test_u8_values() {
|
||||
x := JsonU8{
|
||||
val1: 58
|
||||
val2: 58
|
||||
}
|
||||
|
||||
str := json.encode(x)
|
||||
y := json.decode[JsonU8](str) or { panic(err) }
|
||||
assert x == y
|
||||
println('✓ Basic u8 test passed')
|
||||
}
|
||||
|
||||
fn test_all_number_types() {
|
||||
// Test normal range values
|
||||
x := JsonNumbers{
|
||||
val_i8: -100
|
||||
val_i16: -1000
|
||||
val_i32: -100000
|
||||
val_i64: -1000000000
|
||||
val_u8: 58
|
||||
val_u16: 2000
|
||||
val_u32: 200000
|
||||
val_u64: 2000000000
|
||||
val_int: 300000
|
||||
val_f32: 3.14
|
||||
val_f64: 2.71828
|
||||
}
|
||||
|
||||
str := json.encode(x)
|
||||
y := json.decode[JsonNumbers](str) or { panic('Decoding failed: ${err}') }
|
||||
|
||||
assert x.val_i8 == y.val_i8
|
||||
assert x.val_i16 == y.val_i16
|
||||
assert x.val_i32 == y.val_i32
|
||||
assert x.val_i64 == y.val_i64
|
||||
assert x.val_u8 == y.val_u8
|
||||
assert x.val_u16 == y.val_u16
|
||||
assert x.val_u32 == y.val_u32
|
||||
assert x.val_u64 == y.val_u64
|
||||
assert x.val_int == y.val_int
|
||||
assert math.alike(x.val_f32, y.val_f32)
|
||||
assert math.alike(x.val_f64, y.val_f64)
|
||||
|
||||
println('✓ All number types test passed')
|
||||
}
|
||||
|
||||
fn test_number_boundaries() {
|
||||
// Test minimum and maximum values for various types
|
||||
test_cases := [
|
||||
json.encode(JsonU8{ val1: 0, val2: 255 }),
|
||||
json.encode(JsonU8{ val1: u8(128), val2: u8(255) }),
|
||||
'{"min_i8": -128, "max_i8": 127}',
|
||||
'{"min_u8": 0, "max_u8": 255}',
|
||||
'{"zero": 0}',
|
||||
]!
|
||||
|
||||
for case in test_cases {
|
||||
result := json.decode[JsonU8](case)!
|
||||
assert result.val1 >= 0 && result.val1 <= 255
|
||||
assert result.val2 >= 0 && result.val2 <= 255
|
||||
}
|
||||
println('✓ Boundary values test passed')
|
||||
}
|
||||
|
||||
fn test_quoted_numbers_in_strict_mode() {
|
||||
quoted_number_cases := [
|
||||
'{"val1": "123", "val2": "45"}',
|
||||
'{"val1": 123, "val2": "45"}',
|
||||
'{"val1": "255", "val2": 0}',
|
||||
]!
|
||||
|
||||
for case in quoted_number_cases {
|
||||
json.decode[JsonU8](case, strict: true) or { continue }
|
||||
panic('Expected decoding to fail for quoted number in strict mode but succeeded: ${case}')
|
||||
}
|
||||
println('✓ Quoted numbers correctly rejected in strict mode test passed')
|
||||
}
|
||||
|
||||
fn test_quoted_numbers_in_default_mode() {
|
||||
assert json.decode[JsonU8]('{"val1": "123", "val2": "45"}')! == JsonU8{
|
||||
val1: 123
|
||||
val2: 45
|
||||
}
|
||||
assert json.decode[JsonU8]('{"val1": 123, "val2": "45"}')! == JsonU8{
|
||||
val1: 123
|
||||
val2: 45
|
||||
}
|
||||
println('✓ Quoted numbers accepted in default mode test passed')
|
||||
}
|
||||
|
||||
fn test_error_conditions() {
|
||||
// Test invalid inputs that should trigger errors
|
||||
invalid_cases := [
|
||||
'{"val1": "abc", "val2": 58}', // Non-numeric string
|
||||
'{"val1": "", "val2": 58}', // Empty string
|
||||
'{"val1": 300, "val2": 58}', // Out of u8 range (300 > 255)
|
||||
'{"val1": -1, "val2": 58}', // Negative value for u8
|
||||
'{"val1": 123.45, "val2": 58}', // Float for integer type
|
||||
]!
|
||||
|
||||
mut error_count := 0
|
||||
for case in invalid_cases {
|
||||
result := json.decode[JsonU8](case) or {
|
||||
error_count++
|
||||
continue // Expected failure, error handling works correctly
|
||||
}
|
||||
// If we reach here, it means decoding succeeded when it should have failed
|
||||
panic('Expected decoding to fail but succeeded: ${case}')
|
||||
}
|
||||
assert error_count > 0 // Ensure we caught some errors
|
||||
println('✓ Error handling test passed')
|
||||
}
|
||||
|
||||
fn test_float_precision() {
|
||||
// Test floating point precision and scientific notation
|
||||
test_cases := [
|
||||
JsonFloats{
|
||||
val_f32: 3.14159265
|
||||
val_f64: 2.718281828459045
|
||||
},
|
||||
JsonFloats{
|
||||
val_f32: 1.0e10
|
||||
val_f64: 1.0e-10
|
||||
},
|
||||
JsonFloats{
|
||||
val_f32: -123.456
|
||||
val_f64: -789.012
|
||||
},
|
||||
JsonFloats{
|
||||
val_f32: 0.0
|
||||
val_f64: 0.0
|
||||
},
|
||||
]!
|
||||
|
||||
for x in test_cases {
|
||||
str := json.encode(x)
|
||||
y := json.decode[JsonFloats](str) or { panic('Float decoding failed: ${err}') }
|
||||
|
||||
// Use relative error for assertions
|
||||
assert math.alike(y.val_f32, x.val_f32)
|
||||
assert math.alike(y.val_f64, x.val_f64)
|
||||
}
|
||||
println('✓ Float precision test passed')
|
||||
}
|
||||
|
||||
fn test_performance_large_scale() {
|
||||
// Performance test: decode large numbers of values
|
||||
mut total := u64(0)
|
||||
|
||||
for i in 0 .. 1000 {
|
||||
x := JsonU8{
|
||||
val1: u8(i % 256)
|
||||
val2: u8((i * 7) % 256)
|
||||
}
|
||||
str := json.encode(x)
|
||||
y := json.decode[JsonU8](str) or { panic('Performance test decoding failed: ${err}') }
|
||||
total += y.val1 + y.val2
|
||||
}
|
||||
|
||||
assert total > 0 // Ensure the loop executed
|
||||
println('✓ Performance test passed')
|
||||
}
|
||||
|
||||
fn test_special_float_values() {
|
||||
// Test special float values
|
||||
|
||||
// Json does not support nan, +-inf yet
|
||||
assert json.encode(math.nan()) == 'nan'
|
||||
assert json.encode(math.inf(1)) == '+inf'
|
||||
assert json.encode(math.inf(-1)) == '-inf'
|
||||
|
||||
println('✓ Special float values test passed')
|
||||
}
|
||||
|
||||
fn test_json_format_consistency() {
|
||||
// Test that encoding and decoding preserves data integrity
|
||||
original := JsonNumbers{
|
||||
val_i8: 100
|
||||
val_i16: 1000
|
||||
val_i32: 100000
|
||||
val_i64: 1000000000
|
||||
val_u8: 33
|
||||
val_u16: 2000
|
||||
val_u32: 200000
|
||||
val_u64: 2000000000
|
||||
val_int: 300000
|
||||
val_f32: 3.14
|
||||
val_f64: 2.71828
|
||||
}
|
||||
|
||||
// Encode and decode multiple times
|
||||
str1 := json.encode(original)
|
||||
decoded1 := json.decode[JsonNumbers](str1) or { panic('First decode failed: ${err}') }
|
||||
str2 := json.encode(decoded1)
|
||||
decoded2 := json.decode[JsonNumbers](str2) or { panic('Second decode failed: ${err}') }
|
||||
|
||||
// All versions should be equivalent
|
||||
assert decoded1 == decoded2
|
||||
println('✓ JSON format consistency test passed')
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import json
|
||||
|
||||
fn test_option_types() {
|
||||
assert json.decode[StructTypeOption[string]]('{}')!.val == none
|
||||
assert json.decode[StructTypeOption[string]]('{"val": ""}')!.val == ''
|
||||
assert json.decode[StructTypeOption[string]]('{"val": "0"}')!.val == '0'
|
||||
assert json.decode[StructTypeOption[string]]('{"val": "1"}')!.val == '1'
|
||||
assert json.decode[StructTypeOption[string]]('{"val": "2"}')!.val == '2'
|
||||
assert json.decode[StructTypeOption[string]]('{"val": 0}')!.val == '0'
|
||||
assert json.decode[StructTypeOption[string]]('{"val": 1}')!.val == '1'
|
||||
assert json.decode[StructTypeOption[string]]('{"val": 2}')!.val == '2'
|
||||
assert json.decode[StructTypeOption[string]]('{"val": "true"}')!.val == 'true'
|
||||
assert json.decode[StructTypeOption[string]]('{"val": "false"}')!.val == 'false'
|
||||
assert json.decode[StructTypeOption[string]]('{"val": true}')!.val == 'true'
|
||||
assert json.decode[StructTypeOption[string]]('{"val": false}')!.val == 'false'
|
||||
|
||||
assert json.decode[StructTypeOption[bool]]('{}')!.val == none
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": ""}')!.val == false
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": "0"}')!.val == false
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": "1"}')!.val == true
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": "2"}')!.val == true
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": 0}')!.val == false
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": 1}')!.val == true
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": 2}')!.val == true
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": "true"}')!.val == true
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": "false"}')!.val == false
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": true}')!.val == true
|
||||
assert json.decode[StructTypeOption[bool]]('{"val": false}')!.val == false
|
||||
|
||||
assert json.decode[StructTypeOption[int]]('{}')!.val == none
|
||||
assert json.decode[StructTypeOption[int]]('{"val": ""}')!.val == 0
|
||||
assert json.decode[StructTypeOption[int]]('{"val": "0"}')!.val == 0
|
||||
assert json.decode[StructTypeOption[int]]('{"val": "1"}')!.val == 1
|
||||
assert json.decode[StructTypeOption[int]]('{"val": "2"}')!.val == 2
|
||||
assert json.decode[StructTypeOption[int]]('{"val": 0}')!.val == 0
|
||||
assert json.decode[StructTypeOption[int]]('{"val": 1}')!.val == 1
|
||||
assert json.decode[StructTypeOption[int]]('{"val": 2}')!.val == 2
|
||||
assert json.decode[StructTypeOption[int]]('{"val": "true"}')!.val == 1
|
||||
assert json.decode[StructTypeOption[int]]('{"val": "false"}')!.val == 0
|
||||
assert json.decode[StructTypeOption[int]]('{"val": true}')!.val == 1
|
||||
assert json.decode[StructTypeOption[int]]('{"val": false}')!.val == 0
|
||||
}
|
||||
|
||||
fn test_array() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn test_option_array() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn test_alias() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn test_option_alias() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn test_sumtypes() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn test_option_sumtypes() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn test_pointer() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn test_caos() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn test_caos_array() {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
import x.json2 as json
|
||||
|
||||
fn test_raw_decode_string() {
|
||||
str := json.decode[json.Any]('"Hello!"')!
|
||||
assert str.str() == 'Hello!'
|
||||
}
|
||||
|
||||
fn test_raw_decode_string_escape() {
|
||||
jstr := json.decode[json.Any]('"\\u001b"')!
|
||||
str := jstr.str()
|
||||
assert str.len == 1
|
||||
assert str[0] == 27
|
||||
}
|
||||
|
||||
fn test_raw_decode_number() {
|
||||
num := json.decode[json.Any]('123')!
|
||||
assert num.int() == 123
|
||||
}
|
||||
|
||||
fn test_raw_decode_array() {
|
||||
raw_arr := json.decode[json.Any]('["Foo", 1]')!
|
||||
arr := raw_arr.as_array()
|
||||
assert arr[0] or { 0 }.str() == 'Foo'
|
||||
assert arr[1] or { 0 }.int() == 1
|
||||
}
|
||||
|
||||
fn test_raw_decode_bool() {
|
||||
bol := json.decode[json.Any]('false')!
|
||||
assert bol.bool() == false
|
||||
}
|
||||
|
||||
fn test_raw_decode_map() {
|
||||
raw_mp := json.decode[json.Any]('{"name":"Bob","age":20}')!
|
||||
mp := raw_mp.as_map()
|
||||
assert mp['name'] or { 0 }.str() == 'Bob'
|
||||
assert mp['age'] or { 0 }.int() == 20
|
||||
}
|
||||
|
||||
fn test_raw_decode_invalid() {
|
||||
json.decode[json.Any]('1z') or {
|
||||
assert err.msg().contains('1:2: Invalid json: Syntax: invalid value. Unexpected character after number end')
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_raw_decode_string_with_dollarsign() {
|
||||
str := json.decode[json.Any](r'"Hello $world"')!
|
||||
assert str.str() == r'Hello $world'
|
||||
}
|
||||
|
||||
fn test_raw_decode_map_with_escaped_backslashes_before_string_end() {
|
||||
content := r'{"some_name": "some_value \n [ ] { } ( ) , ; ? * = ! \\@ \\"}'
|
||||
raw_mp := json.decode[json.Any](content)!
|
||||
mp := raw_mp.as_map()
|
||||
assert mp['some_name'] or { 0 }.str() == 'some_value \n [ ] { } ( ) , ; ? * = ! \\@ \\'
|
||||
}
|
||||
|
||||
fn test_raw_decode_map_with_whitespaces() {
|
||||
raw_mp := json.decode[json.Any](' \n\t{"name":"Bob","age":20}\n\t')!
|
||||
mp := raw_mp.as_map()
|
||||
assert mp['name'] or { 0 }.str() == 'Bob'
|
||||
assert mp['age'] or { 0 }.int() == 20
|
||||
}
|
||||
|
||||
fn test_raw_decode_map_invalid() {
|
||||
json.decode[json.Any]('{"name","Bob","age":20}') or {
|
||||
assert err.msg().contains('1:8: Invalid json: Syntax: expected `:`, got `,`')
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_raw_decode_array_invalid() {
|
||||
json.decode[json.Any]('["Foo", 1,}') or {
|
||||
assert err.msg().contains('1:11: Invalid json: Syntax: unknown value kind')
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
int []int
|
||||
str []string
|
||||
f32 []f32
|
||||
oint []?int
|
||||
}
|
||||
|
||||
// fn test_decode_array_fields() {
|
||||
// input := '{"int":[0, 1], "str":["2", "3"], "f32": [4.0, 5.0], "oint": [6, null]}'
|
||||
// foo := json.decode[Foo](input)!
|
||||
// assert foo.int == [0, 1]
|
||||
// assert foo.str == ['2', '3']
|
||||
// assert foo.f32 == [f32(4.0), 5.0]
|
||||
// a, b := foo.oint[0], foo.oint[1]
|
||||
// assert a? == 6
|
||||
// assert b? == 0
|
||||
//}
|
||||
|
||||
struct ContactItem {
|
||||
description string
|
||||
telnr string
|
||||
}
|
||||
|
||||
struct User {
|
||||
name string
|
||||
age int
|
||||
contact ContactItem
|
||||
}
|
||||
|
||||
fn test_decode_missing_comma() {
|
||||
data := '{
|
||||
"name": "Frodo",
|
||||
"age": 25
|
||||
"contact": {
|
||||
"description": "descr",
|
||||
"telnr": "+32333"
|
||||
}
|
||||
}'
|
||||
user := json.decode[User](data) or { return }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import x.json2
|
||||
|
||||
struct Disk {
|
||||
dev string
|
||||
size ?struct {
|
||||
value u64
|
||||
}
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
disk := Disk{
|
||||
size: struct {
|
||||
value: 123
|
||||
}
|
||||
}
|
||||
disk_str := json2.encode[Disk](disk)
|
||||
assert disk_str == '{"dev":"","size":{"value":123}}'
|
||||
}
|
||||
|
||||
fn test_none() {
|
||||
disk := Disk{}
|
||||
disk_str := json2.encode[Disk](disk)
|
||||
assert disk_str == '{"dev":""}'
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import x.json2
|
||||
|
||||
struct RefArrayItem {
|
||||
a int
|
||||
}
|
||||
|
||||
fn ref_array_item(value int, is_nil bool) &RefArrayItem {
|
||||
return if is_nil {
|
||||
unsafe { nil }
|
||||
} else {
|
||||
&RefArrayItem{
|
||||
a: value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_json2_encode_array_of_references_with_nil() {
|
||||
value := [
|
||||
ref_array_item(1, false),
|
||||
ref_array_item(0, true),
|
||||
ref_array_item(2, false),
|
||||
]
|
||||
assert json2.encode(value) == '[{"a":1},null,{"a":2}]'
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct Baz {
|
||||
c bool = true
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
Baz
|
||||
a int = 1
|
||||
b int = 2
|
||||
}
|
||||
|
||||
struct Bar {
|
||||
Foo
|
||||
a string = '1'
|
||||
c string = '3'
|
||||
}
|
||||
|
||||
struct Environment {
|
||||
vflags string
|
||||
}
|
||||
|
||||
struct HistoryEvent {
|
||||
Environment
|
||||
}
|
||||
|
||||
struct History {
|
||||
pub mut:
|
||||
history_events []HistoryEvent
|
||||
}
|
||||
|
||||
struct Classifier {
|
||||
History
|
||||
}
|
||||
|
||||
struct RoundtripInner {
|
||||
inner []f64
|
||||
}
|
||||
|
||||
struct RoundtripOuter {
|
||||
RoundtripInner
|
||||
test f64
|
||||
}
|
||||
|
||||
struct Article {
|
||||
title string
|
||||
description string
|
||||
}
|
||||
|
||||
struct ArticlePlus {
|
||||
Article
|
||||
perex string
|
||||
}
|
||||
|
||||
fn test_embed() {
|
||||
assert json.encode(Bar{}) == '{"Foo.Baz.c":true,"Foo.a":1,"b":2,"a":"1","c":"3"}'
|
||||
assert json.encode(Bar{}, prettify: true) == '{
|
||||
"Foo.Baz.c": true,
|
||||
"Foo.a": 1,
|
||||
"b": 2,
|
||||
"a": "1",
|
||||
"c": "3"
|
||||
}'
|
||||
}
|
||||
|
||||
fn test_decode_embed_with_prefixed_keys() {
|
||||
decoded := json.decode[Bar]('{"a":"1","c":"3","Foo.a":4,"b":2,"Foo.Baz.c":false}')!
|
||||
assert decoded.a == '1'
|
||||
assert decoded.c == '3'
|
||||
assert decoded.Foo.a == 4
|
||||
assert decoded.Foo.b == 2
|
||||
assert decoded.Foo.Baz.c == false
|
||||
}
|
||||
|
||||
fn test_decode_embed_roundtrip_with_flattened_keys() {
|
||||
mut classifier := Classifier{}
|
||||
classifier.history_events = [
|
||||
HistoryEvent{
|
||||
Environment: Environment{
|
||||
vflags: 'one'
|
||||
}
|
||||
},
|
||||
HistoryEvent{
|
||||
Environment: Environment{
|
||||
vflags: 'two'
|
||||
}
|
||||
},
|
||||
]
|
||||
encoded := json.encode(classifier)
|
||||
assert encoded == '{"history_events":[{"vflags":"one"},{"vflags":"two"}]}'
|
||||
decoded := json.decode[Classifier](encoded)!
|
||||
assert decoded.history_events.len == 2
|
||||
assert decoded.history_events[0].Environment.vflags == 'one'
|
||||
assert decoded.history_events[1].Environment.vflags == 'two'
|
||||
}
|
||||
|
||||
fn test_decode_embed_with_nested_objects() {
|
||||
decoded :=
|
||||
json.decode[Classifier]('{"History":{"history_events":[{"Environment":{"vflags":"legacy"}}]}}')!
|
||||
assert decoded.history_events.len == 1
|
||||
assert decoded.history_events[0].Environment.vflags == 'legacy'
|
||||
}
|
||||
|
||||
fn test_embed_roundtrip_preserves_declaration_order() {
|
||||
str := '{"inner":[1,2,3,4,5],"test":1.2}'
|
||||
data := json.decode[RoundtripOuter](str)!
|
||||
assert json.encode(data) == str
|
||||
}
|
||||
|
||||
fn test_encode_array_of_embedded_structs_preserves_declaration_order() {
|
||||
list_of_object := [
|
||||
ArticlePlus{
|
||||
title: 'One good title'
|
||||
description: 'this is the first'
|
||||
},
|
||||
ArticlePlus{
|
||||
title: 'Other good title'
|
||||
description: 'more one'
|
||||
},
|
||||
ArticlePlus{
|
||||
title: 'Other good title'
|
||||
description: 'more one'
|
||||
perex: 'good perex'
|
||||
},
|
||||
]
|
||||
assert json.encode(list_of_object) == '[{"title":"One good title","description":"this is the first","perex":""},{"title":"Other good title","description":"more one","perex":""},{"title":"Other good title","description":"more one","perex":"good perex"}]'
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import x.json2
|
||||
|
||||
union NumberOrBytes {
|
||||
number u64
|
||||
bytes [8]u8 @[skip]
|
||||
}
|
||||
|
||||
fn test_encode_fixed_array() {
|
||||
assert json2.encode([u8(1), 2, 3]!) == '[1,2,3]'
|
||||
}
|
||||
|
||||
fn test_encode_union_with_skipped_fixed_array_field() {
|
||||
decoded := json2.decode[NumberOrBytes]('{"number":256}')!
|
||||
|
||||
unsafe {
|
||||
assert decoded.bytes == [u8(0), 1, 0, 0, 0, 0, 0, 0]!
|
||||
}
|
||||
assert json2.encode(decoded) == '{"number":256}'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import x.json2
|
||||
|
||||
struct JoseHeader {
|
||||
pub mut:
|
||||
cty ?string
|
||||
alg string
|
||||
typ string = 'JWT'
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
res := json2.encode(JoseHeader{ alg: 'HS256' })
|
||||
assert res == '{"alg":"HS256","typ":"JWT"}'
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import time
|
||||
import x.json2 as json
|
||||
|
||||
struct StructTypeSkippedFields[T] {
|
||||
mut:
|
||||
val T @[json: '-']
|
||||
val1 T
|
||||
val2 T @[json: '-']
|
||||
val3 T
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields1[T] {
|
||||
mut:
|
||||
val T
|
||||
val1 T @[json: '-']
|
||||
val2 T
|
||||
val3 T @[json: '-']
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields2[T] {
|
||||
mut:
|
||||
val T @[json: '-']
|
||||
val1 T @[json: '-']
|
||||
val2 T @[json: '-']
|
||||
val3 T @[json: '-']
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields3[T, U, V] {
|
||||
mut:
|
||||
val T @[json: '-']
|
||||
val1 U
|
||||
val2 V
|
||||
val3 T @[json: '-']
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields4 {
|
||||
mut:
|
||||
val string @[json: '-']
|
||||
val1 i64
|
||||
val2 f64
|
||||
val3 time.Time
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields5 {
|
||||
mut:
|
||||
val string @[json: '-']
|
||||
val1 i64 @[json: '-']
|
||||
val2 f64
|
||||
val3 time.Time
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields6 {
|
||||
mut:
|
||||
val string @[json: '-']
|
||||
val1 i64
|
||||
val2 f64 @[json: '-']
|
||||
val3 time.Time
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields7 {
|
||||
mut:
|
||||
val string
|
||||
val1 i64 @[json: '-']
|
||||
val2 f64 @[json: '-']
|
||||
val3 time.Time
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields8 {
|
||||
mut:
|
||||
val string
|
||||
val1 i64 @[json: '-']
|
||||
val2 f64
|
||||
val3 time.Time @[json: '-']
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields9 {
|
||||
mut:
|
||||
val string
|
||||
val1 i64 @[skip]
|
||||
val2 f64
|
||||
val3 time.Time @[skip]
|
||||
}
|
||||
|
||||
fn test_encode_struct_skipped_fields() {
|
||||
assert json.encode(StructTypeSkippedFields[string]{
|
||||
val: 'string_val'
|
||||
val1: 'string_val1'
|
||||
val2: 'string_val2'
|
||||
val3: 'string_val3'
|
||||
}) == '{"val1":"string_val1","val3":"string_val3"}'
|
||||
|
||||
assert json.encode(StructTypeSkippedFields1[string]{
|
||||
val: 'string_val'
|
||||
val1: 'string_val1'
|
||||
val2: 'string_val2'
|
||||
val3: 'string_val3'
|
||||
}) == '{"val":"string_val","val2":"string_val2"}'
|
||||
|
||||
assert json.encode(StructTypeSkippedFields2[string]{
|
||||
val: 'string_val'
|
||||
val1: 'string_val1'
|
||||
val2: 'string_val2'
|
||||
val3: 'string_val3'
|
||||
}) == '{}'
|
||||
|
||||
assert json.encode(StructTypeSkippedFields3[string, i64, f64]{
|
||||
val: 'string_val'
|
||||
val1: 1
|
||||
val2: 1.0
|
||||
val3: 'string_val'
|
||||
}) == '{"val1":1,"val2":1}'
|
||||
|
||||
assert json.encode(StructTypeSkippedFields4{
|
||||
val: 'string_val'
|
||||
val1: 1
|
||||
val2: 1.0
|
||||
}) == '{"val1":1,"val2":1,"val3":"0000-01-01T00:00:00.000Z"}'
|
||||
|
||||
assert json.encode(StructTypeSkippedFields5{
|
||||
val: 'string_val'
|
||||
val1: 1
|
||||
val2: 1.0
|
||||
}) == '{"val2":1,"val3":"0000-01-01T00:00:00.000Z"}'
|
||||
|
||||
assert json.encode(StructTypeSkippedFields6{
|
||||
val: 'string_val'
|
||||
val1: 1
|
||||
val2: 1.0
|
||||
}) == '{"val1":1,"val3":"0000-01-01T00:00:00.000Z"}'
|
||||
|
||||
assert json.encode(StructTypeSkippedFields7{
|
||||
val: 'string_val'
|
||||
val1: 1
|
||||
val2: 1.0
|
||||
}) == '{"val":"string_val","val3":"0000-01-01T00:00:00.000Z"}'
|
||||
|
||||
assert json.encode(StructTypeSkippedFields8{
|
||||
val: 'string_val'
|
||||
val1: 1
|
||||
val2: 1.0
|
||||
}) == '{"val":"string_val","val2":1}'
|
||||
|
||||
assert json.encode(StructTypeSkippedFields9{
|
||||
val: 'string_val'
|
||||
val1: 1
|
||||
val2: 1.0
|
||||
}) == '{"val":"string_val","val2":1}'
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
// vtest build: !macos
|
||||
import x.json2 as json
|
||||
import time
|
||||
|
||||
const fixed_time = time.new(
|
||||
year: 2022
|
||||
month: 3
|
||||
day: 11
|
||||
hour: 13
|
||||
minute: 54
|
||||
second: 25
|
||||
)
|
||||
|
||||
type StringAlias = string
|
||||
type BoolAlias = bool
|
||||
type U8Alias = u8
|
||||
type U16Alias = u16
|
||||
type U32Alias = u32
|
||||
type U64Alias = u64
|
||||
type I8Alias = i8
|
||||
type I16Alias = i16
|
||||
type I32Alias = i32
|
||||
type I64Alias = i64
|
||||
type IntAlias = int
|
||||
type UsizeAlias = usize
|
||||
type IsizeAlias = isize
|
||||
type F32Alias = f32
|
||||
type F64Alias = f64
|
||||
type TimeAlias = time.Time
|
||||
type StructAlias = StructType[int]
|
||||
type EnumAlias = Enumerates
|
||||
|
||||
type SumTypes = StructType[string] | []SumTypes | []string | bool | int | string | time.Time
|
||||
|
||||
enum Enumerates {
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e = 99
|
||||
f
|
||||
}
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
}
|
||||
|
||||
struct StructTypeOption[T] {
|
||||
mut:
|
||||
val ?T
|
||||
}
|
||||
|
||||
struct StructTypePointer[T] {
|
||||
mut:
|
||||
val &T
|
||||
}
|
||||
|
||||
fn test_types() {
|
||||
assert json.encode(StructType[string]{}) == '{"val":""}'
|
||||
assert json.encode(StructType[string]{ val: '' }) == '{"val":""}'
|
||||
assert json.encode(StructType[string]{ val: 'a' }) == '{"val":"a"}'
|
||||
|
||||
assert json.encode(StructType[bool]{}) == '{"val":false}'
|
||||
assert json.encode(StructType[bool]{ val: false }) == '{"val":false}'
|
||||
assert json.encode(StructType[bool]{ val: true }) == '{"val":true}'
|
||||
|
||||
assert json.encode(StructType[int]{}) == '{"val":0}'
|
||||
assert json.encode(StructType[int]{ val: 0 }) == '{"val":0}'
|
||||
assert json.encode(StructType[int]{ val: 1 }) == '{"val":1}'
|
||||
|
||||
assert json.encode(StructType[time.Time]{}) == '{"val":"0000-01-01T00:00:00.000Z"}'
|
||||
assert json.encode(StructType[time.Time]{ val: fixed_time }) == '{"val":"2022-03-11T13:54:25.000Z"}'
|
||||
|
||||
assert json.encode(StructType[StructType[int]]{
|
||||
val: StructType[int]{
|
||||
val: 1
|
||||
}
|
||||
}) == '{"val":{"val":1}}'
|
||||
|
||||
assert json.encode(StructType[Enumerates]{}, enum_as_int: true) == '{"val":0}'
|
||||
assert json.encode(StructType[Enumerates]{ val: Enumerates.a },
|
||||
enum_as_int: true
|
||||
) == '{"val":0}'
|
||||
assert json.encode(StructType[Enumerates]{ val: Enumerates.d },
|
||||
enum_as_int: true
|
||||
) == '{"val":3}'
|
||||
assert json.encode(StructType[Enumerates]{ val: Enumerates.e },
|
||||
enum_as_int: true
|
||||
) == '{"val":99}'
|
||||
assert json.encode(StructType[Enumerates]{ val: Enumerates.f },
|
||||
enum_as_int: true
|
||||
) == '{"val":100}'
|
||||
}
|
||||
|
||||
fn test_option_types() {
|
||||
assert json.encode(StructTypeOption[string]{ val: none }) == '{}'
|
||||
assert json.encode(StructTypeOption[string]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[string]{ val: '' }) == '{"val":""}'
|
||||
assert json.encode(StructTypeOption[string]{ val: 'a' }) == '{"val":"a"}'
|
||||
|
||||
assert json.encode(StructTypeOption[bool]{ val: none }) == '{}'
|
||||
assert json.encode(StructTypeOption[bool]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[bool]{ val: false }) == '{"val":false}'
|
||||
assert json.encode(StructTypeOption[bool]{ val: true }) == '{"val":true}'
|
||||
|
||||
assert json.encode(StructTypeOption[int]{ val: none }) == '{}'
|
||||
assert json.encode(StructTypeOption[int]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[int]{ val: 0 }) == '{"val":0}'
|
||||
assert json.encode(StructTypeOption[int]{ val: 1 }) == '{"val":1}'
|
||||
|
||||
assert json.encode(StructTypeOption[time.Time]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[time.Time]{ val: time.Time{} }) == '{"val":"0000-01-01T00:00:00.000Z"}'
|
||||
assert json.encode(StructTypeOption[time.Time]{ val: fixed_time }) == '{"val":"2022-03-11T13:54:25.000Z"}'
|
||||
|
||||
assert json.encode(StructTypeOption[StructType[int]]{
|
||||
val: StructType[int]{
|
||||
val: 1
|
||||
}
|
||||
}) == '{"val":{"val":1}}'
|
||||
|
||||
assert json.encode(StructTypeOption[Enumerates]{}) == '{}'
|
||||
}
|
||||
|
||||
fn test_array() {
|
||||
assert json.encode(StructType[[]string]{}) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]string]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]string]{ val: ['0'] }) == '{"val":["0"]}'
|
||||
assert json.encode(StructType[[]string]{ val: ['1'] }) == '{"val":["1"]}'
|
||||
|
||||
assert json.encode(StructType[[]int]{}) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]int]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]int]{ val: [0] }) == '{"val":[0]}'
|
||||
assert json.encode(StructType[[]int]{ val: [1] }) == '{"val":[1]}'
|
||||
assert json.encode(StructType[[]int]{ val: [0, 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}'
|
||||
|
||||
assert json.encode(StructType[[]u8]{}) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]u8]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]u8]{ val: [u8(0)] }) == '{"val":[0]}'
|
||||
assert json.encode(StructType[[]u8]{ val: [u8(1)] }) == '{"val":[1]}'
|
||||
assert json.encode(StructType[[]u8]{ val: [u8(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}'
|
||||
|
||||
assert json.encode(StructType[[]i64]{}) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]i64]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]i64]{ val: [i64(0)] }) == '{"val":[0]}'
|
||||
assert json.encode(StructType[[]i64]{ val: [i64(1)] }) == '{"val":[1]}'
|
||||
assert json.encode(StructType[[]i64]{ val: [i64(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}'
|
||||
|
||||
assert json.encode(StructType[[]u64]{}) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]u64]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]u64]{ val: [u64(0)] }) == '{"val":[0]}'
|
||||
assert json.encode(StructType[[]u64]{ val: [u64(1)] }) == '{"val":[1]}'
|
||||
assert json.encode(StructType[[]u64]{ val: [u64(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}'
|
||||
|
||||
assert json.encode(StructType[[]f64]{}) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]f64]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]f64]{ val: [f64(0)] }) == '{"val":[0]}'
|
||||
assert json.encode(StructType[[]f64]{ val: [f64(1)] }) == '{"val":[1]}'
|
||||
assert json.encode(StructType[[]f64]{ val: [f64(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}'
|
||||
|
||||
assert json.encode(StructType[[]bool]{}) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]bool]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructType[[]bool]{ val: [true] }) == '{"val":[true]}'
|
||||
assert json.encode(StructType[[]bool]{ val: [false] }) == '{"val":[false]}'
|
||||
assert json.encode(StructType[[]bool]{ val: [false, true, false] }) == '{"val":[false,true,false]}'
|
||||
|
||||
array_of_struct := [StructType[bool]{
|
||||
val: true
|
||||
}, StructType[bool]{
|
||||
val: false
|
||||
}]
|
||||
assert json.encode(StructType[[]StructType[bool]]{ val: array_of_struct }) == '{"val":[{"val":true},{"val":false}]}'
|
||||
|
||||
assert json.encode(StructType[[][]string]{ val: [['1'], ['2']] }) == '{"val":[["1"],["2"]]}'
|
||||
|
||||
// error: cannot use `[][]string` as `[]string` in argument 1 to `x.json2.Encoder.encode_array`
|
||||
// assert json.encode(StructType[[][][]string]{ val: [[['1']]] }) == '{"val":[[["1"]]]}'
|
||||
}
|
||||
|
||||
fn test_option_array() {
|
||||
assert json.encode(StructTypeOption[[]string]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[[]string]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructTypeOption[[]string]{ val: ['0'] }) == '{"val":["0"]}'
|
||||
assert json.encode(StructTypeOption[[]string]{ val: ['1'] }) == '{"val":["1"]}'
|
||||
|
||||
assert json.encode(StructTypeOption[[]int]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[[]int]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructTypeOption[[]int]{ val: [0] }) == '{"val":[0]}'
|
||||
assert json.encode(StructTypeOption[[]int]{ val: [1] }) == '{"val":[1]}'
|
||||
assert json.encode(StructTypeOption[[]int]{ val: [0, 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}'
|
||||
|
||||
assert json.encode(StructTypeOption[[]u8]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[[]u8]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructTypeOption[[]u8]{ val: [u8(0)] }) == '{"val":[0]}'
|
||||
assert json.encode(StructTypeOption[[]u8]{ val: [u8(1)] }) == '{"val":[1]}'
|
||||
assert json.encode(StructTypeOption[[]u8]{ val: [u8(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}'
|
||||
|
||||
assert json.encode(StructTypeOption[[]i64]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[[]i64]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructTypeOption[[]i64]{ val: [i64(0)] }) == '{"val":[0]}'
|
||||
assert json.encode(StructTypeOption[[]i64]{ val: [i64(1)] }) == '{"val":[1]}'
|
||||
assert json.encode(StructTypeOption[[]i64]{ val: [i64(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}'
|
||||
|
||||
assert json.encode(StructTypeOption[[]u64]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[[]u64]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructTypeOption[[]u64]{ val: [u64(0)] }) == '{"val":[0]}'
|
||||
assert json.encode(StructTypeOption[[]u64]{ val: [u64(1)] }) == '{"val":[1]}'
|
||||
assert json.encode(StructTypeOption[[]u64]{ val: [u64(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}'
|
||||
|
||||
assert json.encode(StructTypeOption[[]f64]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[[]f64]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructTypeOption[[]f64]{ val: [f64(0)] }) == '{"val":[0]}'
|
||||
assert json.encode(StructTypeOption[[]f64]{ val: [f64(1)] }) == '{"val":[1]}'
|
||||
assert json.encode(StructTypeOption[[]f64]{ val: [f64(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}'
|
||||
|
||||
assert json.encode(StructTypeOption[[]bool]{}) == '{}'
|
||||
assert json.encode(StructTypeOption[[]bool]{ val: [] }) == '{"val":[]}'
|
||||
assert json.encode(StructTypeOption[[]bool]{ val: [true] }) == '{"val":[true]}'
|
||||
assert json.encode(StructTypeOption[[]bool]{ val: [false] }) == '{"val":[false]}'
|
||||
assert json.encode(StructTypeOption[[]bool]{ val: [false, true, false] }) == '{"val":[false,true,false]}'
|
||||
|
||||
array_of_struct := [StructType[bool]{
|
||||
val: true
|
||||
}, StructType[bool]{
|
||||
val: false
|
||||
}]
|
||||
assert json.encode(StructTypeOption[[]StructType[bool]]{ val: array_of_struct }) == '{"val":[{"val":true},{"val":false}]}'
|
||||
|
||||
// assert json.encode(StructTypeOption[[][]int]{
|
||||
// val: [[0, 1], [0, 2, 3], [2], [5, 1]]
|
||||
// }) == '{"val":[[0,1],[0,2,3],[2],[5,1]]}'
|
||||
}
|
||||
|
||||
fn test_alias() {
|
||||
assert json.encode(StructType[StringAlias]{}) == '{"val":""}'
|
||||
assert json.encode(StructType[StringAlias]{ val: '' }) == '{"val":""}'
|
||||
assert json.encode(StructType[StringAlias]{ val: 'a' }) == '{"val":"a"}'
|
||||
|
||||
assert json.encode(StructType[BoolAlias]{}) == '{"val":false}'
|
||||
assert json.encode(StructType[BoolAlias]{ val: false }) == '{"val":false}'
|
||||
assert json.encode(StructType[BoolAlias]{ val: true }) == '{"val":true}'
|
||||
|
||||
assert json.encode(StructType[IntAlias]{}) == '{"val":0}'
|
||||
assert json.encode(StructType[IntAlias]{ val: 0 }) == '{"val":0}'
|
||||
assert json.encode(StructType[IntAlias]{ val: 1 }) == '{"val":1}'
|
||||
|
||||
assert json.encode(StructType[TimeAlias]{}) == '{"val":"0000-01-01T00:00:00.000Z"}'
|
||||
assert json.encode(StructType[TimeAlias]{ val: fixed_time }) == '{"val":"2022-03-11T13:54:25.000Z"}'
|
||||
|
||||
assert json.encode(StructType[StructAlias]{}) == '{"val":{"val":0}}'
|
||||
assert json.encode(StructType[StructAlias]{ val: StructType[int]{0} }) == '{"val":{"val":0}}'
|
||||
assert json.encode(StructType[StructAlias]{ val: StructType[int]{1} }) == '{"val":{"val":1}}'
|
||||
}
|
||||
|
||||
fn test_pointer() {
|
||||
mut string_initialized_with_reference := ''
|
||||
assert json.encode(StructTypePointer[string]{ val: unsafe { nil } }) == '{}'
|
||||
assert json.encode(StructTypePointer[string]{ val: &string_initialized_with_reference }) == '{"val":""}'
|
||||
string_initialized_with_reference = 'a'
|
||||
assert json.encode(StructTypePointer[string]{ val: &string_initialized_with_reference }) == '{"val":"a"}'
|
||||
|
||||
mut bool_initialized_with_reference := false
|
||||
assert json.encode(StructTypePointer[bool]{ val: unsafe { nil } }) == '{}'
|
||||
assert json.encode(StructTypePointer[bool]{ val: &bool_initialized_with_reference }) == '{"val":false}'
|
||||
bool_initialized_with_reference = true
|
||||
assert json.encode(StructTypePointer[bool]{ val: &bool_initialized_with_reference }) == '{"val":true}'
|
||||
|
||||
mut int_initialized_with_reference := int(0)
|
||||
assert json.encode(StructTypePointer[int]{ val: unsafe { nil } }) == '{}'
|
||||
assert json.encode(StructTypePointer[int]{ val: &int_initialized_with_reference }) == '{"val":0}'
|
||||
int_initialized_with_reference = 1
|
||||
assert json.encode(StructTypePointer[int]{ val: &int_initialized_with_reference }) == '{"val":1}'
|
||||
}
|
||||
|
||||
fn test_sumtypes() {
|
||||
assert json.encode(StructType[SumTypes]{}) == '{"val":{"val":"","_type":"${typeof(StructType[string]{}).name}"}}' // is_none := val.$(field.name).str() == 'unknown sum type value'
|
||||
assert json.encode(StructType[SumTypes]{ val: '' }) == '{"val":""}'
|
||||
assert json.encode(StructType[SumTypes]{ val: 'a' }) == '{"val":"a"}'
|
||||
|
||||
assert json.encode(StructType[SumTypes]{ val: false }) == '{"val":false}'
|
||||
assert json.encode(StructType[SumTypes]{ val: true }) == '{"val":true}'
|
||||
|
||||
assert json.encode(StructType[SumTypes]{ val: 0 }) == '{"val":0}'
|
||||
assert json.encode(StructType[SumTypes]{ val: 1 }) == '{"val":1}'
|
||||
|
||||
assert json.encode(StructType[SumTypes]{ val: fixed_time }) == '{"val":{"_type":"${typeof(time.Time{}).name.all_after_last('.')}","value":${fixed_time.unix()}}}'
|
||||
|
||||
assert json.encode(StructType[StructType[SumTypes]]{
|
||||
val: StructType[SumTypes]{
|
||||
val: 1
|
||||
}
|
||||
}) == '{"val":{"val":1}}'
|
||||
|
||||
assert json.encode(StructType[SumTypes]{
|
||||
val: StructType[string]{
|
||||
val: '111111'
|
||||
}
|
||||
}) == '{"val":{"val":"111111","_type":"${typeof(StructType[string]{}).name}"}}'
|
||||
|
||||
assert json.encode(StructType[StructType[SumTypes]]{
|
||||
val: StructType[SumTypes]{
|
||||
val: 1
|
||||
}
|
||||
}) == '{"val":{"val":1}}'
|
||||
|
||||
// assert json.encode(StructType{ val: [SumTypes('a')] }) == '{"val":["a"]}'
|
||||
// assert json.encode(StructType[SumTypes]{ val: ['a'] }) == '{"val":["a"]}'
|
||||
// assert json.encode(StructType[SumTypes]{ val: [SumTypes('a')] }) == '{"val":["a"]}'
|
||||
// assert json.encode(StructType[SumTypes]{ val: '' }) == '{"val":""}'
|
||||
}
|
||||
|
||||
fn test_maps() {
|
||||
assert json.encode(StructType[map[string]map[string]int]{}) == '{"val":{}}'
|
||||
assert json.encode(StructType[map[string]string]{
|
||||
val: {
|
||||
'1': '1'
|
||||
}
|
||||
}) == '{"val":{"1":"1"}}'
|
||||
assert json.encode(StructType[map[string]int]{
|
||||
val: {
|
||||
'1': 1
|
||||
}
|
||||
}) == '{"val":{"1":1}}'
|
||||
assert json.encode(StructType[map[string]map[string]int]{
|
||||
val: {
|
||||
'a': {
|
||||
'1': 1
|
||||
}
|
||||
}
|
||||
}) == '{"val":{"a":{"1":1}}}'
|
||||
}
|
||||
|
||||
struct AliasStruct {
|
||||
f_string StringAlias = 'hello'
|
||||
f_bool BoolAlias = true
|
||||
f_u8 U8Alias = 123
|
||||
f_u16 U16Alias = 456
|
||||
f_u32 U32Alias = 789
|
||||
f_u64 U64Alias = 112233
|
||||
f_i8 I8Alias = -123
|
||||
f_i16 I16Alias = -456
|
||||
f_i32 I32Alias = -789
|
||||
f_i64 I64Alias = -112233
|
||||
f_int IntAlias = 7788
|
||||
f_usize UsizeAlias = 9900
|
||||
f_isize IsizeAlias = -9900
|
||||
f_f32 F32Alias = -1.5
|
||||
f_f64 F64Alias = 7.25
|
||||
}
|
||||
|
||||
fn test_struct_alias() {
|
||||
s := AliasStruct{}
|
||||
str := json.encode(s)
|
||||
assert str == '{"f_string":"hello","f_bool":true,"f_u8":123,"f_u16":456,"f_u32":789,"f_u64":112233,"f_i8":-123,"f_i16":-456,"f_i32":-789,"f_i64":-112233,"f_int":7788,"f_usize":9900,"f_isize":-9900,"f_f32":-1.5,"f_f64":7.25}'
|
||||
assert json.decode[AliasStruct](str)! == s
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
import x.json2 as json
|
||||
import time
|
||||
|
||||
const fixed_time = time.new(
|
||||
year: 2022
|
||||
month: 3
|
||||
day: 11
|
||||
hour: 13
|
||||
minute: 54
|
||||
second: 25
|
||||
)
|
||||
|
||||
type StringAlias = string
|
||||
type BoolAlias = bool
|
||||
type IntAlias = int
|
||||
type TimeAlias = time.Time
|
||||
type StructAlias = StructType[int]
|
||||
type EnumAlias = Enumerates
|
||||
|
||||
type SumTypes = bool | int | string
|
||||
|
||||
enum Enumerates {
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e = 99
|
||||
f
|
||||
}
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
}
|
||||
|
||||
struct StructTypeOption[T] {
|
||||
mut:
|
||||
val ?T
|
||||
}
|
||||
|
||||
struct StructTypePointer[T] {
|
||||
mut:
|
||||
val &T
|
||||
}
|
||||
|
||||
fn test_option_types() {
|
||||
assert json.encode(StructTypeOption[Enumerates]{ val: Enumerates.a }) == '{"val":0}'
|
||||
assert json.encode(StructTypeOption[Enumerates]{ val: Enumerates.d }) == '{"val":3}'
|
||||
assert json.encode(StructTypeOption[Enumerates]{ val: Enumerates.e }) == '{"val":99}'
|
||||
assert json.encode(StructTypeOption[Enumerates]{ val: Enumerates.f }) == '{"val":100}'
|
||||
}
|
||||
|
||||
fn test_option_alias() {
|
||||
assert json.encode(StructTypeOption[BoolAlias]{ val: none }) == '{}'
|
||||
assert json.encode(StructTypeOption[BoolAlias]{}) == '{"val":false}'
|
||||
assert json.encode(StructTypeOption[BoolAlias]{ val: false }) == '{"val":false}'
|
||||
assert json.encode(StructTypeOption[BoolAlias]{ val: true }) == '{"val":true}'
|
||||
|
||||
assert json.encode(StructTypeOption[IntAlias]{ val: none }) == '{}'
|
||||
assert json.encode(StructTypeOption[IntAlias]{}) == '{"val":0}'
|
||||
assert json.encode(StructTypeOption[IntAlias]{ val: 0 }) == '{"val":0}'
|
||||
assert json.encode(StructTypeOption[IntAlias]{ val: 1 }) == '{"val":1}'
|
||||
|
||||
assert json.encode(StructTypeOption[EnumAlias]{}) == '{"val":0}'
|
||||
assert json.encode(StructTypeOption[EnumAlias]{ val: Enumerates.a }) == '{"val":0}'
|
||||
assert json.encode(StructTypeOption[EnumAlias]{ val: Enumerates.d }) == '{"val":3}'
|
||||
assert json.encode(StructTypeOption[EnumAlias]{ val: Enumerates.e }) == '{"val":99}'
|
||||
assert json.encode(StructTypeOption[EnumAlias]{ val: Enumerates.f }) == '{"val":100}'
|
||||
}
|
||||
|
||||
fn test_alias() {
|
||||
assert json.encode(StructType[EnumAlias]{}) == '{"val":0}'
|
||||
assert json.encode(StructType[EnumAlias]{ val: Enumerates.a }) == '{"val":0}'
|
||||
assert json.encode(StructType[EnumAlias]{ val: Enumerates.d }) == '{"val":3}'
|
||||
assert json.encode(StructType[EnumAlias]{ val: Enumerates.e }) == '{"val":99}'
|
||||
assert json.encode(StructType[EnumAlias]{ val: Enumerates.f }) == '{"val":100}'
|
||||
}
|
||||
|
||||
fn test_sumtypes() {
|
||||
assert json.encode(StructType[SumTypes]{}) == '{}'
|
||||
assert json.encode(StructType[SumTypes]{ val: '' }) == '{"val":""}'
|
||||
assert json.encode(StructType[SumTypes]{ val: 'a' }) == '{"val":"a"}'
|
||||
|
||||
assert json.encode(StructType[SumTypes]{ val: false }) == '{"val":false}'
|
||||
assert json.encode(StructType[SumTypes]{ val: true }) == '{"val":true}'
|
||||
|
||||
assert json.encode(StructType[SumTypes]{ val: 0 }) == '{"val":0}'
|
||||
assert json.encode(StructType[SumTypes]{ val: 1 }) == '{"val":1}'
|
||||
|
||||
assert json.encode(StructType[StructType[SumTypes]]{
|
||||
val: StructType[SumTypes]{
|
||||
val: 1
|
||||
}
|
||||
}) == '{"val":{"val":1}}'
|
||||
}
|
||||
|
||||
fn test_option_sumtypes() {
|
||||
assert json.encode(StructTypeOption[SumTypes]{}) == '{}' // REVIEW
|
||||
|
||||
assert json.encode(StructTypeOption[SumTypes]{ val: '' }) == '{"val":""}'
|
||||
assert json.encode(StructTypeOption[SumTypes]{ val: 'a' }) == '{"val":"a"}'
|
||||
|
||||
assert json.encode(StructTypeOption[SumTypes]{ val: false }) == '{"val":false}'
|
||||
assert json.encode(StructTypeOption[SumTypes]{ val: true }) == '{"val":true}'
|
||||
|
||||
assert json.encode(StructTypeOption[SumTypes]{ val: 0 }) == '{"val":0}'
|
||||
assert json.encode(StructTypeOption[SumTypes]{ val: 1 }) == '{"val":1}'
|
||||
}
|
||||
|
||||
fn test_caos() {
|
||||
typed_string_struct := StructType[string]{
|
||||
val: 'a'
|
||||
}
|
||||
|
||||
// StructType
|
||||
assert json.encode(StructType[StructType]{}) == 'TODO'
|
||||
assert json.encode(StructType[StructType[string]]{ val: typed_string_struct }) == 'TODO'
|
||||
assert json.encode(StructType[StructType[StringAlias]]{ val: typed_string_struct }) == 'TODO'
|
||||
assert json.encode(StructType[StructType[SumTypes]]{ val: typed_string_struct }) == 'TODO'
|
||||
|
||||
assert json.encode(StructType[StructTypeOption]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructType[StructTypeOption[string]]{ val: StructType{'a'} }) == 'TODO'
|
||||
assert json.encode(StructType[StructTypeOption[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructType[StructTypeOption[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
|
||||
assert json.encode(StructType[StructTypePointer]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructType[StructTypePointer[string]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructType[StructTypePointer[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructType[StructTypePointer[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
|
||||
// StructTypeOptional
|
||||
assert json.encode(StructTypeOption[StructType]{}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[StructType[string]]{ val: '' }) == 'TODO'
|
||||
assert json.encode(StructTypeOption[StructType[StringAlias]]{ val: '' }) == 'TODO'
|
||||
assert json.encode(StructTypeOption[StructType[SumTypes]]{ val: '' }) == 'TODO'
|
||||
|
||||
assert json.encode(StructTypeOption[StructTypeOption]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[StructTypeOption[string]]{ val: StructType{'a'} }) == 'TODO'
|
||||
assert json.encode(StructTypeOption[StructTypeOption[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[StructTypeOption[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
|
||||
assert json.encode(StructTypeOption[StructTypePointer]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[StructTypePointer[string]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[StructTypePointer[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[StructTypePointer[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
|
||||
// StructTypePointer
|
||||
assert json.encode(StructTypePointer[StructType]{}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[StructType[string]]{ val: '' }) == 'TODO'
|
||||
assert json.encode(StructTypePointer[StructType[StringAlias]]{ val: '' }) == 'TODO'
|
||||
assert json.encode(StructTypePointer[StructType[SumTypes]]{ val: '' }) == 'TODO'
|
||||
|
||||
assert json.encode(StructTypePointer[StructTypeOption]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[StructTypeOption[string]]{ val: StructType{'a'} }) == 'TODO'
|
||||
assert json.encode(StructTypePointer[StructTypeOption[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[StructTypeOption[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
|
||||
assert json.encode(StructTypePointer[StructTypePointer]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[StructTypePointer[string]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[StructTypePointer[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[StructTypePointer[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
}
|
||||
|
||||
fn test_caos_array() {
|
||||
typed_string_struct := [StructType[string]{
|
||||
val: 'a'
|
||||
}]
|
||||
|
||||
// StructType
|
||||
assert json.encode(StructType[[]StructType]{}) == 'TODO'
|
||||
assert json.encode(StructType[[]StructType[string]]{ val: typed_string_struct }) == 'TODO'
|
||||
assert json.encode(StructType[[]StructType[StringAlias]]{ val: typed_string_struct }) == 'TODO'
|
||||
assert json.encode(StructType[[]StructType[SumTypes]]{ val: typed_string_struct }) == 'TODO'
|
||||
|
||||
assert json.encode(StructType[[]StructTypeOption]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructType[[]StructTypeOption[string]]{ val: StructType{'a'} }) == 'TODO'
|
||||
assert json.encode(StructType[[]StructTypeOption[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructType[[]StructTypeOption[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
|
||||
assert json.encode(StructType[[]StructTypePointer]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructType[[]StructTypePointer[string]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructType[[]StructTypePointer[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructType[[]StructTypePointer[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
|
||||
// StructTypeOptional
|
||||
assert json.encode(StructTypeOption[[]StructType]{}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[[]StructType[string]]{ val: '' }) == 'TODO'
|
||||
assert json.encode(StructTypeOption[[]StructType[StringAlias]]{ val: '' }) == 'TODO'
|
||||
assert json.encode(StructTypeOption[[]StructType[SumTypes]]{ val: '' }) == 'TODO'
|
||||
|
||||
assert json.encode(StructTypeOption[[]StructTypeOption]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[[]StructTypeOption[string]]{ val: StructType{'a'} }) == 'TODO'
|
||||
assert json.encode(StructTypeOption[[]StructTypeOption[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[[]StructTypeOption[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
|
||||
assert json.encode(StructTypeOption[[]StructTypePointer]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[[]StructTypePointer[string]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[[]StructTypePointer[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypeOption[[]StructTypePointer[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
|
||||
// StructTypePointer
|
||||
assert json.encode(StructTypePointer[[]StructType]{}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[[]StructType[string]]{ val: '' }) == 'TODO'
|
||||
assert json.encode(StructTypePointer[[]StructType[StringAlias]]{ val: '' }) == 'TODO'
|
||||
assert json.encode(StructTypePointer[[]StructType[SumTypes]]{ val: '' }) == 'TODO'
|
||||
|
||||
assert json.encode(StructTypePointer[[]StructTypeOption]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[[]StructTypeOption[string]]{ val: StructType{'a'} }) == 'TODO'
|
||||
assert json.encode(StructTypePointer[[]StructTypeOption[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[[]StructTypeOption[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
|
||||
assert json.encode(StructTypePointer[[]StructTypePointer]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[[]StructTypePointer[string]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[[]StructTypePointer[StringAlias]]{typed_string_struct}) == 'TODO'
|
||||
assert json.encode(StructTypePointer[[]StructTypePointer[SumTypes]]{typed_string_struct}) == 'TODO'
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
import json2 as json
|
||||
import time
|
||||
import math.big
|
||||
|
||||
type StrAlias = string
|
||||
type BoolAlias = bool
|
||||
type IntAlias = int
|
||||
type FloatAlias = f64
|
||||
|
||||
enum TestEnum {
|
||||
a
|
||||
b @[json: 'BBB']
|
||||
c = 10
|
||||
d @[badattr: 'bad'; foobar]
|
||||
}
|
||||
|
||||
type EnumAlias = TestEnum
|
||||
|
||||
@[json_as_number]
|
||||
enum NumberedTestEnum {
|
||||
a
|
||||
b = 5
|
||||
}
|
||||
|
||||
type Sum = int | string
|
||||
type SumAlias = Sum
|
||||
|
||||
struct Basic {
|
||||
a int
|
||||
b string
|
||||
c bool
|
||||
}
|
||||
|
||||
struct NumberedEnumWrap {
|
||||
a NumberedTestEnum
|
||||
}
|
||||
|
||||
type BasicAlias = Basic
|
||||
|
||||
struct Opt {
|
||||
a ?int
|
||||
}
|
||||
|
||||
type OptAlias = Opt
|
||||
|
||||
struct OptRequiered {
|
||||
a ?int @[required]
|
||||
}
|
||||
|
||||
type OptRequieredAlias = OptRequiered
|
||||
|
||||
struct CustomString {
|
||||
data string
|
||||
}
|
||||
|
||||
pub fn (cs CustomString) to_json() string {
|
||||
return '"<<<' + cs.data + '>>>"'
|
||||
}
|
||||
|
||||
type CustomStringAlias = CustomString
|
||||
|
||||
type NullAlias = json.Null
|
||||
|
||||
type TimeAlias = time.Time
|
||||
|
||||
type BigAlias = big.Integer
|
||||
|
||||
struct NamedFields {
|
||||
a int @[json: 'id']
|
||||
name string @[json: 'Name']
|
||||
}
|
||||
|
||||
type NamedFieldsAlias = NamedFields
|
||||
|
||||
struct SkipFields {
|
||||
a int @[json: '-']
|
||||
name string @[skip]
|
||||
}
|
||||
|
||||
type SkipFieldsAlias = SkipFields
|
||||
|
||||
struct SkipSomeFields {
|
||||
a int @[json: '-']
|
||||
name string @[skip]
|
||||
hi bool = true
|
||||
}
|
||||
|
||||
type SkipSomeFieldsAlias = SkipSomeFields
|
||||
|
||||
struct PointerFields {
|
||||
next &PointerFields = unsafe { nil }
|
||||
data int
|
||||
}
|
||||
|
||||
type PointerFieldsAlias = PointerFields
|
||||
|
||||
struct F64ArrayRoundtripPayload {
|
||||
arr []f64
|
||||
}
|
||||
|
||||
struct OmitFields {
|
||||
a ?bool @[omitempty]
|
||||
b string @[omitempty]
|
||||
c int @[omitempty]
|
||||
d f64 @[omitempty]
|
||||
e ?string = '' @[omitempty]
|
||||
f ?int = 0 @[omitempty]
|
||||
g ?f64 = 0.0 @[omitempty]
|
||||
}
|
||||
|
||||
type OmitFieldsAlias = OmitFields
|
||||
|
||||
struct OmitemptyRegressionNumber {
|
||||
min int
|
||||
max int
|
||||
}
|
||||
|
||||
struct OmitemptyRegressionResp {
|
||||
options []string @[omitempty]
|
||||
metadata map[string]string @[omitempty]
|
||||
number &OmitemptyRegressionNumber = unsafe { nil } @[omitempty]
|
||||
config ?OmitemptyRegressionNumber @[omitempty]
|
||||
}
|
||||
|
||||
fn test_primitives() {
|
||||
assert json.encode('hello') == '"hello"'
|
||||
assert json.encode(StrAlias('hello')) == '"hello"'
|
||||
assert json.encode(true) == 'true'
|
||||
assert json.encode(BoolAlias(false)) == 'false'
|
||||
assert json.encode(-12345) == '-12345'
|
||||
assert json.encode(IntAlias(-12345)) == '-12345'
|
||||
assert json.encode(123.323) == '123.323'
|
||||
assert json.encode(FloatAlias(123.323)) == '123.323'
|
||||
}
|
||||
|
||||
fn test_encode_decode_struct_with_f64_array_roundtrips() ! {
|
||||
original := F64ArrayRoundtripPayload{
|
||||
arr: [0.9716157205240175, 0.9336099585062241]
|
||||
}
|
||||
encoded := json.encode(original)
|
||||
assert encoded == '{"arr":[0.9716157205240175,0.9336099585062241]}'
|
||||
assert json.decode[F64ArrayRoundtripPayload](encoded)! == original
|
||||
}
|
||||
|
||||
fn test_arrays() {
|
||||
assert json.encode([1, 2, 3, 4]) == '[1,2,3,4]'
|
||||
}
|
||||
|
||||
fn test_maps() {
|
||||
assert json.encode({
|
||||
'hi': 0
|
||||
'bye': 1
|
||||
}) == '{"hi":0,"bye":1}'
|
||||
}
|
||||
|
||||
fn test_enums() {
|
||||
assert json.encode(TestEnum.c) == '"c"'
|
||||
assert json.encode(EnumAlias(TestEnum.c)) == '"c"'
|
||||
assert json.encode(TestEnum.c, enum_as_int: true) == '10'
|
||||
assert json.encode(EnumAlias(TestEnum.c), enum_as_int: true) == '10'
|
||||
assert json.encode(TestEnum.b) == '"BBB"'
|
||||
assert json.encode(EnumAlias(TestEnum.b)) == '"BBB"'
|
||||
// Ensure that only `json: ` attrs is applied
|
||||
assert json.encode(TestEnum.d) == '"d"'
|
||||
assert json.encode(EnumAlias(TestEnum.d)) == '"d"'
|
||||
}
|
||||
|
||||
fn test_enum_json_as_number_attribute() {
|
||||
assert json.encode(NumberedTestEnum.a) == '0'
|
||||
assert json.encode(NumberedTestEnum.b) == '5'
|
||||
assert json.encode(NumberedEnumWrap{
|
||||
a: .a
|
||||
}) == '{"a":0}'
|
||||
}
|
||||
|
||||
fn test_sumtypes() {
|
||||
assert json.encode(Sum(10)) == '10'
|
||||
assert json.encode(Sum('hi')) == '"hi"'
|
||||
assert json.encode(SumAlias(10)) == '10'
|
||||
assert json.encode(SumAlias('hi')) == '"hi"'
|
||||
}
|
||||
|
||||
fn test_basic_structs() {
|
||||
assert json.encode(Basic{
|
||||
a: 10
|
||||
b: 'hi'
|
||||
c: true
|
||||
}) == '{"a":10,"b":"hi","c":true}'
|
||||
|
||||
assert json.encode(BasicAlias{
|
||||
a: 10
|
||||
b: 'hi'
|
||||
c: true
|
||||
}) == '{"a":10,"b":"hi","c":true}'
|
||||
}
|
||||
|
||||
fn test_nested() {
|
||||
assert json.encode([
|
||||
{
|
||||
'hi': Basic{ a: 1, b: 'a', c: false }
|
||||
'bye': Basic{
|
||||
a: 2
|
||||
b: 'b'
|
||||
c: true
|
||||
}
|
||||
},
|
||||
{
|
||||
'hi2': Basic{
|
||||
a: 3
|
||||
b: 'c'
|
||||
c: false
|
||||
}
|
||||
'bye2': Basic{
|
||||
a: 4
|
||||
b: 'd'
|
||||
c: true
|
||||
}
|
||||
},
|
||||
]) == '[{"hi":{"a":1,"b":"a","c":false},"bye":{"a":2,"b":"b","c":true}},{"hi2":{"a":3,"b":"c","c":false},"bye2":{"a":4,"b":"d","c":true}}]'
|
||||
assert json.encode([
|
||||
{
|
||||
'hi': Basic{ a: 1, b: 'a', c: false }
|
||||
'bye': Basic{
|
||||
a: 2
|
||||
b: 'b'
|
||||
c: true
|
||||
}
|
||||
},
|
||||
{
|
||||
'hi2': Basic{
|
||||
a: 3
|
||||
b: 'c'
|
||||
c: false
|
||||
}
|
||||
'bye2': Basic{
|
||||
a: 4
|
||||
b: 'd'
|
||||
c: true
|
||||
}
|
||||
},
|
||||
],
|
||||
prettify: true
|
||||
) == '[
|
||||
{
|
||||
"hi": {
|
||||
"a": 1,
|
||||
"b": "a",
|
||||
"c": false
|
||||
},
|
||||
"bye": {
|
||||
"a": 2,
|
||||
"b": "b",
|
||||
"c": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"hi2": {
|
||||
"a": 3,
|
||||
"b": "c",
|
||||
"c": false
|
||||
},
|
||||
"bye2": {
|
||||
"a": 4,
|
||||
"b": "d",
|
||||
"c": true
|
||||
}
|
||||
}
|
||||
]'
|
||||
}
|
||||
|
||||
fn test_string_escapes() {
|
||||
assert json.encode('normal escapes: ", \\ special control escapes: \b, \n, \f, \t, \r, other control escapes: \0, \u001b') == r'"normal escapes: \", \\ special control escapes: \b, \n, \f, \t, \r, other control escapes: \u0000, \u001b"'
|
||||
assert json.encode('ascii, é, 한, 😀, ascii') == r'"ascii, é, 한, 😀, ascii"'
|
||||
assert json.encode('ascii, é, 한, 😀, ascii', escape_unicode: true) == r'"ascii, \u00e9, \ud55c, \uD83D\ude00, ascii"'
|
||||
}
|
||||
|
||||
fn test_options() {
|
||||
assert json.encode(Opt{none}) == '{}'
|
||||
assert json.encode(Opt{99}) == '{"a":99}'
|
||||
assert json.encode(OptAlias{none}) == '{}'
|
||||
assert json.encode(OptAlias{99}) == '{"a":99}'
|
||||
|
||||
assert json.encode(OptRequiered{none}) == '{"a":null}'
|
||||
assert json.encode(OptRequiered{99}) == '{"a":99}'
|
||||
assert json.encode(OptRequieredAlias{none}) == '{"a":null}'
|
||||
assert json.encode(OptRequieredAlias{99}) == '{"a":99}'
|
||||
}
|
||||
|
||||
fn test_custom_encoders() {
|
||||
assert json.encode(CustomString{'hi'}) == '"<<<hi>>>"'
|
||||
assert json.encode(CustomStringAlias{'hi'}) == '"<<<hi>>>"'
|
||||
|
||||
assert json.encode(json.Null{}) == 'null'
|
||||
assert json.encode(NullAlias{}) == 'null'
|
||||
|
||||
assert json.encode(time.Time{}) == '"0000-01-01T00:00:00.000Z"'
|
||||
assert json.encode(TimeAlias{}) == '"0000-01-01T00:00:00.000Z"'
|
||||
|
||||
assert json.encode(big.integer_from_i64(1234567890)) == '1234567890'
|
||||
assert json.encode(BigAlias(big.integer_from_i64(1234567890))) == '1234567890'
|
||||
}
|
||||
|
||||
fn test_named_fields() {
|
||||
assert json.encode(NamedFields{ a: 1, name: 'john' }) == '{"id":1,"Name":"john"}'
|
||||
assert json.encode(NamedFieldsAlias{ a: 1, name: 'john' }) == '{"id":1,"Name":"john"}'
|
||||
}
|
||||
|
||||
fn test_skip_fields() {
|
||||
assert json.encode(SkipFields{ a: 1, name: 'john' }) == '{}'
|
||||
assert json.encode(SkipFieldsAlias{ a: 1, name: 'john' }) == '{}'
|
||||
assert json.encode(SkipFields{ a: 1, name: 'john' },
|
||||
prettify: true
|
||||
) == '{}'
|
||||
|
||||
assert json.encode(SkipSomeFields{ a: 1, name: 'john' }) == '{"hi":true}'
|
||||
assert json.encode(SkipSomeFieldsAlias{ a: 1, name: 'john' }) == '{"hi":true}'
|
||||
assert json.encode(SkipSomeFields{ a: 1, name: 'john' },
|
||||
prettify: true
|
||||
) == '{
|
||||
"hi": true
|
||||
}'
|
||||
}
|
||||
|
||||
fn test_omit_fields() {
|
||||
assert json.encode(OmitFields{}) == '{}'
|
||||
assert json.encode(OmitFieldsAlias{}) == '{}'
|
||||
|
||||
r1 := OmitemptyRegressionResp{
|
||||
options: ['first', 'second']
|
||||
}
|
||||
r2 := OmitemptyRegressionResp{
|
||||
number: &OmitemptyRegressionNumber{0, 0}
|
||||
}
|
||||
r3 := OmitemptyRegressionResp{
|
||||
metadata: {
|
||||
'kind': 'test'
|
||||
}
|
||||
}
|
||||
assert json.encode(r1) == '{"options":["first","second"]}'
|
||||
assert json.encode(r2) == '{"number":{"min":0,"max":0}}'
|
||||
assert json.encode(r3) == '{"metadata":{"kind":"test"}}'
|
||||
assert json.encode(OmitemptyRegressionResp{}) == '{}'
|
||||
assert json.encode(OmitemptyRegressionResp{
|
||||
config: OmitemptyRegressionNumber{
|
||||
min: 1
|
||||
max: 2
|
||||
}
|
||||
}) == '{"config":{"min":1,"max":2}}'
|
||||
}
|
||||
|
||||
fn test_pointer_fields() {
|
||||
assert json.encode(PointerFields{
|
||||
next: &PointerFields{
|
||||
next: &PointerFields{
|
||||
next: &PointerFields{
|
||||
data: 4
|
||||
}
|
||||
data: 3
|
||||
}
|
||||
data: 2
|
||||
}
|
||||
data: 1
|
||||
}) == '{"next":{"next":{"next":{"data":4},"data":3},"data":2},"data":1}'
|
||||
assert json.encode(PointerFieldsAlias{
|
||||
next: &PointerFieldsAlias{
|
||||
next: &PointerFieldsAlias{
|
||||
next: &PointerFieldsAlias{
|
||||
data: 4
|
||||
}
|
||||
data: 3
|
||||
}
|
||||
data: 2
|
||||
}
|
||||
data: 1
|
||||
}) == '{"next":{"next":{"next":{"data":4},"data":3},"data":2},"data":1}'
|
||||
assert json.encode(PointerFields{
|
||||
next: &PointerFields{
|
||||
next: &PointerFields{
|
||||
next: &PointerFields{
|
||||
data: 4
|
||||
}
|
||||
data: 3
|
||||
}
|
||||
data: 2
|
||||
}
|
||||
data: 1
|
||||
},
|
||||
prettify: true
|
||||
) == '{
|
||||
"next": {
|
||||
"next": {
|
||||
"next": {
|
||||
"data": 4
|
||||
},
|
||||
"data": 3
|
||||
},
|
||||
"data": 2
|
||||
},
|
||||
"data": 1
|
||||
}'
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
import x.json2 as json
|
||||
import time
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
}
|
||||
|
||||
fn test_json_string_characters() {
|
||||
assert json.encode([u8(`/`)].bytestr()).bytes() == r'"/"'.bytes()
|
||||
assert json.encode([u8(`\\`)].bytestr()).bytes() == r'"\\"'.bytes()
|
||||
assert json.encode([u8(`"`)].bytestr()).bytes() == r'"\""'.bytes()
|
||||
assert json.encode([u8(`\n`)].bytestr()).bytes() == r'"\n"'.bytes()
|
||||
assert json.encode(r'\n\r') == r'"\\n\\r"'
|
||||
assert json.encode('\\n') == r'"\\n"'
|
||||
assert json.encode(r'\n\r\b') == r'"\\n\\r\\b"'
|
||||
assert json.encode(r'\"/').bytes() == r'"\\\"/"'.bytes()
|
||||
|
||||
assert json.encode(r'\n\r\b\f\t\\\"\/') == r'"\\n\\r\\b\\f\\t\\\\\\\"\\/"'
|
||||
|
||||
text := json.decode[json.Any](r'"\n\r\b\f\t\\\"\/"') or { '' }
|
||||
assert text.json_str() == '"\\n\\r\\b\\f\\t\\\\\\"/"'
|
||||
|
||||
assert json.encode("fn main(){nprintln('Hello World! Helo \$a')\n}") == '"fn main(){nprintln(\'Hello World! Helo \$a\')\\n}"'
|
||||
assert json.encode(' And when "\'s are in the string, along with # "') == '" And when \\"\'s are in the string, along with # \\""'
|
||||
assert json.encode('a \\\nb') == r'"a \\\nb"'
|
||||
assert json.encode('Name\tJosé\nLocation\tSF.') == '"Name\\tJosé\\nLocation\\tSF."'
|
||||
}
|
||||
|
||||
fn test_json_escape_low_chars() {
|
||||
esc := '\u001b'
|
||||
assert esc.len == 1
|
||||
text := json.Any(esc)
|
||||
assert text.json_str() == r'"\u001b"'
|
||||
|
||||
assert json.encode('\u000f') == r'"\u000f"'
|
||||
assert json.encode('\u0020') == r'" "'
|
||||
assert json.encode('\u0000') == r'"\u0000"'
|
||||
}
|
||||
|
||||
fn test_json_string() {
|
||||
text := json.Any('te✔st')
|
||||
|
||||
assert text.json_str() == r'"te✔st"'
|
||||
assert json.encode('te✔st', escape_unicode: true) == r'"te\u2714st"'
|
||||
|
||||
boolean := json.Any(true)
|
||||
assert boolean.json_str() == 'true'
|
||||
integer := json.Any(int(-5))
|
||||
assert integer.json_str() == '-5'
|
||||
u64integer := json.Any(u64(5000))
|
||||
assert u64integer.json_str() == '5000'
|
||||
i64integer := json.Any(i64(-17))
|
||||
assert i64integer.json_str() == '-17'
|
||||
}
|
||||
|
||||
fn test_json_string_emoji() {
|
||||
text := json.Any('🐈')
|
||||
assert text.json_str() == r'"🐈"'
|
||||
assert json.Any('💀').json_str() == r'"💀"'
|
||||
|
||||
assert json.encode('🐈') == r'"🐈"'
|
||||
assert json.encode('💀') == r'"💀"'
|
||||
// assert json.encode('🐈💀') == r'"🐈💀"'
|
||||
}
|
||||
|
||||
fn test_json_string_non_ascii() {
|
||||
text := json.Any('ひらがな')
|
||||
assert text.json_str() == r'"ひらがな"'
|
||||
|
||||
assert json.encode('ひらがな', escape_unicode: true) == r'"\u3072\u3089\u304c\u306a"'
|
||||
}
|
||||
|
||||
fn test_utf8_strings_are_not_modified() {
|
||||
original := '{"s":"Schilddrüsenerkrankungen"}'
|
||||
deresult := json.decode[json.Any](original)!
|
||||
assert deresult.str() == original
|
||||
|
||||
assert json.encode('ü') == '"ü"'
|
||||
assert json.encode('Schilddrüsenerkrankungen') == '"Schilddrüsenerkrankungen"'
|
||||
}
|
||||
|
||||
fn test_encoder_unescaped_utf32() ! {
|
||||
jap_text := json.Any('ひらがな')
|
||||
|
||||
assert json.encode(jap_text) == '"${jap_text}"'
|
||||
|
||||
emoji_text := json.Any('🐈')
|
||||
assert json.encode(emoji_text) == '"${emoji_text}"'
|
||||
|
||||
assert json.encode('ひらがな') == '"ひらがな"'
|
||||
}
|
||||
|
||||
fn test_encoder_prettify() {
|
||||
obj := {
|
||||
'hello': json.Any('world')
|
||||
'arr': [json.Any('im a string'), [json.Any('3rd level')]]
|
||||
'obj': {
|
||||
'map': json.Any('map inside a map')
|
||||
}
|
||||
}
|
||||
assert json.encode(obj, prettify: true, indent_string: ' ') == '{
|
||||
"hello": "world",
|
||||
"arr": [
|
||||
"im a string",
|
||||
[
|
||||
"3rd level"
|
||||
]
|
||||
],
|
||||
"obj": {
|
||||
"map": "map inside a map"
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
pub struct Test {
|
||||
val string
|
||||
}
|
||||
|
||||
struct SharedMapHolder {
|
||||
a int
|
||||
cache shared map[u64]string
|
||||
}
|
||||
|
||||
fn test_encode_struct() {
|
||||
enc := json.encode(Test{'hello!'})
|
||||
assert enc == '{"val":"hello!"}'
|
||||
}
|
||||
|
||||
fn test_encode_struct_with_shared_map_field() {
|
||||
mut holder := SharedMapHolder{
|
||||
a: 1
|
||||
}
|
||||
|
||||
rlock holder.cache {
|
||||
assert json.encode(holder) == '{"a":1,"cache":{}}'
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Uri {
|
||||
protocol string
|
||||
path string
|
||||
}
|
||||
|
||||
pub fn (u Uri) json_str() string {
|
||||
return '"${u.protocol}://${u.path}"'
|
||||
}
|
||||
|
||||
fn test_encode_encodable() {
|
||||
assert json.encode(Uri{'file', 'path/to/file'}) == '"file://path/to/file"'
|
||||
}
|
||||
|
||||
fn test_encode_array() {
|
||||
array_of_struct := [StructType[[]bool]{
|
||||
val: [false, true]
|
||||
}, StructType[[]bool]{
|
||||
val: [true, false]
|
||||
}]
|
||||
|
||||
assert json.encode([1, 2, 3]) == '[1,2,3]'
|
||||
|
||||
assert json.encode(array_of_struct) == '[{"val":[false,true]},{"val":[true,false]}]'
|
||||
}
|
||||
|
||||
fn test_encode_simple() {
|
||||
assert json.encode('hello!') == '"hello!"'
|
||||
assert json.encode(1) == '1'
|
||||
}
|
||||
|
||||
fn test_encode_value() {
|
||||
mut manifest := map[string]json.Any{}
|
||||
|
||||
manifest['server_path'] = json.Any('new_path')
|
||||
manifest['last_updated'] = json.Any('timestamp.format_ss()')
|
||||
manifest['from_source'] = json.Any('from_source')
|
||||
|
||||
assert json.encode(manifest, prettify: true, indent_string: ' ') == r'{
|
||||
"server_path": "new_path",
|
||||
"last_updated": "timestamp.format_ss()",
|
||||
"from_source": "from_source"
|
||||
}'
|
||||
}
|
||||
|
||||
fn test_encode_time() {
|
||||
assert json.encode({
|
||||
'bro': json.Any(time.Time{})
|
||||
}) == '{"bro":"0000-01-01T00:00:00.000Z"}'
|
||||
|
||||
assert json.encode({
|
||||
'bro': time.Time{}
|
||||
}) == '{"bro":"0000-01-01T00:00:00.000Z"}'
|
||||
|
||||
assert json.encode(time.Time{}) == '"0000-01-01T00:00:00.000Z"'
|
||||
}
|
||||
|
||||
fn test_encode_float() {
|
||||
// remove ending `.0`
|
||||
assert json.encode(1.0) == '1'
|
||||
|
||||
// TODO: better generate `1e10` instead of `1e+10`
|
||||
assert json.encode(1.0e+10) == '1e+10'
|
||||
assert json.encode(1.0e10) == '1e+10'
|
||||
assert json.encode(1.e10) == '1e+10'
|
||||
assert json.encode(1.0e-10) == '1e-10'
|
||||
assert json.encode(1.1e10) == '1.1e+10'
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import x.json2
|
||||
|
||||
struct IntegerValues {
|
||||
mut:
|
||||
ux8 u8
|
||||
ux16 u16
|
||||
ux32 u32
|
||||
ux64 u64
|
||||
sx8 i8
|
||||
sx16 i16
|
||||
sx32 int
|
||||
sx64 i64
|
||||
}
|
||||
|
||||
fn test_all_primitive_integer_types_are_encodable_and_decodable() {
|
||||
f := IntegerValues{1, 2, 3, 4, -1, -2, -3, -4}
|
||||
s := json2.encode[IntegerValues](f)
|
||||
assert s == '{"ux8":1,"ux16":2,"ux32":3,"ux64":4,"sx8":-1,"sx16":-2,"sx32":-3,"sx64":-4}'
|
||||
x := json2.decode[IntegerValues](s)!
|
||||
assert x == f
|
||||
println('done')
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import x.json2 as json
|
||||
|
||||
enum JobTitle {
|
||||
manager
|
||||
executive
|
||||
worker
|
||||
}
|
||||
|
||||
struct Employee {
|
||||
pub mut:
|
||||
name string
|
||||
age int
|
||||
salary f32
|
||||
title JobTitle
|
||||
}
|
||||
|
||||
fn test_fast_raw_decode() {
|
||||
s := '{"name":"Peter","age":28,"salary":95000.5,"title":2}'
|
||||
o := json.decode[json.Any](s) or {
|
||||
assert false
|
||||
json.Any('')
|
||||
}
|
||||
str := o.str()
|
||||
assert str == '{"name":"Peter","age":28,"salary":95000.5,"title":2}'
|
||||
}
|
||||
|
||||
fn test_character_unescape() {
|
||||
message := r'{
|
||||
"newline": "new\nline",
|
||||
"tab": "\ttab",
|
||||
"backslash": "back\\slash",
|
||||
"quotes": "\"quotes\"",
|
||||
"slash":"\/dev\/null"
|
||||
}'
|
||||
mut obj := json.decode[json.Any](message) or {
|
||||
println(err)
|
||||
assert false
|
||||
return
|
||||
}
|
||||
lines := obj.as_map()
|
||||
assert lines['newline'] or { 0 }.str() == 'new\nline'
|
||||
assert lines['tab'] or { 0 }.str() == '\ttab'
|
||||
assert lines['backslash'] or { 0 }.str() == 'back\\slash'
|
||||
assert lines['quotes'] or { 0 }.str() == '"quotes"'
|
||||
assert lines['slash'] or { 0 }.str() == '/dev/null'
|
||||
}
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
}
|
||||
|
||||
fn test_struct_with_bool_to_map() {
|
||||
array_of_struct := [StructType[bool]{
|
||||
val: true
|
||||
}, StructType[bool]{
|
||||
val: false
|
||||
}]
|
||||
|
||||
mut array_of_map := []json.Any{}
|
||||
|
||||
for variable in array_of_struct {
|
||||
array_of_map << json.map_from(variable)
|
||||
}
|
||||
|
||||
assert array_of_map.str() == '[{"val":true},{"val":false}]'
|
||||
}
|
||||
|
||||
fn test_struct_with_string_to_map() {
|
||||
array_of_struct := [StructType[string]{
|
||||
val: 'true'
|
||||
}, StructType[string]{
|
||||
val: 'false'
|
||||
}]
|
||||
|
||||
mut array_of_map := []json.Any{}
|
||||
|
||||
for variable in array_of_struct {
|
||||
array_of_map << json.map_from(variable)
|
||||
}
|
||||
|
||||
assert array_of_map.str() == '[{"val":"true"},{"val":"false"}]'
|
||||
}
|
||||
|
||||
fn test_struct_with_array_to_map() {
|
||||
array_of_struct := [StructType[[]bool]{
|
||||
val: [false, true]
|
||||
}, StructType[[]bool]{
|
||||
val: [true, false]
|
||||
}]
|
||||
|
||||
mut array_of_map := []json.Any{}
|
||||
|
||||
for variable in array_of_struct {
|
||||
array_of_map << json.map_from(variable)
|
||||
}
|
||||
|
||||
assert array_of_map.str() == '[{"val":[false,true]},{"val":[true,false]}]'
|
||||
}
|
||||
|
||||
fn test_struct_with_array_of_arrays_to_map() {
|
||||
array_of_struct := [
|
||||
StructType[[][]bool]{
|
||||
val: [[true, false], [true, false]]
|
||||
},
|
||||
StructType[[][]bool]{
|
||||
val: [[false, true], [false, true]]
|
||||
},
|
||||
]
|
||||
mut array_of_map := []json.Any{}
|
||||
for variable in array_of_struct {
|
||||
array_of_map << json.map_from(variable)
|
||||
}
|
||||
assert array_of_map.str() == '[{"val":[[true,false],[true,false]]},{"val":[[false,true],[false,true]]}]'
|
||||
|
||||
array_of_struct_int := [
|
||||
StructType[[][]int]{
|
||||
val: [[1, 0], [1, 0]]
|
||||
},
|
||||
StructType[[][]int]{
|
||||
val: [[0, 1], [0, 1]]
|
||||
},
|
||||
]
|
||||
mut array_of_map_int := []json.Any{}
|
||||
for variable in array_of_struct_int {
|
||||
array_of_map_int << json.map_from(variable)
|
||||
}
|
||||
assert array_of_map_int.str() == '[{"val":[[1,0],[1,0]]},{"val":[[0,1],[0,1]]}]'
|
||||
}
|
||||
|
||||
fn test_struct_with_number_to_map() {
|
||||
assert json.map_from(StructType[string]{'3'}).str() == '{"val":"3"}'
|
||||
assert json.map_from(StructType[bool]{true}).str() == '{"val":true}'
|
||||
assert json.map_from(StructType[i8]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[i16]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[int]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[i64]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[i8]{-3}).str() == '{"val":-3}'
|
||||
assert json.map_from(StructType[i16]{i16(-3)}).str() == '{"val":-3}'
|
||||
assert json.map_from(StructType[int]{-3}).str() == '{"val":-3}'
|
||||
assert json.map_from(StructType[i64]{-3}).str() == '{"val":-3}'
|
||||
assert json.map_from(StructType[f32]{3.0}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[f64]{3.0}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[u8]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[u16]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[u32]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[u64]{3}).str() == '{"val":3}'
|
||||
}
|
||||
|
||||
fn test_struct_with_struct_to_map() {
|
||||
assert json.map_from(StructType[StructType[string]]{StructType[string]{'3'}}).str() == '{"val":{"val":"3"}}'
|
||||
assert json.map_from(StructType[StructType[int]]{StructType[int]{3}}).str() == '{"val":{"val":3}}'
|
||||
}
|
||||
|
||||
fn test_maps() {
|
||||
assert json.decode[map[string]string]('{"test":"abc"}')! == {
|
||||
'test': 'abc'
|
||||
}
|
||||
|
||||
// assert json.decode[map[string]StructType[bool]]('{"test":{"val":true}}') or {
|
||||
// dump(err)
|
||||
// assert false
|
||||
// } == {"test":StructType[bool]{true}}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import x.json2 as 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
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import x.json2 as json
|
||||
import time
|
||||
|
||||
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_encode_decode_time() {
|
||||
user := User2{
|
||||
age: 25
|
||||
reg_date: time.new(year: 2020, month: 12, day: 22, hour: 7, minute: 23)
|
||||
}
|
||||
s := json.encode(user)
|
||||
println(s)
|
||||
assert s.contains('"reg_date":1608621780')
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct DbConfig {
|
||||
host string
|
||||
dbname string
|
||||
user string
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_empty() {
|
||||
json.decode[DbConfig]('') or {
|
||||
assert err.msg() == 'failed to decode JSON string'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct City {
|
||||
name string
|
||||
}
|
||||
|
||||
struct Country {
|
||||
cities []City
|
||||
name string
|
||||
}
|
||||
|
||||
struct Data {
|
||||
countries []Country
|
||||
users map[string]User
|
||||
extra map[string]map[string]int
|
||||
}
|
||||
|
||||
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_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()
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct DbConfig {
|
||||
host string
|
||||
dbname string
|
||||
user string
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_just_brace() {
|
||||
json.decode[DbConfig]('{') or {
|
||||
assert err.msg() == 'failed to decode JSON string: {'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import x.json2 as json
|
||||
|
||||
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}' == '{}'
|
||||
}
|
||||
|
||||
fn test_decode_missing_maps_field() ! {
|
||||
info := json.decode[Info]('{"id": 22, "items": null}')!
|
||||
assert info.id == 22
|
||||
assert '${info.items}' == '[]'
|
||||
assert '${info.maps}' == '{}'
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct DbConfig {
|
||||
host string
|
||||
dbname string
|
||||
user string
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_trailing_comma_at_end() {
|
||||
txt := '{
|
||||
"host": "localhost",
|
||||
"dbname": "alex",
|
||||
"user": "alex",
|
||||
}'
|
||||
json.decode[DbConfig](txt) or {
|
||||
assert err.msg().contains(' "user": "alex",\n}')
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct ChildNullish {
|
||||
name string
|
||||
}
|
||||
|
||||
struct NullishStruct {
|
||||
name string
|
||||
lastname string
|
||||
age int
|
||||
salary f32
|
||||
child ChildNullish
|
||||
}
|
||||
|
||||
struct RequiredStruct {
|
||||
name string @[required]
|
||||
lastname string
|
||||
}
|
||||
|
||||
fn test_required() ! {
|
||||
nullish_one :=
|
||||
json.decode[NullishStruct]('{"name":"Peter", "lastname":null, "age":28,"salary":95000.5,"title":"worker"}')!
|
||||
assert nullish_one.name == 'Peter'
|
||||
assert nullish_one.lastname == ''
|
||||
assert nullish_one.age == 28
|
||||
assert nullish_one.salary == 95000.5
|
||||
assert nullish_one.child.name == ''
|
||||
|
||||
nullish_two :=
|
||||
json.decode[NullishStruct]('{"name":"Peter", "lastname": "Parker", "age":28,"salary":95000.5,"title":"worker"}')!
|
||||
assert nullish_two.name == 'Peter'
|
||||
assert nullish_two.lastname == 'Parker'
|
||||
assert nullish_two.age == 28
|
||||
assert nullish_two.salary == 95000.5
|
||||
assert nullish_two.child.name == ''
|
||||
|
||||
nullish_third :=
|
||||
json.decode[NullishStruct]('{"name":"Peter", "age":28,"salary":95000.5,"title":"worker", "child": {"name":"Nullish"}}')!
|
||||
assert nullish_third.name == 'Peter'
|
||||
assert nullish_third.lastname == ''
|
||||
assert nullish_third.age == 28
|
||||
assert nullish_third.salary == 95000.5
|
||||
assert nullish_third.child.name == 'Nullish'
|
||||
|
||||
required_struct := json.decode[RequiredStruct]('{"name":"Peter", "lastname": "Parker"}')!
|
||||
assert required_struct.name == 'Peter'
|
||||
assert required_struct.lastname == 'Parker'
|
||||
|
||||
required_struct_err := json.decode[RequiredStruct]('{"name": null, "lastname": "Parker"}') or {
|
||||
assert err.msg() == "type mismatch for field 'name', expecting `string` type, got: null"
|
||||
RequiredStruct{
|
||||
name: 'Peter'
|
||||
lastname: 'Parker'
|
||||
}
|
||||
}
|
||||
println(required_struct_err)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct DbConfig {
|
||||
host string
|
||||
dbname string
|
||||
user string
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_in_the_middle() {
|
||||
txt := '{"host": "localhost", "dbname": "alex" "user": "alex", "port": "1234"}'
|
||||
json.decode[DbConfig](txt) or {
|
||||
assert err.msg().contains('ost", "dbname": "alex" "user":')
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import x.json2 as json
|
||||
|
||||
type Any = string | f32 | bool | ?int
|
||||
|
||||
fn test_main() {
|
||||
x := json.decode[[]Any]('["hi", -9.8e7, true, null]')!
|
||||
assert dump(x) == [Any('hi'), Any(f32(-9.8e+7)), Any(true), Any('')]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import x.json2 as json
|
||||
|
||||
type Test = []bool | []int | []string | string
|
||||
|
||||
struct Some {
|
||||
t Test
|
||||
}
|
||||
|
||||
fn test_json_decode_with_sumtype() {
|
||||
v1 := json.decode[Some]('{"t": ["string", "string2"]}')!
|
||||
println(v1)
|
||||
assert v1.t == Test(['string', 'string2'])
|
||||
|
||||
v2 := json.decode[Some]('{"t": [11, 22]}')!
|
||||
println(v2)
|
||||
assert v2.t == Test([11, 22])
|
||||
|
||||
v3 := json.decode[Some]('{"t": [true, false]}')!
|
||||
println(v3)
|
||||
assert v3.t == Test([true, false])
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct UnicodeString {
|
||||
emoji string
|
||||
}
|
||||
|
||||
fn test_encode_unicode_as_ascii_escape_sequences() {
|
||||
valid_json := r'{"emoji":"\u3007"}'
|
||||
decoded := json.decode[UnicodeString](valid_json)!
|
||||
assert decoded.emoji == '〇'
|
||||
assert json.encode(UnicodeString{
|
||||
emoji: '〇'
|
||||
}) == valid_json
|
||||
assert json.encode('😀') == r'"\uD83D\ude00"'
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import x.json2 as json
|
||||
|
||||
pub struct DocumentFindFilter[T] {
|
||||
pub:
|
||||
selector ?T
|
||||
selector2 ?[]T
|
||||
selector3 ?[1]T
|
||||
limit ?int
|
||||
skip ?int
|
||||
fields ?[]string // This breaks the compiler when encoding to JSON
|
||||
conflicts ?bool
|
||||
read_quorum ?int @[json: r]
|
||||
update ?bool
|
||||
stable ?bool
|
||||
stale ?string
|
||||
execution_stats ?bool
|
||||
}
|
||||
|
||||
fn test_string() {
|
||||
t := DocumentFindFilter[string]{
|
||||
selector: 'aa'
|
||||
selector2: ['a', 'b']
|
||||
selector3: ['z']!
|
||||
}
|
||||
|
||||
assert json.encode(t) == '{"selector":"aa","selector2":["a","b"],"selector3":["z"]}'
|
||||
assert json.decode[DocumentFindFilter[string]]('{"selector":"aa","selector2":["a","b"],"selector3":["z"]}')! == t
|
||||
}
|
||||
|
||||
fn test_int() ! {
|
||||
t := DocumentFindFilter[int]{
|
||||
selector: 1
|
||||
selector2: [1, 2]
|
||||
selector3: [3]!
|
||||
}
|
||||
|
||||
assert json.encode(t) == '{"selector":1,"selector2":[1,2],"selector3":[3]}'
|
||||
assert json.decode[DocumentFindFilter[int]]('{"selector":1,"selector2":[1,2],"selector3":[3]}')! == t
|
||||
}
|
||||
|
||||
fn test_none() ! {
|
||||
t := DocumentFindFilter[int]{}
|
||||
|
||||
assert json.encode(t) == '{}'
|
||||
assert json.decode[DocumentFindFilter[int]]('{}')! == t
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct Test {
|
||||
optional_alias ?MyAlias // primitive
|
||||
optional_struct ?MyAlias2 // complex
|
||||
}
|
||||
|
||||
struct Complex {
|
||||
a int = 3
|
||||
}
|
||||
|
||||
type MyAlias = int
|
||||
type MyAlias2 = Complex
|
||||
|
||||
fn test_empty() {
|
||||
test := Test{}
|
||||
encoded := json.encode(test)
|
||||
assert dump(encoded) == '{}'
|
||||
assert json.decode[Test]('{}')! == test
|
||||
}
|
||||
|
||||
fn test_value() {
|
||||
test := Test{
|
||||
optional_alias: 1
|
||||
}
|
||||
encoded := json.encode(test)
|
||||
assert dump(encoded) == '{"optional_alias":1}'
|
||||
assert json.decode[Test]('{"optional_alias":1}')! == test
|
||||
}
|
||||
|
||||
fn test_value_2() {
|
||||
test := Test{
|
||||
optional_alias: 1
|
||||
optional_struct: Complex{
|
||||
a: 1
|
||||
}
|
||||
}
|
||||
encoded := json.encode(test)
|
||||
assert dump(encoded) == '{"optional_alias":1,"optional_struct":{"a":1}}'
|
||||
assert json.decode[Test]('{"optional_alias":1,"optional_struct":{"a":1}}')! == test
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
module main
|
||||
|
||||
import x.json2 as json
|
||||
|
||||
struct MyStruct {
|
||||
name string // should fail
|
||||
age ?int
|
||||
active bool
|
||||
}
|
||||
|
||||
struct TestStructOne {
|
||||
property_one string @[json: 'propertyOne']
|
||||
property_two string @[json: 'propertyTwo']
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
mut errors := 0
|
||||
json.decode[MyStruct]('{ "name": 1}') or {
|
||||
errors++
|
||||
assert err.msg() == "type mismatch for field 'name', expecting `string` type, got: 1"
|
||||
}
|
||||
json.decode[MyStruct]('{ "name": "John Doe", "age": ""}') or {
|
||||
errors++
|
||||
assert err.msg() == 'type mismatch for field \'age\', expecting `?int` type, got: ""'
|
||||
}
|
||||
json.decode[MyStruct]('{ "name": "John Doe", "age": 1, "active": ""}') or {
|
||||
errors++
|
||||
assert err.msg() == 'type mismatch for field \'active\', expecting `bool` type, got: ""'
|
||||
}
|
||||
res := json.decode[MyStruct]('{ "name": "John Doe", "age": "1"}') or { panic(err) }
|
||||
assert errors == 3
|
||||
assert res.name == 'John Doe'
|
||||
}
|
||||
|
||||
fn test_decode_object_into_string_field() {
|
||||
payload := '{"propertyOne":"property_two should stay a regular string {}","propertyTwo":{}}'
|
||||
res := json.decode[TestStructOne](payload) or { panic(err) }
|
||||
assert res.property_one == 'property_two should stay a regular string {}'
|
||||
assert res.property_two == '{}'
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct TestRawStringifiedObject {
|
||||
metadata string @[raw]
|
||||
}
|
||||
|
||||
fn test_stringified_object_returns_error_for_raw_field() {
|
||||
stringified_json :=
|
||||
json.encode('{"metadata":{"topLevelProperty":{"nestedProperty1":"Value 1"}}}')
|
||||
json.decode[TestRawStringifiedObject](stringified_json) or {
|
||||
assert err.msg().starts_with('Json element is not an object:')
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import x.json2 as json
|
||||
|
||||
type Prices = Price | []Price
|
||||
|
||||
pub struct ShopResponseData {
|
||||
attributes Attributes
|
||||
}
|
||||
|
||||
struct Attributes {
|
||||
price ?Prices
|
||||
}
|
||||
|
||||
struct Price {
|
||||
net f64
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
data3 := json.encode(ShopResponseData{
|
||||
attributes: Attributes{
|
||||
price: Prices([Price{
|
||||
net: 1.2
|
||||
}])
|
||||
}
|
||||
})
|
||||
assert data3 == '{"attributes":{"price":[{"net":1.2,"_type":"Price"}]}}'
|
||||
|
||||
entity3 := json.decode[ShopResponseData](data3) or { panic(err) }
|
||||
assert entity3 == ShopResponseData{
|
||||
attributes: Attributes{
|
||||
price: Prices([Price{
|
||||
net: 1.2
|
||||
}])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct AnyStruct[T] {
|
||||
val T
|
||||
}
|
||||
|
||||
struct OptAnyStruct[T] {
|
||||
val ?T
|
||||
}
|
||||
|
||||
// struct OptAnyArrStruct {
|
||||
// val []?json.Any
|
||||
// }
|
||||
|
||||
fn test_values() {
|
||||
assert json.decode[AnyStruct[json.Any]]('{"val":5}')!.val.int() == 5
|
||||
assert json.decode[OptAnyStruct[json.Any]]('{}')!.val == none
|
||||
assert json.decode[OptAnyStruct[json.Any]]('{"val":null}')!.val == none
|
||||
assert json.decode[AnyStruct[[]json.Any]]('{"val":[5,10]}')!.val.map(it.int()) == [
|
||||
5,
|
||||
10,
|
||||
]
|
||||
// assert json.decode[OptAnyArrStruct]('{"val":[5,null,10]}')!.val == [?json.Any(5),json.Null{},10] // skipped because test still fails even though they're the same
|
||||
|
||||
assert json.encode[AnyStruct[json.Any]](AnyStruct[json.Any]{json.Any(5)}) == '{"val":5}'
|
||||
assert json.encode[OptAnyStruct[json.Any]](OptAnyStruct[json.Any]{none}) == '{}'
|
||||
assert json.encode[AnyStruct[[]json.Any]](AnyStruct[[]json.Any]{[json.Any(5), 10]}) == '{"val":[5,10]}'
|
||||
// assert json.encode[OptAnyArrStruct](OptAnyArrStruct{[?json.Any(5),none,10]}) == '{"val":[5,null,10]}' // encode_array has not implemented optional arrays yet
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import x.json2 as json
|
||||
|
||||
const data = '
|
||||
{
|
||||
"comments": {
|
||||
"26788945": {
|
||||
"id": "26788945",
|
||||
"message": "some comment 1"
|
||||
},
|
||||
"26788946": {
|
||||
"id": "26788946",
|
||||
"message": "some comment 2"
|
||||
},
|
||||
"26788947": {
|
||||
"id": "26788947",
|
||||
"message": "some comment 3"
|
||||
}
|
||||
},
|
||||
"comments2": {
|
||||
"26788945": true,
|
||||
"26788946": false,
|
||||
"26788947": true
|
||||
},
|
||||
"comments3": {
|
||||
"26788945": 1,
|
||||
"26788946": 2,
|
||||
"26788947": 3
|
||||
}
|
||||
}
|
||||
'
|
||||
|
||||
pub struct Comment {
|
||||
id string
|
||||
message string
|
||||
}
|
||||
|
||||
struct Comments {
|
||||
mut:
|
||||
comments map[string]Comment
|
||||
comments2 map[string]bool
|
||||
comments3 map[string]int
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
mut root := json.decode[Comments](data)!
|
||||
assert root.comments.len == 3
|
||||
assert root.comments['26788945']!.id == '26788945'
|
||||
assert root.comments['26788946']!.id == '26788946'
|
||||
assert root.comments['26788947']!.id == '26788947'
|
||||
|
||||
assert root.comments2['26788945']! == true
|
||||
assert root.comments2['26788946']! == false
|
||||
assert root.comments2['26788947']! == true
|
||||
|
||||
assert root.comments3['26788945']! == 1
|
||||
assert root.comments3['26788946']! == 2
|
||||
assert root.comments3['26788947']! == 3
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
import x.json2 as json
|
||||
import time
|
||||
|
||||
const fixed_time = time.new(
|
||||
year: 2022
|
||||
month: 3
|
||||
day: 11
|
||||
hour: 13
|
||||
minute: 54
|
||||
second: 25
|
||||
)
|
||||
|
||||
type StringAlias = string
|
||||
type BoolAlias = bool
|
||||
type IntAlias = int
|
||||
|
||||
type SumTypes = bool | int | string
|
||||
|
||||
enum Enumerates {
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e = 99
|
||||
f
|
||||
}
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
}
|
||||
|
||||
struct StructTypeSub {
|
||||
test string
|
||||
}
|
||||
|
||||
struct StructTypeOption[T] {
|
||||
mut:
|
||||
val ?T
|
||||
}
|
||||
|
||||
struct StructTypePointer[T] {
|
||||
mut:
|
||||
val &T
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields[T] {
|
||||
mut:
|
||||
val T @[json: '-']
|
||||
val1 T
|
||||
val2 T @[json: '-']
|
||||
val3 T
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields2[T] {
|
||||
mut:
|
||||
val T
|
||||
val1 T @[json: '-']
|
||||
val2 T
|
||||
val3 T @[json: '-']
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields3[T] {
|
||||
mut:
|
||||
val T @[json: '-']
|
||||
val1 T @[json: '-']
|
||||
val2 T @[json: '-']
|
||||
val3 T @[json: '-']
|
||||
}
|
||||
|
||||
struct StructTypeSkippedField4 {
|
||||
mut:
|
||||
val map[string]string @[json: '-']
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields5[T] {
|
||||
mut:
|
||||
val T @[skip]
|
||||
val1 T @[skip]
|
||||
val2 T @[skip]
|
||||
val3 T @[skip]
|
||||
}
|
||||
|
||||
struct StructTypeSkippedFields6[T] {
|
||||
mut:
|
||||
val T
|
||||
val1 T @[skip]
|
||||
val2 T
|
||||
val3 T @[skip]
|
||||
}
|
||||
|
||||
fn test_types() {
|
||||
assert json.decode[StructType[string]]('{"val": ""}')!.val == ''
|
||||
assert json.decode[StructType[string]]('{"val": "0"}')!.val == '0'
|
||||
assert json.decode[StructType[string]]('{"val": "1"}')!.val == '1'
|
||||
assert json.decode[StructType[string]]('{"val": "2"}')!.val == '2'
|
||||
// assert json.decode[StructType[string]]('{"val": 0}')!.val == '0' // This should be a error
|
||||
// assert json.decode[StructType[string]]('{"val": 1}')!.val == '1' // This should be a error
|
||||
// assert json.decode[StructType[string]]('{"val": 2}')!.val == '2' // This should be a error
|
||||
assert json.decode[StructType[string]]('{"val": "true"}')!.val == 'true'
|
||||
assert json.decode[StructType[string]]('{"val": "false"}')!.val == 'false'
|
||||
// assert json.decode[StructType[string]]('{"val": true}')!.val == 'true' // This should be a error
|
||||
// assert json.decode[StructType[string]]('{"val": false}')!.val == 'false' // This should be a error
|
||||
|
||||
// assert json.decode[StructType[bool]]('{"val": ""}')!.val == false // This should be a error
|
||||
// assert json.decode[StructType[bool]]('{"val": "0"}')!.val == false // This should be a error
|
||||
// assert json.decode[StructType[bool]]('{"val": "1"}')!.val == true // This should be a error
|
||||
// assert json.decode[StructType[bool]]('{"val": "2"}')!.val == true // This should be a error
|
||||
// assert json.decode[StructType[bool]]('{"val": 0}')!.val == false // This should be a error
|
||||
// assert json.decode[StructType[bool]]('{"val": 1}')!.val == true // This should be a error
|
||||
// assert json.decode[StructType[bool]]('{"val": 2}')!.val == true // This should be a error
|
||||
// assert json.decode[StructType[bool]]('{"val": "true"}')!.val == true // This should be a error
|
||||
// assert json.decode[StructType[bool]]('{"val": "false"}')!.val == false // This should be a error
|
||||
assert json.decode[StructType[bool]]('{"val": true}')!.val == true
|
||||
assert json.decode[StructType[bool]]('{"val": false}')!.val == false
|
||||
|
||||
// assert json.decode[StructType[int]]('{"val": ""}')!.val == 0 // This should be a error
|
||||
// assert json.decode[StructType[int]]('{"val": "0"}')!.val == 0 // This should be a error
|
||||
// assert json.decode[StructType[int]]('{"val": "1"}')!.val == 1 // This should be a error
|
||||
// assert json.decode[StructType[int]]('{"val": "2"}')!.val == 2 // This should be a error
|
||||
assert json.decode[StructType[int]]('{"val": 0}')!.val == 0
|
||||
assert json.decode[StructType[int]]('{"val": 1}')!.val == 1
|
||||
assert json.decode[StructType[int]]('{"val": 2}')!.val == 2
|
||||
// assert json.decode[StructType[int]]('{"val": "true"}')!.val == 0 // This should be a error
|
||||
// assert json.decode[StructType[int]]('{"val": "false"}')!.val == 0 // This should be a error
|
||||
// assert json.decode[StructType[int]]('{"val": true}')!.val == 1 // This should be a error
|
||||
// assert json.decode[StructType[int]]('{"val": false}')!.val == 0 // This should be a error
|
||||
|
||||
assert json.decode[StructType[time.Time]]('{"val": "2022-03-11T13:54:25.000Z"}')!.val == fixed_time
|
||||
assert json.decode[StructType[time.Time]]('{"val": "2001-01-05"}')!.val.year == 2001
|
||||
assert json.decode[StructType[time.Time]]('{"val": "2001-01-05"}')!.val.month == 1
|
||||
assert json.decode[StructType[time.Time]]('{"val": "2001-01-05"}')!.val.day == 5
|
||||
assert json.decode[StructType[time.Time]]('{"val": "2001-01-05"}')!.val.hour == 0
|
||||
assert json.decode[StructType[time.Time]]('{"val": "2001-01-05"}')!.val.minute == 0
|
||||
assert json.decode[StructType[time.Time]]('{"val": "2001-01-05"}')!.val.second == 0
|
||||
|
||||
assert json.decode[StructType[StructTypeSub]]('{"val": {"test": "test"}}')!.val.test == 'test'
|
||||
|
||||
assert json.decode[StructType[Enumerates]]('{"val": 0}')!.val == .a
|
||||
assert json.decode[StructType[Enumerates]]('{"val": 1}')!.val == .b
|
||||
assert json.decode[StructType[Enumerates]]('{"val": 99}')!.val == .e
|
||||
assert json.decode[StructType[Enumerates]]('{}')!.val == .a
|
||||
|
||||
if x := json.decode[StructTypeOption[Enumerates]]('{"val": 0}')!.val {
|
||||
assert x == .a
|
||||
}
|
||||
if x := json.decode[StructTypeOption[Enumerates]]('{"val": 1}')!.val {
|
||||
assert x == .b
|
||||
}
|
||||
if x := json.decode[StructTypeOption[Enumerates]]('{"val": 99}')!.val {
|
||||
assert x == .e
|
||||
}
|
||||
if x := json.decode[StructTypeOption[Enumerates]]('{}')!.val {
|
||||
assert false
|
||||
} else {
|
||||
assert true
|
||||
}
|
||||
}
|
||||
|
||||
fn test_skipped_fields() {
|
||||
if x := json.decode[StructTypeSkippedFields[int]]('{"val":10,"val1":10,"val2":10,"val3":10}') {
|
||||
assert x.val == 0
|
||||
assert x.val1 == 10
|
||||
assert x.val2 == 0
|
||||
assert x.val3 == 10
|
||||
} else {
|
||||
assert false
|
||||
}
|
||||
|
||||
if x := json.decode[StructTypeSkippedFields2[int]]('{"val":10,"val1":10,"val2":10,"val3":10}') {
|
||||
assert x.val == 10
|
||||
assert x.val1 == 0
|
||||
assert x.val2 == 10
|
||||
assert x.val3 == 0
|
||||
} else {
|
||||
assert false
|
||||
}
|
||||
|
||||
if x := json.decode[StructTypeSkippedFields3[int]]('{"val":10,"val1":10,"val2":10,"val3":10}') {
|
||||
assert x.val == 0
|
||||
assert x.val1 == 0
|
||||
assert x.val2 == 0
|
||||
assert x.val3 == 0
|
||||
} else {
|
||||
assert false
|
||||
}
|
||||
|
||||
if x := json.decode[StructTypeSkippedField4]('{"val":{"a":"b"}}') {
|
||||
assert x.val.len == 0
|
||||
} else {
|
||||
assert false
|
||||
}
|
||||
|
||||
if x := json.decode[StructTypeSkippedFields5[int]]('{"val":10,"val1":10,"val2":10,"val3":10}') {
|
||||
assert x.val == 0
|
||||
assert x.val1 == 0
|
||||
assert x.val2 == 0
|
||||
assert x.val3 == 0
|
||||
} else {
|
||||
assert false
|
||||
}
|
||||
|
||||
if x := json.decode[StructTypeSkippedFields6[int]]('{"val":10,"val1":10,"val2":10,"val3":10}') {
|
||||
assert x.val == 10
|
||||
assert x.val1 == 0
|
||||
assert x.val2 == 10
|
||||
assert x.val3 == 0
|
||||
} else {
|
||||
assert false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import x.json2 as json
|
||||
|
||||
fn test_raw_decode_string() {
|
||||
str := json.decode[json.Any]('"Hello!"')!
|
||||
assert str.str() == 'Hello!'
|
||||
}
|
||||
|
||||
fn test_raw_decode_string_escape() {
|
||||
jstr := json.decode[json.Any]('"\u001b"')!
|
||||
str := jstr.str()
|
||||
assert str.len == 1
|
||||
assert str[0] == 27
|
||||
}
|
||||
|
||||
fn test_raw_decode_number() {
|
||||
num := json.decode[json.Any]('123')!
|
||||
assert num.int() == 123
|
||||
}
|
||||
|
||||
fn test_raw_decode_array() {
|
||||
raw_arr := json.decode[json.Any]('["Foo", 1]')!
|
||||
arr := raw_arr.as_array()
|
||||
assert arr[0] or { 0 }.str() == 'Foo'
|
||||
assert arr[1] or { 0 }.int() == 1
|
||||
}
|
||||
|
||||
fn test_raw_decode_bool() {
|
||||
bol := json.decode[json.Any]('false')!
|
||||
assert bol.bool() == false
|
||||
}
|
||||
|
||||
fn test_raw_decode_map() {
|
||||
raw_mp := json.decode[json.Any]('{"name":"Bob","age":20}')!
|
||||
mp := raw_mp.as_map()
|
||||
assert mp['name'] or { 0 }.str() == 'Bob'
|
||||
assert mp['age'] or { 0 }.int() == 20
|
||||
}
|
||||
|
||||
fn test_raw_decode_string_with_dollarsign() {
|
||||
str := json.decode[json.Any](r'"Hello $world"')!
|
||||
assert str.str() == r'Hello $world'
|
||||
}
|
||||
|
||||
fn test_raw_decode_map_with_whitespaces() {
|
||||
raw_mp := json.decode[json.Any](' \n\t{"name":"Bob","age":20}\n\t')!
|
||||
mp := raw_mp.as_map()
|
||||
assert mp['name'] or { 0 }.str() == 'Bob'
|
||||
assert mp['age'] or { 0 }.int() == 20
|
||||
}
|
||||
|
||||
fn test_raw_decode_map_invalid() {
|
||||
json.decode[json.Any]('{"name","Bob","age":20}') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 8
|
||||
assert err.message == 'Syntax: expected `:`, got `,`'
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_raw_decode_array_invalid() {
|
||||
json.decode[json.Any]('["Foo", 1,}') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 11
|
||||
assert err.message == 'Syntax: unknown value kind'
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import x.json2 as json
|
||||
|
||||
fn test_utf8_strings_are_not_modified() {
|
||||
original := '{"s":"Schilddrüsenerkrankungen"}'
|
||||
deresult := json.decode[json.Any](original)!
|
||||
assert deresult.str() == original
|
||||
|
||||
assert json.encode('ü') == '"ü"'
|
||||
assert json.encode('Schilddrüsenerkrankungen') == '"Schilddrüsenerkrankungen"'
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import x.json2 as json
|
||||
|
||||
enum JobTitle {
|
||||
manager
|
||||
executive
|
||||
worker
|
||||
}
|
||||
|
||||
struct Employee {
|
||||
pub mut:
|
||||
name string
|
||||
age int
|
||||
salary f32
|
||||
title JobTitle
|
||||
}
|
||||
|
||||
fn test_fast_raw_decode() {
|
||||
s := '{"name":"Peter","age":28,"salary":95000.5,"title":2}'
|
||||
o := json.decode[json.Any](s) or {
|
||||
assert false
|
||||
json.Any('')
|
||||
}
|
||||
str := o.str()
|
||||
assert str == '{"name":"Peter","age":28,"salary":95000.5,"title":2}'
|
||||
}
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
}
|
||||
|
||||
fn test_struct_with_bool_to_map() {
|
||||
array_of_struct := [StructType[bool]{
|
||||
val: true
|
||||
}, StructType[bool]{
|
||||
val: false
|
||||
}]
|
||||
|
||||
mut array_of_map := []json.Any{}
|
||||
|
||||
for variable in array_of_struct {
|
||||
array_of_map << json.map_from(variable)
|
||||
}
|
||||
|
||||
assert array_of_map.str() == '[{"val":true},{"val":false}]'
|
||||
}
|
||||
|
||||
fn test_struct_with_string_to_map() {
|
||||
array_of_struct := [StructType[string]{
|
||||
val: 'true'
|
||||
}, StructType[string]{
|
||||
val: 'false'
|
||||
}]
|
||||
|
||||
mut array_of_map := []json.Any{}
|
||||
|
||||
for variable in array_of_struct {
|
||||
array_of_map << json.map_from(variable)
|
||||
}
|
||||
|
||||
assert array_of_map.str() == '[{"val":"true"},{"val":"false"}]'
|
||||
}
|
||||
|
||||
fn test_struct_with_array_to_map() {
|
||||
array_of_struct := [StructType[[]bool]{
|
||||
val: [false, true]
|
||||
}, StructType[[]bool]{
|
||||
val: [true, false]
|
||||
}]
|
||||
|
||||
mut array_of_map := []json.Any{}
|
||||
|
||||
for variable in array_of_struct {
|
||||
array_of_map << json.map_from(variable)
|
||||
}
|
||||
|
||||
assert array_of_map.str() == '[{"val":[false,true]},{"val":[true,false]}]'
|
||||
}
|
||||
|
||||
fn test_struct_with_array_of_arrays_to_map() {
|
||||
array_of_struct := [
|
||||
StructType[[][]bool]{
|
||||
val: [[true, false], [true, false]]
|
||||
},
|
||||
StructType[[][]bool]{
|
||||
val: [[false, true], [false, true]]
|
||||
},
|
||||
]
|
||||
mut array_of_map := []json.Any{}
|
||||
for variable in array_of_struct {
|
||||
array_of_map << json.map_from(variable)
|
||||
}
|
||||
assert array_of_map.str() == '[{"val":[[true,false],[true,false]]},{"val":[[false,true],[false,true]]}]'
|
||||
|
||||
array_of_struct_int := [
|
||||
StructType[[][]int]{
|
||||
val: [[1, 0], [1, 0]]
|
||||
},
|
||||
StructType[[][]int]{
|
||||
val: [[0, 1], [0, 1]]
|
||||
},
|
||||
]
|
||||
mut array_of_map_int := []json.Any{}
|
||||
for variable in array_of_struct_int {
|
||||
array_of_map_int << json.map_from(variable)
|
||||
}
|
||||
assert array_of_map_int.str() == '[{"val":[[1,0],[1,0]]},{"val":[[0,1],[0,1]]}]'
|
||||
}
|
||||
|
||||
fn test_struct_with_number_to_map() {
|
||||
assert json.map_from(StructType[string]{'3'}).str() == '{"val":"3"}'
|
||||
assert json.map_from(StructType[bool]{true}).str() == '{"val":true}'
|
||||
assert json.map_from(StructType[i8]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[i16]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[int]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[i64]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[i8]{-3}).str() == '{"val":-3}'
|
||||
assert json.map_from(StructType[i16]{i16(-3)}).str() == '{"val":-3}'
|
||||
assert json.map_from(StructType[int]{-3}).str() == '{"val":-3}'
|
||||
assert json.map_from(StructType[i64]{-3}).str() == '{"val":-3}'
|
||||
assert json.map_from(StructType[f32]{3.0}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[f64]{3.0}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[u8]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[u16]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[u32]{3}).str() == '{"val":3}'
|
||||
assert json.map_from(StructType[u64]{3}).str() == '{"val":3}'
|
||||
}
|
||||
|
||||
fn test_struct_with_struct_to_map() {
|
||||
assert json.map_from(StructType[StructType[string]]{StructType[string]{'3'}}).str() == '{"val":{"val":"3"}}'
|
||||
assert json.map_from(StructType[StructType[int]]{StructType[int]{3}}).str() == '{"val":{"val":3}}'
|
||||
}
|
||||
|
||||
fn test_maps() {
|
||||
assert json.decode[map[string]string]('{"test":"abc"}')! == {
|
||||
'test': 'abc'
|
||||
}
|
||||
|
||||
assert json.decode[map[string]StructType[bool]]('{"test":{"val":true}}')! == {
|
||||
'test': StructType[bool]{true}
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct Mount {
|
||||
size u64
|
||||
}
|
||||
|
||||
fn test_decode_u64() {
|
||||
data := '{"size": 10737418240}'
|
||||
m := json.decode[Mount](data)!
|
||||
assert m.size == 10737418240
|
||||
}
|
||||
|
||||
pub struct Comment {
|
||||
pub mut:
|
||||
id string
|
||||
comment string
|
||||
}
|
||||
|
||||
pub struct Task {
|
||||
mut:
|
||||
description string
|
||||
id int
|
||||
total_comments int
|
||||
file_name string @[skip]
|
||||
comments []Comment @[skip]
|
||||
}
|
||||
|
||||
fn test_skip_fields_should_be_initialised_by_json_decode() {
|
||||
data := '{"total_comments": 55, "id": 123}'
|
||||
mut task := json.decode[Task](data)!
|
||||
assert task.id == 123
|
||||
assert task.total_comments == 55
|
||||
assert task.comments == []
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
struct DbConfig {
|
||||
host string
|
||||
dbname string
|
||||
user string
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_empty() {
|
||||
json.decode[DbConfig]('') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'empty string'
|
||||
}
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_just_brace() {
|
||||
json.decode[DbConfig]('{') or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 1
|
||||
assert err.message == 'Syntax: EOF: expected object end'
|
||||
}
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_trailing_comma_at_end() {
|
||||
txt := '{
|
||||
"host": "localhost",
|
||||
"dbname": "alex",
|
||||
"user": "alex",
|
||||
}'
|
||||
|
||||
json.decode[DbConfig](txt) or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 5
|
||||
assert err.character == 1
|
||||
assert err.message == 'Syntax: Cannot use `,`, before `}`'
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_in_the_middle() {
|
||||
txt := '{"host": "localhost", "dbname": "alex" "user": "alex", "port": "1234"}'
|
||||
json.decode[DbConfig](txt) or {
|
||||
if err is json.JsonDecodeError {
|
||||
assert err.line == 1
|
||||
assert err.character == 40
|
||||
assert err.message == 'Syntax: invalid value. Unexpected character after string end'
|
||||
}
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
import x.json2 as json
|
||||
import time
|
||||
|
||||
enum JobTitle {
|
||||
manager
|
||||
executive
|
||||
worker
|
||||
}
|
||||
|
||||
struct Employee {
|
||||
pub mut:
|
||||
name string
|
||||
age int
|
||||
salary f32
|
||||
title JobTitle
|
||||
sub_employee SubEmployee //! FIXME - decode
|
||||
}
|
||||
|
||||
pub struct SubEmployee {
|
||||
pub mut:
|
||||
name string
|
||||
age int
|
||||
salary f32
|
||||
title JobTitle
|
||||
}
|
||||
|
||||
fn test_simple() {
|
||||
sub_employee := SubEmployee{
|
||||
name: 'João'
|
||||
}
|
||||
x := Employee{'Peter', 28, 95000.5, .worker, sub_employee}
|
||||
s := json.encode[Employee](x, enum_as_int: true)
|
||||
assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":2,"sub_employee":{"name":"João","age":0,"salary":0,"title":0}}'
|
||||
|
||||
y := json.decode[Employee](s) or {
|
||||
println(err)
|
||||
assert false
|
||||
return
|
||||
}
|
||||
assert y.name == 'Peter'
|
||||
assert y.age == 28
|
||||
assert y.salary == 95000.5
|
||||
assert y.title == .worker
|
||||
// assert y.sub_employee.name == 'João'
|
||||
assert y.sub_employee.age == 0
|
||||
assert y.sub_employee.salary == 0.0
|
||||
// assert y.sub_employee.title == .worker //! FIXME
|
||||
}
|
||||
|
||||
// const currency_id = 'cconst'
|
||||
|
||||
// struct Price {
|
||||
// net f64
|
||||
// currency_id string @[json: currencyId] = currency_id
|
||||
// }
|
||||
|
||||
struct User2 {
|
||||
mut:
|
||||
age int
|
||||
nums []int
|
||||
reg_date time.Time
|
||||
}
|
||||
|
||||
// User struct needs to be `pub mut` for now in order to access and manipulate values
|
||||
pub struct User {
|
||||
pub mut:
|
||||
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"}}'
|
||||
|
||||
u := json.decode[User](s)!
|
||||
|
||||
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(year: 2020, month: 12, day: 22, hour: 7, minute: 23)
|
||||
}
|
||||
s := json.encode(user)
|
||||
|
||||
assert s.contains('"reg_date":"2020-12-22T07:23:00.000Z"')
|
||||
}
|
||||
|
||||
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[User](usr)
|
||||
// println(out)
|
||||
assert out == expected
|
||||
// Test json.encode on mutable pointers
|
||||
assert usr.foo() == expected
|
||||
}
|
||||
|
||||
struct Color {
|
||||
pub mut:
|
||||
space string
|
||||
point string @[raw]
|
||||
}
|
||||
|
||||
fn test_raw_json_field() {
|
||||
color := json.decode[Color]('{"space": "YCbCr", "point": {"Y": 123}}') or {
|
||||
assert false
|
||||
Color{}
|
||||
}
|
||||
assert color.point == '{"Y": 123}'
|
||||
assert color.space == 'YCbCr'
|
||||
}
|
||||
|
||||
fn test_bad_raw_json_field() {
|
||||
color := json.decode[Color]('{"space": "YCbCr"}') or { return }
|
||||
assert color.point == ''
|
||||
assert color.space == 'YCbCr'
|
||||
}
|
||||
|
||||
fn test_encode_map() {
|
||||
expected := '{"one":1,"two":2,"three":3,"four":4}'
|
||||
numbers := {
|
||||
'one': json.Any(1)
|
||||
'two': json.Any(2)
|
||||
'three': json.Any(3)
|
||||
'four': json.Any(4)
|
||||
}
|
||||
// numbers := {
|
||||
// 'one': 1
|
||||
// 'two': 2
|
||||
// 'three': 3
|
||||
// 'four': 4
|
||||
// }
|
||||
assert json.encode(numbers) == expected
|
||||
assert numbers.str() == expected
|
||||
}
|
||||
|
||||
type ID = string
|
||||
type GG = int
|
||||
|
||||
struct Message {
|
||||
id ID
|
||||
ij GG
|
||||
}
|
||||
|
||||
fn test_encode_alias_struct() {
|
||||
expected := '{"id":"118499178790780929","ij":999998888}'
|
||||
msg := Message{'118499178790780929', 999998888}
|
||||
out := json.encode[Message](msg)
|
||||
assert out == expected
|
||||
}
|
||||
|
||||
struct StByteArray {
|
||||
ba []u8
|
||||
}
|
||||
|
||||
fn test_byte_array() {
|
||||
assert json.encode(StByteArray{ ba: [u8(1), 2, 3, 4, 5] }) == '{"ba":[1,2,3,4,5]}'
|
||||
}
|
||||
|
||||
struct Bar {
|
||||
x string
|
||||
}
|
||||
|
||||
fn bar[T](payload string) !Bar { // ?T doesn't work currently
|
||||
result := json.decode[T](payload)!
|
||||
return result
|
||||
}
|
||||
|
||||
fn test_generic() {
|
||||
result := bar[Bar]('{"x":"test"}') or { Bar{} }
|
||||
assert result.x == 'test'
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type StringAlias = string
|
||||
|
||||
// TODO: encode_pretty array, sum types, struct, alias of struct and others...
|
||||
struct Foo2 {
|
||||
ux8 u8
|
||||
ux16 u16
|
||||
ux32 u32
|
||||
ux64 u64
|
||||
sx8 i8
|
||||
sx16 i16
|
||||
sx32 int
|
||||
sx64 i64
|
||||
a bool
|
||||
b string
|
||||
c StringAlias
|
||||
}
|
||||
|
||||
fn test_pretty() {
|
||||
foo := Foo2{1, 2, 3, 4, -1, -2, -3, -4, true, 'abc', 'aliens'}
|
||||
assert json.encode(foo, prettify: true, indent_string: ' ') == '{
|
||||
"ux8": 1,
|
||||
"ux16": 2,
|
||||
"ux32": 3,
|
||||
"ux64": 4,
|
||||
"sx8": -1,
|
||||
"sx16": -2,
|
||||
"sx32": -3,
|
||||
"sx64": -4,
|
||||
"a": true,
|
||||
"b": "abc",
|
||||
"c": "aliens"
|
||||
}'
|
||||
}
|
||||
|
||||
struct Aa {
|
||||
sub AliasType
|
||||
}
|
||||
|
||||
struct Bb {
|
||||
a int
|
||||
}
|
||||
|
||||
type AliasType = Bb
|
||||
|
||||
fn test_encode_alias_field() {
|
||||
s := json.encode(Aa{
|
||||
sub: Bb{
|
||||
a: 1
|
||||
}
|
||||
})
|
||||
assert s == '{"sub":{"a":1}}'
|
||||
}
|
||||
|
||||
struct APrice {}
|
||||
|
||||
pub struct Association {
|
||||
association &Association = unsafe { nil }
|
||||
price APrice
|
||||
}
|
||||
|
||||
fn test_encoding_struct_with_pointers() {
|
||||
value := Association{
|
||||
association: &Association{
|
||||
price: APrice{}
|
||||
}
|
||||
price: APrice{}
|
||||
}
|
||||
// println(value)
|
||||
assert json.encode(value) == '{"association":{"price":{}},"price":{}}'
|
||||
}
|
||||
|
||||
pub struct City {
|
||||
mut:
|
||||
name string
|
||||
}
|
||||
|
||||
pub struct Country {
|
||||
mut:
|
||||
cities []City
|
||||
name string
|
||||
}
|
||||
|
||||
struct Data {
|
||||
mut:
|
||||
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)
|
||||
assert out == data_expected
|
||||
}
|
||||
|
||||
struct Human {
|
||||
name string
|
||||
}
|
||||
|
||||
struct Item {
|
||||
tag string
|
||||
}
|
||||
|
||||
enum Animal {
|
||||
dog // Will be encoded as `0`
|
||||
cat
|
||||
}
|
||||
|
||||
type Entity = Animal | Human | Item | string | time.Time
|
||||
|
||||
struct SomeGame {
|
||||
title string
|
||||
player Entity
|
||||
other []Entity
|
||||
}
|
||||
|
||||
fn test_encode_decode_sumtype() {
|
||||
t := time.now()
|
||||
game := SomeGame{
|
||||
title: 'Super Mega Game'
|
||||
player: Human{'Monke'}
|
||||
other: [
|
||||
Entity(Item{'Pen'}),
|
||||
Item{'Cookie'},
|
||||
Animal.cat,
|
||||
'Stool',
|
||||
t,
|
||||
]
|
||||
}
|
||||
|
||||
enc := json.encode(game, enum_as_int: true)
|
||||
|
||||
assert enc == '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},1,"Stool",{"_type":"${typeof(time.Time{}).name.all_after_last('.')}","value":${t.unix()}}]}'
|
||||
|
||||
dec := json.decode[SomeGame](enc)!
|
||||
|
||||
assert game.title == dec.title
|
||||
assert game.player == dec.player
|
||||
assert (game.other[2] as Animal) == .cat
|
||||
assert dec.other[2] == Entity(Animal.cat)
|
||||
assert (game.other[4] as time.Time).unix() == (dec.other[4] as time.Time).unix()
|
||||
}
|
||||
|
||||
struct Foo3 {
|
||||
name string
|
||||
age int @[omitempty]
|
||||
}
|
||||
|
||||
// fn test_omit_empty() {
|
||||
// foo := Foo3{'Bob', 0}
|
||||
// assert json.encode(foo) == '{"name":"Bob"}'
|
||||
// assert json.encode_pretty(foo) == '{
|
||||
// "name": "Bob"
|
||||
// }'
|
||||
// }
|
||||
|
||||
struct Foo31 {
|
||||
name string
|
||||
age ?int
|
||||
}
|
||||
|
||||
fn test_option_instead_of_omit_empty() {
|
||||
foo := Foo31{
|
||||
name: 'Bob'
|
||||
}
|
||||
assert json.encode(foo, prettify: true, indent_string: ' ') == '{
|
||||
"name": "Bob"
|
||||
}'
|
||||
}
|
||||
|
||||
struct Asdasd {
|
||||
data GamePacketData
|
||||
}
|
||||
|
||||
type GamePacketData = GPEquipItem | GPScale
|
||||
|
||||
struct GPScale {
|
||||
value f32
|
||||
}
|
||||
|
||||
struct GPEquipItem {
|
||||
name string
|
||||
}
|
||||
|
||||
fn create_game_packet(data &GamePacketData) string {
|
||||
return json.encode(data)
|
||||
}
|
||||
|
||||
fn test_encode_sumtype_defined_ahead() {
|
||||
ret := create_game_packet(&GamePacketData(GPScale{}))
|
||||
assert ret == '{"value":0,"_type":"GPScale"}'
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import x.json2 as 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)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
pub struct Comment {
|
||||
pub mut:
|
||||
id string
|
||||
comment string
|
||||
}
|
||||
|
||||
pub struct Task {
|
||||
mut:
|
||||
description string
|
||||
id int
|
||||
total_comments int
|
||||
file_name string @[skip]
|
||||
comments []Comment @[skip]
|
||||
}
|
||||
|
||||
fn test_skip_fields_should_be_initialised_by_json_decode() {
|
||||
data := '{"total_comments": 55, "id": 123}'
|
||||
mut task := json.decode[Task](data)!
|
||||
assert task.id == 123
|
||||
assert task.total_comments == 55
|
||||
assert task.comments == []
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
struct DbConfig {
|
||||
host string
|
||||
dbname string
|
||||
user string
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_empty() {
|
||||
json.decode[DbConfig]('') or {
|
||||
assert err.msg().len < 2
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_just_brace() {
|
||||
json.decode[DbConfig]('{') or {
|
||||
assert err.msg() == '{'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_trailing_comma_at_end() {
|
||||
txt := '{
|
||||
"host": "localhost",
|
||||
"dbname": "alex",
|
||||
"user": "alex",
|
||||
}'
|
||||
json.decode[DbConfig](txt) or {
|
||||
assert err.msg() == ' "user": "alex",\n}'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_decode_error_message_should_have_enough_context_in_the_middle() {
|
||||
txt := '{"host": "localhost", "dbname": "alex" "user": "alex", "port": "1234"}'
|
||||
json.decode[DbConfig](txt) or {
|
||||
assert err.msg() == 'ost", "dbname": "alex" "user":'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct TodoDto {
|
||||
foo int
|
||||
}
|
||||
|
||||
fn test_decode_with_encode_arg() {
|
||||
body := TodoDto{}
|
||||
ret := json.decode[TodoDto](json.encode(body))!
|
||||
println(ret)
|
||||
assert ret.foo == 0
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct Result[T] {
|
||||
ok bool
|
||||
result T
|
||||
}
|
||||
|
||||
struct User {
|
||||
id int
|
||||
username string
|
||||
}
|
||||
|
||||
fn func[T]() !T {
|
||||
text := '{"ok": true, "result":{"id":37467243, "username": "ciao"}}'
|
||||
a := json.decode[Result[T]](text)!
|
||||
return a.result
|
||||
}
|
||||
|
||||
fn test_decode_with_generic_struct() {
|
||||
ret := func[User]()!
|
||||
println(ret)
|
||||
assert ret.id == 37467243
|
||||
assert ret.username == 'ciao'
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import x.json2 as json
|
||||
import os
|
||||
|
||||
struct DbConfig {}
|
||||
|
||||
fn test_json_decode_with_option_arg() {
|
||||
if ret := print_info() {
|
||||
println(ret)
|
||||
} else {
|
||||
println(err)
|
||||
}
|
||||
assert true
|
||||
}
|
||||
|
||||
fn print_info() !string {
|
||||
dbconf := json.decode[DbConfig](os.read_file('dbconf.json')!)!
|
||||
println(dbconf)
|
||||
return '${dbconf}'
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import x.json2 as json
|
||||
|
||||
type Test = []bool | []int | []string | string
|
||||
|
||||
struct Some {
|
||||
t Test
|
||||
}
|
||||
|
||||
fn test_json_decode_with_sumtype() {
|
||||
v1 := json.decode[Some]('{"t": ["string", "string2"]}')!
|
||||
println(v1)
|
||||
assert v1.t == Test(['string', 'string2'])
|
||||
|
||||
v2 := json.decode[Some]('{"t": [11, 22]}')!
|
||||
println(v2)
|
||||
assert v2.t == Test([11, 22])
|
||||
|
||||
v3 := json.decode[Some]('{"t": [true, false]}')!
|
||||
println(v3)
|
||||
assert v3.t == Test([true, false])
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
import x.json2 as json
|
||||
import time
|
||||
|
||||
enum JobTitle {
|
||||
manager
|
||||
executive
|
||||
worker
|
||||
}
|
||||
|
||||
struct Employee {
|
||||
pub mut:
|
||||
name string
|
||||
age int
|
||||
salary f32
|
||||
title JobTitle
|
||||
sub_employee SubEmployee //! FIXME - decode
|
||||
}
|
||||
|
||||
pub struct SubEmployee {
|
||||
pub mut:
|
||||
name string
|
||||
age int
|
||||
salary f32
|
||||
title JobTitle
|
||||
}
|
||||
|
||||
fn test_simple() {
|
||||
sub_employee := SubEmployee{
|
||||
name: 'João'
|
||||
}
|
||||
x := Employee{'Peter', 28, 95000.5, .worker, sub_employee}
|
||||
s := json.encode[Employee](x, enum_as_int: true)
|
||||
assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":2,"sub_employee":{"name":"João","age":0,"salary":0,"title":0}}'
|
||||
|
||||
y := json.decode[Employee](s) or {
|
||||
println(err)
|
||||
assert false
|
||||
return
|
||||
}
|
||||
assert y.name == 'Peter'
|
||||
assert y.age == 28
|
||||
assert y.salary == 95000.5
|
||||
assert y.title == .worker
|
||||
// assert y.sub_employee.name == 'João'
|
||||
assert y.sub_employee.age == 0
|
||||
assert y.sub_employee.salary == 0.0
|
||||
// assert y.sub_employee.title == .worker //! FIXME
|
||||
}
|
||||
|
||||
// const currency_id = 'cconst'
|
||||
|
||||
// struct Price {
|
||||
// net f64
|
||||
// currency_id string @[json: currencyId] = currency_id
|
||||
// }
|
||||
|
||||
struct User2 {
|
||||
mut:
|
||||
age int
|
||||
nums []int
|
||||
reg_date time.Time
|
||||
}
|
||||
|
||||
// User struct needs to be `pub mut` for now in order to access and manipulate values
|
||||
pub struct User {
|
||||
pub mut:
|
||||
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"}}'
|
||||
|
||||
u := json.decode[User](s)!
|
||||
|
||||
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(year: 2020, month: 12, day: 22, hour: 7, minute: 23)
|
||||
}
|
||||
s := json.encode(user)
|
||||
assert s == '{"age":25,"nums":[],"reg_date":"2020-12-22T07:23:00.000Z"}'
|
||||
|
||||
assert s.contains('"reg_date":"2020-12-22T07:23:00.000Z"')
|
||||
user2 := json.decode[User2](s)!
|
||||
assert user2.reg_date.str() == '2020-12-22 07:23:00'
|
||||
}
|
||||
|
||||
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[User](usr)
|
||||
// println(out)
|
||||
assert out == expected
|
||||
// Test json.encode on mutable pointers
|
||||
assert usr.foo() == expected
|
||||
}
|
||||
|
||||
struct Color {
|
||||
pub mut:
|
||||
space string
|
||||
point string @[raw]
|
||||
}
|
||||
|
||||
fn test_raw_json_field() {
|
||||
color := json.decode[Color]('{"space": "YCbCr", "point": {"Y": 123}}') or {
|
||||
assert false
|
||||
Color{}
|
||||
}
|
||||
assert color.point == '{"Y": 123}'
|
||||
assert color.space == 'YCbCr'
|
||||
}
|
||||
|
||||
fn test_bad_raw_json_field() {
|
||||
color := json.decode[Color]('{"space": "YCbCr"}') or { return }
|
||||
assert color.point == ''
|
||||
assert color.space == 'YCbCr'
|
||||
}
|
||||
|
||||
fn test_encode_map() {
|
||||
expected := '{"one":1,"two":2,"three":3,"four":4}'
|
||||
numbers := {
|
||||
'one': json.Any(1)
|
||||
'two': json.Any(2)
|
||||
'three': json.Any(3)
|
||||
'four': json.Any(4)
|
||||
}
|
||||
// numbers := {
|
||||
// 'one': 1
|
||||
// 'two': 2
|
||||
// 'three': 3
|
||||
// 'four': 4
|
||||
// }
|
||||
assert json.encode(numbers) == expected
|
||||
assert numbers.str() == expected
|
||||
}
|
||||
|
||||
type ID = string
|
||||
type GG = int
|
||||
|
||||
struct Message {
|
||||
id ID
|
||||
ij GG
|
||||
}
|
||||
|
||||
fn test_encode_alias_struct() {
|
||||
expected := '{"id":"118499178790780929","ij":999998888}'
|
||||
msg := Message{'118499178790780929', 999998888}
|
||||
out := json.encode[Message](msg)
|
||||
assert out == expected
|
||||
}
|
||||
|
||||
struct StByteArray {
|
||||
ba []u8
|
||||
}
|
||||
|
||||
fn test_byte_array() {
|
||||
assert json.encode(StByteArray{ ba: [u8(1), 2, 3, 4, 5] }) == '{"ba":[1,2,3,4,5]}'
|
||||
}
|
||||
|
||||
struct Bar {
|
||||
x string
|
||||
}
|
||||
|
||||
fn bar[T](payload string) !Bar { // ?T doesn't work currently
|
||||
result := json.decode[T](payload)!
|
||||
return result
|
||||
}
|
||||
|
||||
fn test_generic() {
|
||||
result := bar[Bar]('{"x":"test"}') or { Bar{} }
|
||||
assert result.x == 'test'
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type StringAlias = string
|
||||
|
||||
// TODO: encode_pretty array, sum types, struct, alias of struct and others...
|
||||
struct Foo2 {
|
||||
ux8 u8
|
||||
ux16 u16
|
||||
ux32 u32
|
||||
ux64 u64
|
||||
sx8 i8
|
||||
sx16 i16
|
||||
sx32 int
|
||||
sx64 i64
|
||||
a bool
|
||||
b string
|
||||
c StringAlias
|
||||
}
|
||||
|
||||
fn test_pretty() {
|
||||
foo := Foo2{1, 2, 3, 4, -1, -2, -3, -4, true, 'abc', 'aliens'}
|
||||
assert json.encode(foo, prettify: true, indent_string: ' ') == '{
|
||||
"ux8": 1,
|
||||
"ux16": 2,
|
||||
"ux32": 3,
|
||||
"ux64": 4,
|
||||
"sx8": -1,
|
||||
"sx16": -2,
|
||||
"sx32": -3,
|
||||
"sx64": -4,
|
||||
"a": true,
|
||||
"b": "abc",
|
||||
"c": "aliens"
|
||||
}'
|
||||
}
|
||||
|
||||
struct Aa {
|
||||
sub AliasType
|
||||
}
|
||||
|
||||
struct Bb {
|
||||
a int
|
||||
}
|
||||
|
||||
type AliasType = Bb
|
||||
|
||||
fn test_encode_alias_field() {
|
||||
s := json.encode(Aa{
|
||||
sub: Bb{
|
||||
a: 1
|
||||
}
|
||||
})
|
||||
assert s == '{"sub":{"a":1}}'
|
||||
}
|
||||
|
||||
struct APrice {}
|
||||
|
||||
pub struct Association {
|
||||
association &Association = unsafe { nil }
|
||||
price APrice
|
||||
}
|
||||
|
||||
fn test_encoding_struct_with_pointers() {
|
||||
value := Association{
|
||||
association: &Association{
|
||||
price: APrice{}
|
||||
}
|
||||
price: APrice{}
|
||||
}
|
||||
// println(value)
|
||||
assert json.encode(value) == '{"association":{"price":{}},"price":{}}'
|
||||
}
|
||||
|
||||
pub struct City {
|
||||
mut:
|
||||
name string
|
||||
}
|
||||
|
||||
pub struct Country {
|
||||
mut:
|
||||
cities []City
|
||||
name string
|
||||
}
|
||||
|
||||
struct Data {
|
||||
mut:
|
||||
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)
|
||||
assert out == data_expected
|
||||
}
|
||||
|
||||
struct Human {
|
||||
name string
|
||||
}
|
||||
|
||||
struct Item {
|
||||
tag string
|
||||
}
|
||||
|
||||
enum Animal {
|
||||
dog // Will be encoded as `0`
|
||||
cat
|
||||
}
|
||||
|
||||
type Entity = Animal | Human | Item | string | time.Time
|
||||
|
||||
struct SomeGame {
|
||||
title string
|
||||
player Entity
|
||||
other []Entity
|
||||
}
|
||||
|
||||
fn test_encode_decode_sumtype() {
|
||||
t := time.now()
|
||||
game := SomeGame{
|
||||
title: 'Super Mega Game'
|
||||
player: Human{'Monke'}
|
||||
other: [
|
||||
Entity(Item{'Pen'}),
|
||||
Item{'Cookie'},
|
||||
Animal.cat,
|
||||
'Stool',
|
||||
t,
|
||||
]
|
||||
}
|
||||
|
||||
enc := json.encode(game, enum_as_int: true)
|
||||
|
||||
assert enc == '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},1,"Stool",{"_type":"${typeof(time.Time{}).name.all_after_last('.')}","value":${t.unix()}}]}'
|
||||
|
||||
dec := json.decode[SomeGame](enc)!
|
||||
|
||||
assert game.title == dec.title
|
||||
assert game.player == dec.player
|
||||
assert (game.other[2] as Animal) == .cat
|
||||
assert dec.other[2] == Entity(Animal.cat)
|
||||
assert (game.other[4] as time.Time).unix() == (dec.other[4] as time.Time).unix()
|
||||
}
|
||||
|
||||
struct Foo3 {
|
||||
name string
|
||||
age int @[omitempty]
|
||||
}
|
||||
|
||||
// fn test_omit_empty() {
|
||||
// foo := Foo3{'Bob', 0}
|
||||
// assert json.encode(foo) == '{"name":"Bob"}'
|
||||
// assert json.encode_pretty(foo) == '{
|
||||
// "name": "Bob"
|
||||
// }'
|
||||
// }
|
||||
|
||||
struct Foo31 {
|
||||
name string
|
||||
age ?int
|
||||
}
|
||||
|
||||
fn test_option_instead_of_omit_empty() {
|
||||
foo := Foo31{
|
||||
name: 'Bob'
|
||||
}
|
||||
assert json.encode(foo, prettify: true, indent_string: ' ') == '{
|
||||
"name": "Bob"
|
||||
}'
|
||||
}
|
||||
|
||||
struct Asdasd {
|
||||
data GamePacketData
|
||||
}
|
||||
|
||||
type GamePacketData = GPEquipItem | GPScale
|
||||
|
||||
struct GPScale {
|
||||
value f32
|
||||
}
|
||||
|
||||
struct GPEquipItem {
|
||||
name string
|
||||
}
|
||||
|
||||
fn create_game_packet(data &GamePacketData) string {
|
||||
return json.encode(data)
|
||||
}
|
||||
|
||||
fn test_encode_sumtype_defined_ahead() {
|
||||
ret := create_game_packet(&GamePacketData(GPScale{}))
|
||||
assert ret == '{"value":0,"_type":"GPScale"}'
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
import x.json2 as json
|
||||
import time
|
||||
|
||||
enum JobTitle {
|
||||
manager
|
||||
executive
|
||||
worker
|
||||
}
|
||||
|
||||
pub struct Employee {
|
||||
pub mut:
|
||||
name string
|
||||
age int
|
||||
salary f32
|
||||
title JobTitle
|
||||
}
|
||||
|
||||
const currency_id = 'cconst'
|
||||
|
||||
pub struct Price {
|
||||
net f64
|
||||
currency_id string = currency_id @[json: currencyId]
|
||||
}
|
||||
|
||||
//! BUGFIX
|
||||
fn test_field_with_default_expr() {
|
||||
data := '[{"net":1},{"net":2,"currencyId":"cjson"}]'
|
||||
prices := json.decode[[]Price](data)!
|
||||
assert prices == [Price{
|
||||
net: 1
|
||||
currency_id: 'cconst'
|
||||
}, Price{
|
||||
net: 2
|
||||
currency_id: 'cjson'
|
||||
}]
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
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
|
||||
}
|
||||
|
||||
struct Human {
|
||||
name string
|
||||
}
|
||||
|
||||
struct Item {
|
||||
tag string
|
||||
}
|
||||
|
||||
enum Animal {
|
||||
dog
|
||||
cat
|
||||
}
|
||||
|
||||
type Entity = Animal | Human | Item | string | time.Time
|
||||
|
||||
struct SomeGame {
|
||||
title string
|
||||
player Entity
|
||||
other []Entity
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
fn test_encode_decode_sumtype() {
|
||||
enc := '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},1,"Stool",{"_type":"Time","value":${t.unix()}}]}'
|
||||
|
||||
dec := json.decode[SomeGame](enc)!
|
||||
|
||||
assert game.title == dec.title
|
||||
assert game.player == dec.player
|
||||
assert (game.other[2] as Animal) == (dec.other[2] as Animal)
|
||||
assert (game.other[4] as time.Time).unix() == (dec.other[4] as time.Time).unix()
|
||||
}
|
||||
|
||||
struct User2 {
|
||||
mut:
|
||||
age int
|
||||
nums []int
|
||||
reg_date time.Time
|
||||
}
|
||||
|
||||
// User struct needs to be `pub mut` for now in order to access and manipulate values
|
||||
struct User {
|
||||
pub mut:
|
||||
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)!
|
||||
u := json.decode[User](s)!
|
||||
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"}'
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
fn test_encode_decode_time() {
|
||||
user := User2{
|
||||
age: 25
|
||||
reg_date: time.new(year: 2020, month: 12, day: 22, hour: 7, minute: 23)
|
||||
}
|
||||
s := json.encode(user)
|
||||
assert s.contains('"reg_date":1608621780')
|
||||
user2 := json.decode[User2](s)!
|
||||
assert user2.reg_date.str() == '2020-12-22 07:23:00'
|
||||
}
|
||||
|
||||
struct City {
|
||||
name string
|
||||
}
|
||||
|
||||
struct Country {
|
||||
cities []City
|
||||
name string
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
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'
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
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}')!
|
||||
assert out == expected
|
||||
}
|
||||
|
||||
struct Data {
|
||||
countries []Country
|
||||
users map[string]User
|
||||
extra map[string]map[string]int
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
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 {
|
||||
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 {
|
||||
assert err.msg().starts_with('Json element is not an object:')
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
invalid_array()
|
||||
invalid_object()
|
||||
}
|
||||
|
||||
type ID = string
|
||||
type GG = int
|
||||
|
||||
struct Message {
|
||||
id ID
|
||||
ij GG
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
fn test_decode_alias_struct() {
|
||||
msg := json.decode[Message]('{"id": "118499178790780929"}')!
|
||||
// hacky way of comparing aliased strings
|
||||
assert msg.id.str() == '118499178790780929'
|
||||
}
|
||||
|
||||
struct List {
|
||||
id int
|
||||
items []string
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
fn test_list() {
|
||||
list := json.decode[List]('{"id": 1, "items": ["1", "2"]}')!
|
||||
assert list.id == 1
|
||||
assert list.items == ['1', '2']
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
fn test_list_no_id() {
|
||||
list := json.decode[List]('{"items": ["1", "2"]}')!
|
||||
assert list.id == 0
|
||||
assert list.items == ['1', '2']
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
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
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
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}' == '{}'
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
fn test_decode_missing_maps_field() {
|
||||
info := json.decode[Info]('{"id": 22, "items": null}')!
|
||||
assert info.id == 22
|
||||
assert '${info.items}' == '[]'
|
||||
assert '${info.maps}' == '{}'
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
struct Response[T] {
|
||||
code int
|
||||
msg string
|
||||
data ?T
|
||||
}
|
||||
|
||||
struct UserInfo {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
fn g[T](x T) string {
|
||||
return 'abc'
|
||||
}
|
||||
|
||||
fn response_to_string[T](response Response[T]) string {
|
||||
return g(response)
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
res := response_to_string(Response{1, 'success', ?UserInfo{'Jay Chou', 46}})
|
||||
assert res == 'abc'
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
import x.json2 as json
|
||||
|
||||
type Prices = Price | []Price
|
||||
|
||||
pub struct ShopResponseData {
|
||||
attributes Attributes
|
||||
}
|
||||
|
||||
struct Attributes {
|
||||
price ?Prices
|
||||
}
|
||||
|
||||
struct Price {
|
||||
net f64
|
||||
}
|
||||
|
||||
pub type Animal = Cat | Dog
|
||||
|
||||
pub struct Cat {
|
||||
cat_name string
|
||||
}
|
||||
|
||||
pub struct Dog {
|
||||
dog_name string
|
||||
}
|
||||
|
||||
type Sum = int | string | bool | []string
|
||||
|
||||
type StructSumTypes = Stru | Stru2
|
||||
|
||||
type Mixed = Cat | int | map[string]int
|
||||
|
||||
type Maybes = ?int | ?string
|
||||
|
||||
type MultiArray = []int
|
||||
| []bool
|
||||
| [][]string
|
||||
| []map[string]map[string][]int
|
||||
| map[string]json.Any
|
||||
| string
|
||||
|
||||
type StructLists = Cat | []Cat | map[string]Dog
|
||||
|
||||
type SumAlias = Sum
|
||||
|
||||
struct AliasWrap {
|
||||
val SumAlias
|
||||
}
|
||||
|
||||
struct Empty1 {
|
||||
a ?string @[omitempty]
|
||||
}
|
||||
|
||||
type Elem1 = int | Empty1
|
||||
|
||||
pub struct Stru {
|
||||
val int
|
||||
val2 string
|
||||
val3 Stru2
|
||||
val4 int
|
||||
val5 int
|
||||
}
|
||||
|
||||
pub struct Stru2 {
|
||||
a int
|
||||
steak string
|
||||
}
|
||||
|
||||
type NewAny = int | string | bool | []NewAny | map[string]NewAny | ?int
|
||||
|
||||
fn test_simple_sum_type() {
|
||||
assert json.decode[Sum]('1')! == Sum(1)
|
||||
|
||||
assert json.decode[Sum]('"hello"')! == Sum('hello')
|
||||
|
||||
assert json.decode[Sum]('true')! == Sum(true)
|
||||
assert json.decode[Sum]('false')! == Sum(false)
|
||||
|
||||
assert json.decode[Sum]('["1", "2", "3"]')! == Sum(['1', '2', '3'])
|
||||
}
|
||||
|
||||
fn test_any_sum_type() {
|
||||
assert json.decode[json.Any]('1')! == json.Any(f64(1))
|
||||
assert json.decode[json.Any]('123321')! == json.Any(f64(123321))
|
||||
|
||||
assert json.decode[json.Any]('"hello"')! == json.Any('hello')
|
||||
|
||||
assert json.decode[json.Any]('true')! == json.Any(true)
|
||||
assert json.decode[json.Any]('false')! == json.Any(false)
|
||||
|
||||
assert json.decode[json.Any]('1.1')! == json.Any(f64(1.1))
|
||||
|
||||
assert json.decode[[]json.Any]('["1", "2", "3"]')! == [json.Any('1'), json.Any('2'), json.Any('3')]
|
||||
assert json.decode[json.Any]('["1", "2", "3"]')! == json.Any([json.Any('1'), json.Any('2'),
|
||||
json.Any('3')])
|
||||
|
||||
assert json.decode[[]json.Any]('[true, false, true]')! == [json.Any(true), json.Any(false),
|
||||
json.Any(true)]
|
||||
assert json.decode[json.Any]('[true, false, true]')! == json.Any([json.Any(true), json.Any(false),
|
||||
json.Any(true)])
|
||||
|
||||
assert json.decode[json.Any]('{"hello": "world"}')! == json.Any({
|
||||
'hello': json.Any('world')
|
||||
})
|
||||
|
||||
assert json.decode[map[string]json.Any]('{"hello": "world"}')! == {
|
||||
'hello': json.Any('world')
|
||||
}
|
||||
|
||||
assert json.decode[json.Any]('{"hello1": {"hello2": "world"}}')! == json.Any({
|
||||
'hello1': json.Any({
|
||||
'hello2': json.Any('world')
|
||||
})
|
||||
})
|
||||
|
||||
assert json.decode[NewAny]('{"name": null, "value": "hi"}')! == NewAny({
|
||||
'name': NewAny(?int(none))
|
||||
'value': NewAny('hi')
|
||||
})
|
||||
|
||||
assert json.decode[json.Any]('[]')! == json.Any([]json.Any{})
|
||||
assert json.decode[json.Any]('{}')! == json.Any(map[string]json.Any{})
|
||||
}
|
||||
|
||||
fn test_sum_type_struct() {
|
||||
if x := json.decode[Animal]('{"cat_name": "Tom"}') {
|
||||
assert false
|
||||
}
|
||||
if x := json.decode[Animal]('{"dog_name": "Rex"}') {
|
||||
assert false
|
||||
}
|
||||
assert json.decode[Animal]('{"dog_name": "Rex", "_type": "Dog"}')! == Animal(Dog{'Rex'})
|
||||
|
||||
// struct sumtype in random order
|
||||
assert json.decode[StructSumTypes]('{"_type": "Stru", "val": 1, "val2": "lala", "val3": {"a": 2, "steak": "leleu"}, "val4": 2147483000, "val5": 2147483000}')! == StructSumTypes(Stru{1, 'lala', Stru2{2, 'leleu'}, 2147483000, 2147483000})
|
||||
}
|
||||
|
||||
fn test_sum_type_array_with_single_struct_variant() {
|
||||
assert json.decode[Elem1]('{"a":"b"}')! == Elem1(Empty1{
|
||||
a: 'b'
|
||||
})
|
||||
|
||||
assert json.decode[[]Elem1]('[0, {"a":"b"}, 2]')! == [Elem1(0), Elem1(Empty1{
|
||||
a: 'b'
|
||||
}),
|
||||
Elem1(2)]
|
||||
}
|
||||
|
||||
fn test_sum_type_mixed() {
|
||||
assert json.decode[Mixed]('{"key":0}')! == Mixed({
|
||||
'key': 0
|
||||
})
|
||||
assert json.decode[Mixed]('10')! == Mixed(10)
|
||||
}
|
||||
|
||||
// to be implemented
|
||||
fn test_sum_type_options_fail() {
|
||||
assert json.decode[Maybes]('null')! == Maybes(?int(none))
|
||||
if x := json.decode[Maybes]('99') {
|
||||
assert false
|
||||
}
|
||||
if x := json.decode[Maybes]('hi') {
|
||||
assert false
|
||||
}
|
||||
if x := json.decode[Maybes]('true') {
|
||||
assert false
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sum_type_alias() {
|
||||
assert json.decode[SumAlias]('99')! == SumAlias(99)
|
||||
assert json.decode[SumAlias]('"hi"')! == SumAlias('hi')
|
||||
assert json.decode[SumAlias]('true')! == SumAlias(true)
|
||||
assert json.decode[SumAlias]('["hi", "bye"]')! == SumAlias(['hi', 'bye'])
|
||||
|
||||
assert json.decode[AliasWrap]('{"val": 99}')! == AliasWrap{
|
||||
val: SumAlias(99)
|
||||
}
|
||||
assert json.decode[[]SumAlias]('[99, "hi", true, ["bye"]]')! == [SumAlias(99), SumAlias('hi'),
|
||||
SumAlias(true), SumAlias(['bye'])]
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import x.json2 as json
|
||||
|
||||
type Prices = Price | []Price
|
||||
|
||||
pub struct ShopResponseData {
|
||||
attributes Attributes
|
||||
}
|
||||
|
||||
struct Attributes {
|
||||
price ?Prices
|
||||
}
|
||||
|
||||
struct Price {
|
||||
net f64
|
||||
}
|
||||
|
||||
type Animal = Cat | Dog
|
||||
|
||||
struct Cat {
|
||||
cat_name string
|
||||
}
|
||||
|
||||
struct Dog {
|
||||
dog_name string
|
||||
}
|
||||
|
||||
type Sum = int | string | bool
|
||||
|
||||
fn test_main() {
|
||||
data := '{"attributes": {"price": [{"net": 1, "_type": "Price"}, {"net": 2, "_type": "Price"}]}}'
|
||||
entity := json.decode[ShopResponseData](data) or { panic(err) }
|
||||
assert entity == ShopResponseData{
|
||||
attributes: Attributes{
|
||||
price: Prices([Price{
|
||||
net: 1
|
||||
}, Price{
|
||||
net: 2
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
data2 := '{"attributes": {"price": {"net": 1, "_type": "Price"}}}'
|
||||
entity2 := json.decode[ShopResponseData](data2) or { panic(err) }
|
||||
assert entity2 == ShopResponseData{
|
||||
attributes: Attributes{
|
||||
price: Prices(Price{
|
||||
net: 1
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
data3 := json.encode(ShopResponseData{
|
||||
attributes: Attributes{
|
||||
price: Prices([Price{
|
||||
net: 1.2
|
||||
}])
|
||||
}
|
||||
})
|
||||
assert data3 == '{"attributes":{"price":[{"net":1.2,"_type":"Price"}]}}'
|
||||
|
||||
entity3 := json.decode[ShopResponseData](data3) or { panic(err) }
|
||||
assert entity3 == ShopResponseData{
|
||||
attributes: Attributes{
|
||||
price: Prices([Price{
|
||||
net: 1.2
|
||||
}])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sum_types() {
|
||||
data1 := json.encode(Animal(Dog{
|
||||
dog_name: 'Caramelo'
|
||||
}))
|
||||
assert data1 == '{"dog_name":"Caramelo","_type":"Dog"}'
|
||||
|
||||
s := '{"_type":"Cat","cat_name":"Whiskers"}'
|
||||
animal := json.decode[Animal](s) or {
|
||||
println(err)
|
||||
assert false
|
||||
return
|
||||
}
|
||||
|
||||
assert animal is Cat
|
||||
if animal is Cat {
|
||||
assert animal.cat_name == 'Whiskers'
|
||||
} else {
|
||||
assert false, 'Wrong sumtype decode. In this case animal is a Cat'
|
||||
}
|
||||
|
||||
s2 := '[{"_type":"Cat","cat_name":"Whiskers"}, {"_type":"Dog","dog_name":"Goofie"}]'
|
||||
|
||||
animals := json.decode[[]Animal](s2) or {
|
||||
println(err)
|
||||
assert false
|
||||
return
|
||||
}
|
||||
|
||||
assert animals.len == 2
|
||||
assert animals[0] is Cat
|
||||
assert animals[1] is Dog
|
||||
cat := animals[0] as Cat
|
||||
dog := animals[1] as Dog
|
||||
assert cat.cat_name == 'Whiskers'
|
||||
assert dog.dog_name == 'Goofie'
|
||||
|
||||
j := json.encode(animals[0])
|
||||
assert j == '{"cat_name":"Whiskers","_type":"Cat"}'
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import x.json2 as json
|
||||
|
||||
struct Bar {
|
||||
b []int = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
Bar
|
||||
a []int = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
str := json.encode(Foo{})
|
||||
assert json.decode[Foo](str)!.str() == 'Foo{
|
||||
Bar: Bar{
|
||||
b: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
}
|
||||
a: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
}'
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import io
|
||||
import x.json2 as json
|
||||
|
||||
struct ChunkedReader {
|
||||
data []u8
|
||||
chunk_size int
|
||||
mut:
|
||||
pos int
|
||||
}
|
||||
|
||||
fn (mut r ChunkedReader) read(mut buf []u8) !int {
|
||||
if r.pos >= r.data.len {
|
||||
return io.Eof{}
|
||||
}
|
||||
mut n := r.chunk_size
|
||||
remaining := r.data.len - r.pos
|
||||
if n > remaining {
|
||||
n = remaining
|
||||
}
|
||||
if n > buf.len {
|
||||
n = buf.len
|
||||
}
|
||||
read := copy(mut buf[..n], r.data[r.pos..r.pos + n])
|
||||
r.pos += read
|
||||
return read
|
||||
}
|
||||
|
||||
fn test_public_scanner_iterates_tokens() {
|
||||
mut scanner := json.new_scanner('{"items":[1,true,null,"x"]}')
|
||||
mut got := []string{}
|
||||
for {
|
||||
token := scanner.next()!
|
||||
got << '${token.kind}:${token.literal()}'
|
||||
if token.is_eof() {
|
||||
break
|
||||
}
|
||||
}
|
||||
assert got == [
|
||||
'lcbr:',
|
||||
'str:items',
|
||||
'colon:',
|
||||
'lsbr:',
|
||||
'int:1',
|
||||
'comma:',
|
||||
'bool:true',
|
||||
'comma:',
|
||||
'null:null',
|
||||
'comma:',
|
||||
'str:x',
|
||||
'rsbr:',
|
||||
'rcbr:',
|
||||
'eof:',
|
||||
]
|
||||
}
|
||||
|
||||
fn test_reader_scanner_iterates_tokens() {
|
||||
mut reader := ChunkedReader{
|
||||
data: '[10,true,"chunked",null]'.bytes()
|
||||
chunk_size: 2
|
||||
}
|
||||
mut scanner := json.new_reader_scanner(reader: reader, buffer_size: 3)
|
||||
defer {
|
||||
scanner.free()
|
||||
}
|
||||
mut got := []string{}
|
||||
for {
|
||||
token := scanner.next()!
|
||||
got << '${token.kind}:${token.literal()}'
|
||||
if token.is_eof() {
|
||||
break
|
||||
}
|
||||
}
|
||||
assert got == [
|
||||
'lsbr:',
|
||||
'int:10',
|
||||
'comma:',
|
||||
'bool:true',
|
||||
'comma:',
|
||||
'str:chunked',
|
||||
'comma:',
|
||||
'null:null',
|
||||
'rsbr:',
|
||||
'eof:',
|
||||
]
|
||||
}
|
||||
|
||||
fn test_reader_scanner_reports_errors() {
|
||||
mut reader := ChunkedReader{
|
||||
data: r'["\z"]'.bytes()
|
||||
chunk_size: 1
|
||||
}
|
||||
mut scanner := json.new_reader_scanner(reader: reader, buffer_size: 1)
|
||||
defer {
|
||||
scanner.free()
|
||||
}
|
||||
assert scanner.next()!.kind == .lsbr
|
||||
scanner.next() or {
|
||||
assert err.msg().contains('invalid backslash escape')
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import x.json2
|
||||
|
||||
struct ScientificI64Value {
|
||||
size i64
|
||||
}
|
||||
|
||||
fn test_decode_i64_from_scientific_notation_number() {
|
||||
decoded := json2.decode[ScientificI64Value]('{"size":1.7596215426069998e+12}')!
|
||||
assert decoded.size == i64(1.7596215426069998e+12)
|
||||
}
|
||||
|
||||
fn test_decode_i64_from_scientific_notation_string() {
|
||||
decoded := json2.decode[ScientificI64Value]('{"size":"1.7596215426069998e+12"}')!
|
||||
assert decoded.size == i64(1.7596215426069998e+12)
|
||||
}
|
||||
|
||||
fn test_decode_i64_from_plain_fraction_still_fails() {
|
||||
if _ := json2.decode[ScientificI64Value]('{"size":123.45}') {
|
||||
assert false
|
||||
} else {
|
||||
assert true
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user