mirror of
https://github.com/vlang/v.git
synced 2025-09-09 15:27:05 -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 whitespace_chars = [` `, `\t`, `\n`]!
|
||||
|
||||
// Node represents a node in a linked list to store ValueInfo.
|
||||
struct Node[T] {
|
||||
mut:
|
||||
@ -147,55 +149,6 @@ pub enum ValueKind {
|
||||
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.
|
||||
fn (mut checker Decoder) error(message string) ! {
|
||||
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')
|
||||
}
|
||||
|
||||
// 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
|
||||
value_kind := get_value_kind(val[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
|
||||
}
|
||||
.object {
|
||||
if checker_end - checker.checker_idx < 2 {
|
||||
return checker.error('EOF error: expecting a complete object after `{`')
|
||||
}
|
||||
checker.checker_idx++
|
||||
for val[checker.checker_idx] != `}` {
|
||||
// check if the JSON string is an empty object
|
||||
@ -272,7 +236,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
break
|
||||
}
|
||||
@ -294,7 +258,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
||||
if checker.checker_idx >= checker_end - 1 {
|
||||
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')
|
||||
}
|
||||
checker.checker_idx++
|
||||
@ -307,7 +271,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
||||
checker.checker_idx++
|
||||
|
||||
// skip whitespace
|
||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
||||
for val[checker.checker_idx] in whitespace_chars {
|
||||
checker.checker_idx++
|
||||
}
|
||||
|
||||
@ -315,7 +279,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
||||
`"`, `[`, `{`, `0`...`9`, `-`, `n`, `t`, `f` {
|
||||
checker.check_json_format(val)!
|
||||
// whitespace
|
||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
||||
for val[checker.checker_idx] in whitespace_chars {
|
||||
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] == `,` {
|
||||
checker.checker_idx++
|
||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
||||
for val[checker.checker_idx] in whitespace_chars {
|
||||
checker.checker_idx++
|
||||
}
|
||||
if val[checker.checker_idx] != `"` {
|
||||
return checker.error('Expecting object key')
|
||||
return checker.error('Expecting object key after `,`')
|
||||
}
|
||||
} else {
|
||||
if val[checker.checker_idx] == `}` {
|
||||
@ -360,7 +324,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
||||
|
||||
for val[checker.checker_idx] != `]` {
|
||||
// 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 {
|
||||
break
|
||||
}
|
||||
@ -378,7 +342,7 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
||||
checker.check_json_format(val)!
|
||||
|
||||
// whitespace
|
||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
||||
for val[checker.checker_idx] in whitespace_chars {
|
||||
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] == `,` {
|
||||
checker.checker_idx++
|
||||
for val[checker.checker_idx] in [` `, `\t`, `\n`] {
|
||||
for val[checker.checker_idx] in whitespace_chars {
|
||||
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 [`,`, `:`, `}`, `]`] {
|
||||
// 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')!
|
||||
} else {
|
||||
// whitespace
|
||||
@ -558,17 +522,23 @@ fn (mut checker Decoder) check_json_format(val string) ! {
|
||||
}
|
||||
|
||||
// decode decodes a JSON string into a specified type.
|
||||
@[manualfree]
|
||||
pub fn decode[T](val string) !T {
|
||||
if val == '' {
|
||||
return error('empty string')
|
||||
}
|
||||
mut decoder := Decoder{
|
||||
json: val
|
||||
}
|
||||
|
||||
decoder.check_json_format(val)!
|
||||
check_if_json_match[T](val)!
|
||||
|
||||
mut result := T{}
|
||||
decoder.current_node = decoder.values_info.head
|
||||
decoder.decode_value(mut &result)!
|
||||
unsafe {
|
||||
decoder.values_info.free()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@ -629,6 +599,8 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||
}
|
||||
|
||||
val = string_buffer.bytestr()
|
||||
} else {
|
||||
return error('Expected string, but got ${string_info.value_kind}')
|
||||
}
|
||||
} $else $if T.unaliased_typ is $sumtype {
|
||||
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
|
||||
}
|
||||
} else {
|
||||
return error('Expected object, but got ${struct_info.value_kind}')
|
||||
}
|
||||
unsafe {
|
||||
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 {
|
||||
value_info := decoder.current_node.value
|
||||
|
||||
if value_info.value_kind != .boolean {
|
||||
return error('Expected boolean, but got ${value_info.value_kind}')
|
||||
}
|
||||
|
||||
unsafe {
|
||||
val = vmemcmp(decoder.json.str + value_info.position, true_in_string.str,
|
||||
true_in_string.len) == 0
|
||||
@ -873,6 +851,8 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||
unsafe {
|
||||
string_buffer_to_generic_number(val, bytes)
|
||||
}
|
||||
} else {
|
||||
return error('Expected number, but got ${value_info.value_kind}')
|
||||
}
|
||||
} $else {
|
||||
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
|
||||
}
|
||||
} 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])!
|
||||
}
|
||||
} 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 {
|
||||
$for v in initialized_sumtype.variants {
|
||||
if initialized_sumtype is v {
|
||||
$if v is $array {
|
||||
mut val := initialized_sumtype.clone()
|
||||
decoder.decode_value(mut val)!
|
||||
return T(val)
|
||||
} $else {
|
||||
// workaround for auto generated function considering sumtype as array
|
||||
unsafe {
|
||||
mut val := initialized_sumtype
|
||||
decoder.decode_value(mut val)!
|
||||
return T(val)
|
||||
|
@ -4,42 +4,42 @@ fn test_check_if_json_match() {
|
||||
// /* Test wrong string values */
|
||||
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'
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
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_'
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
has_error = false
|
||||
|
||||
check_if_json_match[[]int]('{"key": "value"}') or {
|
||||
decode[[]int]('{"key": "value"}') or {
|
||||
assert err.str() == 'Expected array, but got object'
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
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'
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
has_error = false
|
||||
|
||||
check_if_json_match[int]('{"key": "value"}') or {
|
||||
decode[int]('{"key": "value"}') or {
|
||||
assert err.str() == 'Expected number, but got object'
|
||||
has_error = true
|
||||
}
|
||||
assert has_error, 'Expected error'
|
||||
has_error = false
|
||||
|
||||
check_if_json_match[bool]('{"key": "value"}') or {
|
||||
decode[bool]('{"key": "value"}') or {
|
||||
assert err.str() == 'Expected boolean, but got object'
|
||||
has_error = true
|
||||
}
|
||||
@ -47,19 +47,19 @@ fn test_check_if_json_match() {
|
||||
has_error = false
|
||||
|
||||
// /* 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
|
||||
}
|
||||
@ -157,7 +157,7 @@ fn test_check_json_format() {
|
||||
},
|
||||
{
|
||||
'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],]'
|
||||
|
@ -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
|
||||
const null_in_bytes = 'null'
|
||||
const null_in_string = 'null'
|
||||
|
||||
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()
|
||||
unsafe { buf.push_many(str_value.str, str_value.len) }
|
||||
} $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 {
|
||||
e.encode_struct(val, level, mut buf)!
|
||||
} $else $if T is $enum {
|
||||
|
Loading…
x
Reference in New Issue
Block a user