mirror of
https://github.com/vlang/v.git
synced 2025-09-11 08:25:42 -04:00
x.json2.decode2: minor improvements and bugfixes (#23083)
This commit is contained in:
parent
de3b184b01
commit
e32e9f70ce
@ -12,6 +12,8 @@ const false_in_string = 'false'
|
|||||||
|
|
||||||
const float_zero_in_string = '0.0'
|
const float_zero_in_string = '0.0'
|
||||||
|
|
||||||
|
const whitespace_chars = [` `, `\t`, `\n`]!
|
||||||
|
|
||||||
// Node represents a node in a linked list to store ValueInfo.
|
// Node represents a node in a linked list to store ValueInfo.
|
||||||
struct Node[T] {
|
struct Node[T] {
|
||||||
mut:
|
mut:
|
||||||
@ -147,55 +149,6 @@ pub enum ValueKind {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
// check_if_json_match checks if the JSON string matches the expected type T.
|
|
||||||
fn check_if_json_match[T](val string) ! {
|
|
||||||
// check if the JSON string is empty
|
|
||||||
if val == '' {
|
|
||||||
return error('empty string')
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if generic type matches the JSON type
|
|
||||||
value_kind := get_value_kind(val[0])
|
|
||||||
|
|
||||||
$if T is $option {
|
|
||||||
// TODO
|
|
||||||
} $else $if T is $sumtype {
|
|
||||||
// TODO
|
|
||||||
} $else $if T is $alias {
|
|
||||||
// TODO
|
|
||||||
} $else $if T is $string {
|
|
||||||
if value_kind != .string_ {
|
|
||||||
return error('Expected string, but got ${value_kind}')
|
|
||||||
}
|
|
||||||
} $else $if T is time.Time {
|
|
||||||
if value_kind != .string_ {
|
|
||||||
return error('Expected string, but got ${value_kind}')
|
|
||||||
}
|
|
||||||
} $else $if T is $map {
|
|
||||||
if value_kind != .object {
|
|
||||||
return error('Expected object, but got ${value_kind}')
|
|
||||||
}
|
|
||||||
} $else $if T is $array {
|
|
||||||
if value_kind != .array {
|
|
||||||
return error('Expected array, but got ${value_kind}')
|
|
||||||
}
|
|
||||||
} $else $if T is $struct {
|
|
||||||
if value_kind != .object {
|
|
||||||
return error('Expected object, but got ${value_kind}')
|
|
||||||
}
|
|
||||||
} $else $if T in [$enum, $int, $float] {
|
|
||||||
if value_kind != .number {
|
|
||||||
return error('Expected number, but got ${value_kind}')
|
|
||||||
}
|
|
||||||
} $else $if T is bool {
|
|
||||||
if value_kind != .boolean {
|
|
||||||
return error('Expected boolean, but got ${value_kind}')
|
|
||||||
}
|
|
||||||
} $else {
|
|
||||||
return error('cannot decode value with ${value_kind} type')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// error generates an error message with context from the JSON string.
|
// error generates an error message with context from the JSON string.
|
||||||
fn (mut checker Decoder) error(message string) ! {
|
fn (mut checker Decoder) error(message string) ! {
|
||||||
json := if checker.json.len < checker.checker_idx + 5 {
|
json := if checker.json.len < checker.checker_idx + 5 {
|
||||||
@ -234,6 +187,14 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
return checker.error('empty string')
|
return checker.error('empty string')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip whitespace
|
||||||
|
for val[checker.checker_idx] in whitespace_chars {
|
||||||
|
if checker.checker_idx >= checker_end - 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
|
||||||
// check if generic type matches the JSON type
|
// check if generic type matches the JSON type
|
||||||
value_kind := get_value_kind(val[checker.checker_idx])
|
value_kind := get_value_kind(val[checker.checker_idx])
|
||||||
start_idx_position := checker.checker_idx
|
start_idx_position := checker.checker_idx
|
||||||
@ -264,6 +225,9 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
checker.checker_idx += 3
|
checker.checker_idx += 3
|
||||||
}
|
}
|
||||||
.object {
|
.object {
|
||||||
|
if checker_end - checker.checker_idx < 2 {
|
||||||
|
return checker.error('EOF error: expecting a complete object after `{`')
|
||||||
|
}
|
||||||
checker.checker_idx++
|
checker.checker_idx++
|
||||||
for val[checker.checker_idx] != `}` {
|
for val[checker.checker_idx] != `}` {
|
||||||
// check if the JSON string is an empty object
|
// check if the JSON string is an empty object
|
||||||
@ -272,7 +236,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// skip whitespace
|
// skip whitespace
|
||||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
for val[checker.checker_idx] in whitespace_chars {
|
||||||
if checker.checker_idx >= checker_end - 1 {
|
if checker.checker_idx >= checker_end - 1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -294,7 +258,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
if checker.checker_idx >= checker_end - 1 {
|
if checker.checker_idx >= checker_end - 1 {
|
||||||
return checker.error('EOF error: key colon not found')
|
return checker.error('EOF error: key colon not found')
|
||||||
}
|
}
|
||||||
if val[checker.checker_idx] !in [` `, `\t`, `\n`] {
|
if val[checker.checker_idx] !in whitespace_chars {
|
||||||
return checker.error('invalid value after object key')
|
return checker.error('invalid value after object key')
|
||||||
}
|
}
|
||||||
checker.checker_idx++
|
checker.checker_idx++
|
||||||
@ -307,7 +271,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
checker.checker_idx++
|
checker.checker_idx++
|
||||||
|
|
||||||
// skip whitespace
|
// skip whitespace
|
||||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
for val[checker.checker_idx] in whitespace_chars {
|
||||||
checker.checker_idx++
|
checker.checker_idx++
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +279,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
`"`, `[`, `{`, `0`...`9`, `-`, `n`, `t`, `f` {
|
`"`, `[`, `{`, `0`...`9`, `-`, `n`, `t`, `f` {
|
||||||
checker.check_json_format(val)!
|
checker.check_json_format(val)!
|
||||||
// whitespace
|
// whitespace
|
||||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
for val[checker.checker_idx] in whitespace_chars {
|
||||||
checker.checker_idx++
|
checker.checker_idx++
|
||||||
}
|
}
|
||||||
if val[checker.checker_idx] == `}` {
|
if val[checker.checker_idx] == `}` {
|
||||||
@ -327,11 +291,11 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
|
|
||||||
if val[checker.checker_idx] == `,` {
|
if val[checker.checker_idx] == `,` {
|
||||||
checker.checker_idx++
|
checker.checker_idx++
|
||||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
for val[checker.checker_idx] in whitespace_chars {
|
||||||
checker.checker_idx++
|
checker.checker_idx++
|
||||||
}
|
}
|
||||||
if val[checker.checker_idx] != `"` {
|
if val[checker.checker_idx] != `"` {
|
||||||
return checker.error('Expecting object key')
|
return checker.error('Expecting object key after `,`')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if val[checker.checker_idx] == `}` {
|
if val[checker.checker_idx] == `}` {
|
||||||
@ -360,7 +324,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
|
|
||||||
for val[checker.checker_idx] != `]` {
|
for val[checker.checker_idx] != `]` {
|
||||||
// skip whitespace
|
// skip whitespace
|
||||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
for val[checker.checker_idx] in whitespace_chars {
|
||||||
if checker.checker_idx >= checker_end - 1 {
|
if checker.checker_idx >= checker_end - 1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -378,7 +342,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
checker.check_json_format(val)!
|
checker.check_json_format(val)!
|
||||||
|
|
||||||
// whitespace
|
// whitespace
|
||||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
for val[checker.checker_idx] in whitespace_chars {
|
||||||
checker.checker_idx++
|
checker.checker_idx++
|
||||||
}
|
}
|
||||||
if val[checker.checker_idx] == `]` {
|
if val[checker.checker_idx] == `]` {
|
||||||
@ -390,7 +354,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
|
|
||||||
if val[checker.checker_idx] == `,` {
|
if val[checker.checker_idx] == `,` {
|
||||||
checker.checker_idx++
|
checker.checker_idx++
|
||||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
for val[checker.checker_idx] in whitespace_chars {
|
||||||
checker.checker_idx++
|
checker.checker_idx++
|
||||||
}
|
}
|
||||||
if val[checker.checker_idx] == `]` {
|
if val[checker.checker_idx] == `]` {
|
||||||
@ -548,7 +512,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
|
|
||||||
for checker.checker_idx < checker_end - 1 && val[checker.checker_idx] !in [`,`, `:`, `}`, `]`] {
|
for checker.checker_idx < checker_end - 1 && val[checker.checker_idx] !in [`,`, `:`, `}`, `]`] {
|
||||||
// get trash characters after the value
|
// get trash characters after the value
|
||||||
if val[checker.checker_idx] !in [` `, `\t`, `\n`] {
|
if val[checker.checker_idx] !in whitespace_chars {
|
||||||
checker.error('invalid value. Unexpected character after ${value_kind} end')!
|
checker.error('invalid value. Unexpected character after ${value_kind} end')!
|
||||||
} else {
|
} else {
|
||||||
// whitespace
|
// whitespace
|
||||||
@ -558,17 +522,23 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// decode decodes a JSON string into a specified type.
|
// decode decodes a JSON string into a specified type.
|
||||||
|
@[manualfree]
|
||||||
pub fn decode[T](val string) !T {
|
pub fn decode[T](val string) !T {
|
||||||
|
if val == '' {
|
||||||
|
return error('empty string')
|
||||||
|
}
|
||||||
mut decoder := Decoder{
|
mut decoder := Decoder{
|
||||||
json: val
|
json: val
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder.check_json_format(val)!
|
decoder.check_json_format(val)!
|
||||||
check_if_json_match[T](val)!
|
|
||||||
|
|
||||||
mut result := T{}
|
mut result := T{}
|
||||||
decoder.current_node = decoder.values_info.head
|
decoder.current_node = decoder.values_info.head
|
||||||
decoder.decode_value(mut &result)!
|
decoder.decode_value(mut &result)!
|
||||||
|
unsafe {
|
||||||
|
decoder.values_info.free()
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,6 +599,8 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val = string_buffer.bytestr()
|
val = string_buffer.bytestr()
|
||||||
|
} else {
|
||||||
|
return error('Expected string, but got ${string_info.value_kind}')
|
||||||
}
|
}
|
||||||
} $else $if T.unaliased_typ is $sumtype {
|
} $else $if T.unaliased_typ is $sumtype {
|
||||||
decoder.decode_sumtype(mut val)!
|
decoder.decode_sumtype(mut val)!
|
||||||
@ -852,6 +824,8 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||||||
}
|
}
|
||||||
current_field_info = current_field_info.next
|
current_field_info = current_field_info.next
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return error('Expected object, but got ${struct_info.value_kind}')
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
struct_fields_info.free()
|
struct_fields_info.free()
|
||||||
@ -860,6 +834,10 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||||||
} $else $if T.unaliased_typ is bool {
|
} $else $if T.unaliased_typ is bool {
|
||||||
value_info := decoder.current_node.value
|
value_info := decoder.current_node.value
|
||||||
|
|
||||||
|
if value_info.value_kind != .boolean {
|
||||||
|
return error('Expected boolean, but got ${value_info.value_kind}')
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
val = vmemcmp(decoder.json.str + value_info.position, true_in_string.str,
|
val = vmemcmp(decoder.json.str + value_info.position, true_in_string.str,
|
||||||
true_in_string.len) == 0
|
true_in_string.len) == 0
|
||||||
@ -873,6 +851,8 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
|||||||
unsafe {
|
unsafe {
|
||||||
string_buffer_to_generic_number(val, bytes)
|
string_buffer_to_generic_number(val, bytes)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return error('Expected number, but got ${value_info.value_kind}')
|
||||||
}
|
}
|
||||||
} $else {
|
} $else {
|
||||||
return error('cannot decode value with ${typeof(val).name} type')
|
return error('cannot decode value with ${typeof(val).name} type')
|
||||||
@ -904,6 +884,8 @@ fn (mut decoder Decoder) decode_array[T](mut val []T) ! {
|
|||||||
|
|
||||||
val << array_element
|
val << array_element
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return error('Expected array, but got ${array_info.value_kind}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -946,6 +928,8 @@ fn (mut decoder Decoder) decode_map[K, V](mut val map[K]V) ! {
|
|||||||
}
|
}
|
||||||
decoder.decode_value(mut val[key])!
|
decoder.decode_value(mut val[key])!
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return error('Expected object, but got ${map_info.value_kind}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,8 @@ fn (mut decoder Decoder) get_decoded_sumtype_workaround[T](initialized_sumtype T
|
|||||||
$if initialized_sumtype is $sumtype {
|
$if initialized_sumtype is $sumtype {
|
||||||
$for v in initialized_sumtype.variants {
|
$for v in initialized_sumtype.variants {
|
||||||
if initialized_sumtype is v {
|
if initialized_sumtype is v {
|
||||||
$if v is $array {
|
// workaround for auto generated function considering sumtype as array
|
||||||
mut val := initialized_sumtype.clone()
|
unsafe {
|
||||||
decoder.decode_value(mut val)!
|
|
||||||
return T(val)
|
|
||||||
} $else {
|
|
||||||
mut val := initialized_sumtype
|
mut val := initialized_sumtype
|
||||||
decoder.decode_value(mut val)!
|
decoder.decode_value(mut val)!
|
||||||
return T(val)
|
return T(val)
|
||||||
|
@ -4,42 +4,42 @@ fn test_check_if_json_match() {
|
|||||||
// /* Test wrong string values */
|
// /* Test wrong string values */
|
||||||
mut has_error := false
|
mut has_error := false
|
||||||
|
|
||||||
check_if_json_match[string]('{"key": "value"}') or {
|
decode[string]('{"key": "value"}') or {
|
||||||
assert err.str() == 'Expected string, but got object'
|
assert err.str() == 'Expected string, but got object'
|
||||||
has_error = true
|
has_error = true
|
||||||
}
|
}
|
||||||
assert has_error, 'Expected error'
|
assert has_error, 'Expected error'
|
||||||
has_error = false
|
has_error = false
|
||||||
|
|
||||||
check_if_json_match[map[string]string]('"value"') or {
|
decode[map[string]string]('"value"') or {
|
||||||
assert err.str() == 'Expected object, but got string_'
|
assert err.str() == 'Expected object, but got string_'
|
||||||
has_error = true
|
has_error = true
|
||||||
}
|
}
|
||||||
assert has_error, 'Expected error'
|
assert has_error, 'Expected error'
|
||||||
has_error = false
|
has_error = false
|
||||||
|
|
||||||
check_if_json_match[[]int]('{"key": "value"}') or {
|
decode[[]int]('{"key": "value"}') or {
|
||||||
assert err.str() == 'Expected array, but got object'
|
assert err.str() == 'Expected array, but got object'
|
||||||
has_error = true
|
has_error = true
|
||||||
}
|
}
|
||||||
assert has_error, 'Expected error'
|
assert has_error, 'Expected error'
|
||||||
has_error = false
|
has_error = false
|
||||||
|
|
||||||
check_if_json_match[string]('[1, 2, 3]') or {
|
decode[string]('[1, 2, 3]') or {
|
||||||
assert err.str() == 'Expected string, but got array'
|
assert err.str() == 'Expected string, but got array'
|
||||||
has_error = true
|
has_error = true
|
||||||
}
|
}
|
||||||
assert has_error, 'Expected error'
|
assert has_error, 'Expected error'
|
||||||
has_error = false
|
has_error = false
|
||||||
|
|
||||||
check_if_json_match[int]('{"key": "value"}') or {
|
decode[int]('{"key": "value"}') or {
|
||||||
assert err.str() == 'Expected number, but got object'
|
assert err.str() == 'Expected number, but got object'
|
||||||
has_error = true
|
has_error = true
|
||||||
}
|
}
|
||||||
assert has_error, 'Expected error'
|
assert has_error, 'Expected error'
|
||||||
has_error = false
|
has_error = false
|
||||||
|
|
||||||
check_if_json_match[bool]('{"key": "value"}') or {
|
decode[bool]('{"key": "value"}') or {
|
||||||
assert err.str() == 'Expected boolean, but got object'
|
assert err.str() == 'Expected boolean, but got object'
|
||||||
has_error = true
|
has_error = true
|
||||||
}
|
}
|
||||||
@ -47,19 +47,19 @@ fn test_check_if_json_match() {
|
|||||||
has_error = false
|
has_error = false
|
||||||
|
|
||||||
// /* Right string values */
|
// /* Right string values */
|
||||||
check_if_json_match[string]('"value"') or { assert false }
|
decode[string]('"value"') or { assert false }
|
||||||
|
|
||||||
check_if_json_match[map[string]string]('{"key": "value"}') or { assert false }
|
decode[map[string]string]('{"key": "value"}') or { assert false }
|
||||||
|
|
||||||
check_if_json_match[[]int]('[1, 2, 3]') or { assert false }
|
decode[[]int]('[1, 2, 3]') or { assert false }
|
||||||
|
|
||||||
check_if_json_match[string]('"string"') or { assert false }
|
decode[string]('"string"') or { assert false }
|
||||||
|
|
||||||
check_if_json_match[int]('123') or { assert false }
|
decode[int]('123') or { assert false }
|
||||||
|
|
||||||
check_if_json_match[bool]('true') or { assert false }
|
decode[bool]('true') or { assert false }
|
||||||
|
|
||||||
check_if_json_match[bool]('false') or { assert false }
|
decode[bool]('false') or { assert false }
|
||||||
|
|
||||||
// TODO: test null
|
// TODO: test null
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ fn test_check_json_format() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'json': '{"key": 123, "key2": 456,}'
|
'json': '{"key": 123, "key2": 456,}'
|
||||||
'error': '\n{"key": 123, "key2": 456,}\n ^ Expecting object key'
|
'error': '\n{"key": 123, "key2": 456,}\n ^ Expecting object key after `,`'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'json': '[[1, 2, 3], [4, 5, 6],]'
|
'json': '[[1, 2, 3], [4, 5, 6],]'
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
import x.json2.decoder2 as json
|
||||||
|
import x.json2
|
||||||
|
|
||||||
|
struct AnyStruct[T] {
|
||||||
|
val T
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OptAnyStruct[T] {
|
||||||
|
val ?T
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct OptAnyArrStruct {
|
||||||
|
// val []?json2.Any
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn test_values() {
|
||||||
|
assert json.decode[AnyStruct[json2.Any]]('{"val":5}')!.val.int() == 5
|
||||||
|
assert json.decode[OptAnyStruct[json2.Any]]('{}')!.val == none
|
||||||
|
assert json.decode[AnyStruct[[]json2.Any]]('{"val":[5,10]}')!.val.map(it.int()) == [
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
]
|
||||||
|
// assert json.decode[OptAnyArrStruct]('{"val":[5,null,10]}')!.val == [?json2.Any(5),json.Null{},10] // skipped because test still fails even though they're the same
|
||||||
|
|
||||||
|
assert json2.encode[AnyStruct[json2.Any]](AnyStruct[json2.Any]{json2.Any(5)}) == '{"val":5}'
|
||||||
|
assert json2.encode[OptAnyStruct[json2.Any]](OptAnyStruct[json2.Any]{none}) == '{}'
|
||||||
|
assert json2.encode[AnyStruct[[]json2.Any]](AnyStruct[[]json2.Any]{[json2.Any(5), 10]}) == '{"val":[5,10]}'
|
||||||
|
// assert json2.encode[OptAnyArrStruct](OptAnyArrStruct{[?json2.Any(5),none,10]}) == '{"val":[5,null,10]}' // encode_array has not implemented optional arrays yet
|
||||||
|
}
|
58
vlib/x/json2/decoder2/tests/json2_tests/decode_map_test.v
Normal file
58
vlib/x/json2/decoder2/tests/json2_tests/decode_map_test.v
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import x.json2.decoder2 as 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
|
||||||
|
}
|
212
vlib/x/json2/decoder2/tests/json2_tests/decode_struct_test.v
Normal file
212
vlib/x/json2/decoder2/tests/json2_tests/decode_struct_test.v
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
import x.json2.decoder2 as json
|
||||||
|
import x.json2
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
74
vlib/x/json2/decoder2/tests/json2_tests/decoder_test.v
Normal file
74
vlib/x/json2/decoder2/tests/json2_tests/decoder_test.v
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import x.json2.decoder2 as json
|
||||||
|
import x.json2
|
||||||
|
|
||||||
|
fn test_raw_decode_string() {
|
||||||
|
str := json.decode[json2.Any]('"Hello!"')!
|
||||||
|
assert str.str() == 'Hello!'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_raw_decode_string_escape() {
|
||||||
|
jstr := json.decode[json2.Any]('"\u001b"')!
|
||||||
|
str := jstr.str()
|
||||||
|
assert str.len == 1
|
||||||
|
assert str[0] == 27
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_raw_decode_number() {
|
||||||
|
num := json.decode[json2.Any]('123')!
|
||||||
|
assert num.int() == 123
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_raw_decode_array() {
|
||||||
|
raw_arr := json.decode[json2.Any]('["Foo", 1]')!
|
||||||
|
arr := raw_arr.arr()
|
||||||
|
assert arr[0] or { 0 }.str() == 'Foo'
|
||||||
|
assert arr[1] or { 0 }.int() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_raw_decode_bool() {
|
||||||
|
bol := json.decode[json2.Any]('false')!
|
||||||
|
assert bol.bool() == false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_raw_decode_map() {
|
||||||
|
raw_mp := json.decode[json2.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[json2.Any](r'"Hello $world"')!
|
||||||
|
assert str.str() == r'Hello $world'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_raw_decode_map_with_whitespaces() {
|
||||||
|
raw_mp := json.decode[json2.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_nested_array_object() {
|
||||||
|
mut parser := json2.new_parser(r'[[[[[],[],[]]]],{"Test":{}},[[]]]', false)
|
||||||
|
decoded := parser.decode()!
|
||||||
|
assert parser.n_level == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_raw_decode_map_invalid() {
|
||||||
|
json.decode[json2.Any]('{"name","Bob","age":20}') or {
|
||||||
|
assert err.msg() == '\n{"name",\n ^ invalid value after object key'
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_raw_decode_array_invalid() {
|
||||||
|
json.decode[json2.Any]('["Foo", 1,}') or {
|
||||||
|
assert err.msg() == '\n["Foo", 1,}\n ^ EOF error: array not closed'
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert false
|
||||||
|
}
|
226
vlib/x/json2/decoder2/tests/json2_tests/encoder_test.v
Normal file
226
vlib/x/json2/decoder2/tests/json2_tests/encoder_test.v
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
import x.json2.decoder2 as json
|
||||||
|
import x.json2
|
||||||
|
import strings
|
||||||
|
import time
|
||||||
|
|
||||||
|
struct StructType[T] {
|
||||||
|
mut:
|
||||||
|
val T
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_json_string_characters() {
|
||||||
|
assert json2.encode([u8(`/`)].bytestr()).bytes() == r'"\/"'.bytes()
|
||||||
|
assert json2.encode([u8(`\\`)].bytestr()).bytes() == r'"\\"'.bytes()
|
||||||
|
assert json2.encode([u8(`"`)].bytestr()).bytes() == r'"\""'.bytes()
|
||||||
|
assert json2.encode([u8(`\n`)].bytestr()).bytes() == r'"\n"'.bytes()
|
||||||
|
assert json2.encode(r'\n\r') == r'"\\n\\r"'
|
||||||
|
assert json2.encode('\\n') == r'"\\n"'
|
||||||
|
assert json2.encode(r'\n\r\b') == r'"\\n\\r\\b"'
|
||||||
|
assert json2.encode(r'\"/').bytes() == r'"\\\"\/"'.bytes()
|
||||||
|
|
||||||
|
assert json2.encode(r'\n\r\b\f\t\\\"\/') == r'"\\n\\r\\b\\f\\t\\\\\\\"\\\/"'
|
||||||
|
|
||||||
|
assert json2.encode("fn main(){nprintln('Hello World! Helo \$a')\n}") == '"fn main(){nprintln(\'Hello World! Helo \$a\')\\n}"'
|
||||||
|
assert json2.encode(' And when "\'s are in the string, along with # "') == '" And when \\"\'s are in the string, along with # \\""'
|
||||||
|
assert json2.encode('a \\\nb') == r'"a \\\nb"'
|
||||||
|
assert json2.encode('Name\tJosé\nLocation\tSF.') == '"Name\\tJosé\\nLocation\\tSF."'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_json_escape_low_chars() {
|
||||||
|
esc := '\u001b'
|
||||||
|
assert esc.len == 1
|
||||||
|
text := json2.Any(esc)
|
||||||
|
assert text.json_str() == r'"\u001b"'
|
||||||
|
|
||||||
|
assert json2.encode('\u000f') == r'"\u000f"'
|
||||||
|
assert json2.encode('\u0020') == r'" "'
|
||||||
|
assert json2.encode('\u0000') == r'"\u0000"'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_json_string() {
|
||||||
|
text := json2.Any('te✔st')
|
||||||
|
|
||||||
|
assert text.json_str() == r'"te\u2714st"'
|
||||||
|
assert json2.encode('te✔st') == r'"te\u2714st"'
|
||||||
|
|
||||||
|
boolean := json2.Any(true)
|
||||||
|
assert boolean.json_str() == 'true'
|
||||||
|
integer := json2.Any(int(-5))
|
||||||
|
assert integer.json_str() == '-5'
|
||||||
|
u64integer := json2.Any(u64(5000))
|
||||||
|
assert u64integer.json_str() == '5000'
|
||||||
|
i64integer := json2.Any(i64(-17))
|
||||||
|
assert i64integer.json_str() == '-17'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_json_string_emoji() {
|
||||||
|
text := json2.Any('🐈')
|
||||||
|
assert text.json_str() == r'"🐈"'
|
||||||
|
assert json2.Any('💀').json_str() == r'"💀"'
|
||||||
|
|
||||||
|
assert json2.encode('🐈') == r'"🐈"'
|
||||||
|
assert json2.encode('💀') == r'"💀"'
|
||||||
|
assert json2.encode('🐈💀') == r'"🐈💀"'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_json_string_non_ascii() {
|
||||||
|
text := json2.Any('ひらがな')
|
||||||
|
assert text.json_str() == r'"\u3072\u3089\u304c\u306a"'
|
||||||
|
|
||||||
|
assert json2.encode('ひらがな') == r'"\u3072\u3089\u304c\u306a"'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_utf8_strings_are_not_modified() {
|
||||||
|
original := '{"s":"Schilddrüsenerkrankungen"}'
|
||||||
|
deresult := json.decode[json2.Any](original)!
|
||||||
|
assert deresult.str() == original
|
||||||
|
|
||||||
|
assert json2.encode('ü') == '"ü"'
|
||||||
|
assert json2.encode('Schilddrüsenerkrankungen') == '"Schilddrüsenerkrankungen"'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_encoder_unescaped_utf32() ! {
|
||||||
|
jap_text := json2.Any('ひらがな')
|
||||||
|
enc := json2.Encoder{
|
||||||
|
escape_unicode: false
|
||||||
|
}
|
||||||
|
|
||||||
|
mut sb := strings.new_builder(20)
|
||||||
|
defer {
|
||||||
|
unsafe { sb.free() }
|
||||||
|
}
|
||||||
|
|
||||||
|
enc.encode_value(jap_text, mut sb)!
|
||||||
|
|
||||||
|
assert sb.str() == '"${jap_text}"'
|
||||||
|
sb.go_back_to(0)
|
||||||
|
|
||||||
|
emoji_text := json2.Any('🐈')
|
||||||
|
enc.encode_value(emoji_text, mut sb)!
|
||||||
|
assert sb.str() == '"${emoji_text}"'
|
||||||
|
|
||||||
|
mut buf := []u8{cap: 14}
|
||||||
|
|
||||||
|
enc.encode_value('ひらがな', mut buf)!
|
||||||
|
|
||||||
|
assert buf.len == 14
|
||||||
|
assert buf.bytestr() == '"ひらがな"'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_encoder_prettify() {
|
||||||
|
obj := {
|
||||||
|
'hello': json2.Any('world')
|
||||||
|
'arr': [json2.Any('im a string'), [json2.Any('3rd level')]]
|
||||||
|
'obj': {
|
||||||
|
'map': json2.Any('map inside a map')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enc := json2.Encoder{
|
||||||
|
newline: `\n`
|
||||||
|
newline_spaces_count: 2
|
||||||
|
}
|
||||||
|
mut sb := strings.new_builder(20)
|
||||||
|
defer {
|
||||||
|
unsafe { sb.free() }
|
||||||
|
}
|
||||||
|
enc.encode_value(obj, mut sb)!
|
||||||
|
assert sb.str() == '{
|
||||||
|
"hello": "world",
|
||||||
|
"arr": [
|
||||||
|
"im a string",
|
||||||
|
[
|
||||||
|
"3rd level"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"obj": {
|
||||||
|
"map": "map inside a map"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Test {
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_encode_struct() {
|
||||||
|
enc := json2.encode(Test{'hello!'})
|
||||||
|
assert enc == '{"val":"hello!"}'
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Uri {
|
||||||
|
protocol string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (u Uri) json_str() string {
|
||||||
|
return '"${u.protocol}://${u.path}"'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_encode_encodable() {
|
||||||
|
assert json2.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 json2.encode([1, 2, 3]) == '[1,2,3]'
|
||||||
|
|
||||||
|
assert json2.encode(array_of_struct) == '[{"val":[false,true]},{"val":[true,false]}]'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_encode_simple() {
|
||||||
|
assert json2.encode('hello!') == '"hello!"'
|
||||||
|
assert json2.encode(1) == '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_encode_value() {
|
||||||
|
json_enc := json2.Encoder{
|
||||||
|
newline: `\n`
|
||||||
|
newline_spaces_count: 2
|
||||||
|
escape_unicode: false
|
||||||
|
}
|
||||||
|
|
||||||
|
mut manifest := map[string]json2.Any{}
|
||||||
|
|
||||||
|
manifest['server_path'] = json2.Any('new_path')
|
||||||
|
manifest['last_updated'] = json2.Any('timestamp.format_ss()')
|
||||||
|
manifest['from_source'] = json2.Any('from_source')
|
||||||
|
|
||||||
|
mut sb := strings.new_builder(64)
|
||||||
|
mut buffer := []u8{}
|
||||||
|
json_enc.encode_value(manifest, mut buffer)!
|
||||||
|
|
||||||
|
assert buffer.len > 0
|
||||||
|
assert buffer == [u8(123), 10, 32, 32, 34, 115, 101, 114, 118, 101, 114, 95, 112, 97, 116,
|
||||||
|
104, 34, 58, 32, 34, 110, 101, 119, 95, 112, 97, 116, 104, 34, 44, 10, 32, 32, 34, 108,
|
||||||
|
97, 115, 116, 95, 117, 112, 100, 97, 116, 101, 100, 34, 58, 32, 34, 116, 105, 109, 101,
|
||||||
|
115, 116, 97, 109, 112, 46, 102, 111, 114, 109, 97, 116, 95, 115, 115, 40, 41, 34, 44,
|
||||||
|
10, 32, 32, 34, 102, 114, 111, 109, 95, 115, 111, 117, 114, 99, 101, 34, 58, 32, 34, 102,
|
||||||
|
114, 111, 109, 95, 115, 111, 117, 114, 99, 101, 34, 10, 125]
|
||||||
|
|
||||||
|
sb.write(buffer)!
|
||||||
|
|
||||||
|
unsafe { buffer.free() }
|
||||||
|
|
||||||
|
assert sb.str() == r'{
|
||||||
|
"server_path": "new_path",
|
||||||
|
"last_updated": "timestamp.format_ss()",
|
||||||
|
"from_source": "from_source"
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_encode_time() {
|
||||||
|
assert json2.encode({
|
||||||
|
'bro': json2.Any(time.Time{})
|
||||||
|
}) == '{"bro":"0000-00-00T00:00:00.000Z"}'
|
||||||
|
|
||||||
|
assert json2.encode({
|
||||||
|
'bro': time.Time{}
|
||||||
|
}) == '{"bro":"0000-00-00T00:00:00.000Z"}'
|
||||||
|
|
||||||
|
assert json2.encode(time.Time{}) == '"0000-00-00T00:00:00.000Z"'
|
||||||
|
}
|
143
vlib/x/json2/decoder2/tests/json2_tests/json2_test.v
Normal file
143
vlib/x/json2/decoder2/tests/json2_tests/json2_test.v
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import x.json2.decoder2 as json
|
||||||
|
import x.json2
|
||||||
|
|
||||||
|
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 := json2.fast_raw_decode(s) or {
|
||||||
|
assert false
|
||||||
|
json2.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 := []json2.Any{}
|
||||||
|
|
||||||
|
for variable in array_of_struct {
|
||||||
|
array_of_map << json2.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 := []json2.Any{}
|
||||||
|
|
||||||
|
for variable in array_of_struct {
|
||||||
|
array_of_map << json2.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 := []json2.Any{}
|
||||||
|
|
||||||
|
for variable in array_of_struct {
|
||||||
|
array_of_map << json2.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 := []json2.Any{}
|
||||||
|
for variable in array_of_struct {
|
||||||
|
array_of_map << json2.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 := []json2.Any{}
|
||||||
|
for variable in array_of_struct_int {
|
||||||
|
array_of_map_int << json2.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 json2.map_from(StructType[string]{'3'}).str() == '{"val":"3"}'
|
||||||
|
assert json2.map_from(StructType[bool]{true}).str() == '{"val":true}'
|
||||||
|
assert json2.map_from(StructType[i8]{3}).str() == '{"val":3}'
|
||||||
|
assert json2.map_from(StructType[i16]{3}).str() == '{"val":3}'
|
||||||
|
assert json2.map_from(StructType[int]{3}).str() == '{"val":3}'
|
||||||
|
assert json2.map_from(StructType[i64]{3}).str() == '{"val":3}'
|
||||||
|
assert json2.map_from(StructType[i8]{-3}).str() == '{"val":-3}'
|
||||||
|
assert json2.map_from(StructType[i16]{i16(-3)}).str() == '{"val":-3}'
|
||||||
|
assert json2.map_from(StructType[int]{-3}).str() == '{"val":-3}'
|
||||||
|
assert json2.map_from(StructType[i64]{-3}).str() == '{"val":-3}'
|
||||||
|
assert json2.map_from(StructType[f32]{3.0}).str() == '{"val":3}'
|
||||||
|
assert json2.map_from(StructType[f64]{3.0}).str() == '{"val":3}'
|
||||||
|
assert json2.map_from(StructType[u8]{3}).str() == '{"val":3}'
|
||||||
|
assert json2.map_from(StructType[u16]{3}).str() == '{"val":3}'
|
||||||
|
assert json2.map_from(StructType[u32]{3}).str() == '{"val":3}'
|
||||||
|
assert json2.map_from(StructType[u64]{3}).str() == '{"val":3}'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_struct_with_struct_to_map() {
|
||||||
|
assert json2.map_from(StructType[StructType[string]]{StructType[string]{'3'}}).str() == '{"val":{"val":"3"}}'
|
||||||
|
assert json2.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}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
import x.json2.decoder2 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 {
|
||||||
|
assert err.msg() == 'empty string'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_decode_error_message_should_have_enough_context_just_brace() {
|
||||||
|
json.decode[DbConfig]('{') or {
|
||||||
|
assert err.msg() == '
|
||||||
|
{
|
||||||
|
^ EOF error: expecting a complete object after `{`'
|
||||||
|
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() == '\n\n}\n ^ Expecting object key after `,`'
|
||||||
|
|
||||||
|
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() == '\n{"host": "localhost", "dbname": "alex" "\n ^ invalid value. Unexpected character after string_ end'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert false
|
||||||
|
}
|
@ -0,0 +1,441 @@
|
|||||||
|
import x.json2.decoder2 as json
|
||||||
|
import x.json2
|
||||||
|
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 := json2.encode[Employee](x)
|
||||||
|
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 := json2.encode(user)
|
||||||
|
|
||||||
|
assert s.contains('"reg_date":"2020-12-22T07:23:00.000Z"')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut u User) foo() string {
|
||||||
|
return json2.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 := json2.encode[User](usr)
|
||||||
|
// println(out)
|
||||||
|
assert out == expected
|
||||||
|
// Test json2.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': json2.Any(1)
|
||||||
|
'two': json2.Any(2)
|
||||||
|
'three': json2.Any(3)
|
||||||
|
'four': json2.Any(4)
|
||||||
|
}
|
||||||
|
// numbers := {
|
||||||
|
// 'one': 1
|
||||||
|
// 'two': 2
|
||||||
|
// 'three': 3
|
||||||
|
// 'four': 4
|
||||||
|
// }
|
||||||
|
assert json2.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 := json2.encode[Message](msg)
|
||||||
|
assert out == expected
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StByteArray {
|
||||||
|
ba []u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_byte_array() {
|
||||||
|
assert json2.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 := json2.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 json2.encode_pretty(foo) == '{
|
||||||
|
"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 := json2.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 json2.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 := json2.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 := json2.encode(game)
|
||||||
|
|
||||||
|
assert enc == '{"title":"Super Mega Game","player":{"name":"Monke"},"other":[{"tag":"Pen"},{"tag":"Cookie"},1,"Stool","${t.format_rfc3339()}"]}'
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo3 {
|
||||||
|
name string
|
||||||
|
age int @[omitempty]
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn test_omit_empty() {
|
||||||
|
// foo := Foo3{'Bob', 0}
|
||||||
|
// assert json2.encode(foo) == '{"name":"Bob"}'
|
||||||
|
// assert json2.encode_pretty(foo) == '{
|
||||||
|
// "name": "Bob"
|
||||||
|
// }'
|
||||||
|
// }
|
||||||
|
|
||||||
|
struct Foo31 {
|
||||||
|
name string
|
||||||
|
age ?int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_option_instead_of_omit_empty() {
|
||||||
|
foo := Foo31{
|
||||||
|
name: 'Bob'
|
||||||
|
}
|
||||||
|
assert json2.encode_pretty(foo) == '{
|
||||||
|
"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 json2.encode(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn test_encode_sumtype_defined_ahead() {
|
||||||
|
// ret := create_game_packet(&GamePacketData(GPScale{}))
|
||||||
|
// assert ret == '{"value":0}'
|
||||||
|
// }
|
@ -17,7 +17,7 @@ pub:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// byte array versions of the most common tokens/chars to avoid reallocations
|
// byte array versions of the most common tokens/chars to avoid reallocations
|
||||||
const null_in_bytes = 'null'
|
const null_in_string = 'null'
|
||||||
|
|
||||||
const true_in_string = 'true'
|
const true_in_string = 'true'
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ fn (e &Encoder) encode_value_with_level[T](val T, level int, mut buf []u8) ! {
|
|||||||
str_value := val.json_str()
|
str_value := val.json_str()
|
||||||
unsafe { buf.push_many(str_value.str, str_value.len) }
|
unsafe { buf.push_many(str_value.str, str_value.len) }
|
||||||
} $else $if T is Null {
|
} $else $if T is Null {
|
||||||
unsafe { buf.push_many(null_in_bytes.str, null_in_bytes.len) }
|
unsafe { buf.push_many(null_in_string.str, null_in_string.len) }
|
||||||
} $else $if T is $struct {
|
} $else $if T is $struct {
|
||||||
e.encode_struct(val, level, mut buf)!
|
e.encode_struct(val, level, mut buf)!
|
||||||
} $else $if T is $enum {
|
} $else $if T is $enum {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user