mirror of
https://github.com/vlang/v.git
synced 2025-09-14 09:56:16 -04:00
json2: cleanup (#20347)
This commit is contained in:
parent
bcd77eddce
commit
62872c677f
@ -33,6 +33,7 @@ pub fn (t Time) format_ss_nano() string {
|
||||
// format_rfc3339 returns a date string in "YYYY-MM-DDTHH:mm:ss.123Z" format (24 hours, see https://www.rfc-editor.org/rfc/rfc3339.html)
|
||||
// RFC3339 is an Internet profile, based on the ISO 8601 standard for for representation of dates and times using the Gregorian calendar.
|
||||
// It is intended to improve consistency and interoperability, when representing and using date and time in Internet protocols.
|
||||
@[markused]
|
||||
pub fn (t Time) format_rfc3339() string {
|
||||
u := t.local_to_utc()
|
||||
return '${u.year:04d}-${u.month:02d}-${u.day:02d}T${u.hour:02d}:${u.minute:02d}:${u.second:02d}.${(u.nanosecond / 1_000_000):03d}Z'
|
||||
|
@ -123,6 +123,6 @@ fn test_str() {
|
||||
assert sample_data['bool'] or { 0 }.str() == 'false'
|
||||
assert sample_data['str'] or { 0 }.str() == 'test'
|
||||
assert sample_data['null'] or { 0 }.str() == 'null'
|
||||
assert sample_data['arr'] or { 0 }.str() == '["lol"]'
|
||||
assert sample_data['arr'] or { 'not lol' }.str() == '["lol"]'
|
||||
assert sample_data.str() == '{"int":1,"i64":128,"f32":2.0,"f64":1.283,"bool":false,"str":"test","null":null,"arr":["lol"],"obj":{"foo":10}}'
|
||||
}
|
||||
|
@ -185,7 +185,9 @@ fn (mut p Parser) decode_value() !Any {
|
||||
}
|
||||
}
|
||||
}
|
||||
return Any(null)
|
||||
return InvalidTokenError{
|
||||
token: p.tok
|
||||
}
|
||||
}
|
||||
|
||||
@[manualfree]
|
||||
|
@ -253,7 +253,7 @@ fn test_pointer() {
|
||||
}
|
||||
|
||||
fn test_sumtypes() {
|
||||
assert json.encode(StructType[SumTypes]{}) == '{}'
|
||||
assert json.encode(StructType[SumTypes]{}) == '{}' // is_none := val.$(field.name).str() == 'unknown sum type value'
|
||||
assert json.encode(StructType[SumTypes]{ val: '' }) == '{"val":""}'
|
||||
assert json.encode(StructType[SumTypes]{ val: 'a' }) == '{"val":"a"}'
|
||||
|
||||
|
@ -50,80 +50,6 @@ fn (e &Encoder) encode_newline(level int, mut buf []u8) ! {
|
||||
}
|
||||
}
|
||||
|
||||
fn (e &Encoder) encode_any(val Any, level int, mut buf []u8) ! {
|
||||
match val {
|
||||
string {
|
||||
e.encode_string(val, mut buf)!
|
||||
}
|
||||
bool {
|
||||
if val == true {
|
||||
unsafe { buf.push_many(json2.true_in_string.str, json2.true_in_string.len) }
|
||||
} else {
|
||||
unsafe { buf.push_many(json2.false_in_string.str, json2.false_in_string.len) }
|
||||
}
|
||||
}
|
||||
i8, i16, int, i64 {
|
||||
str_int := val.str()
|
||||
unsafe { buf.push_many(str_int.str, str_int.len) }
|
||||
}
|
||||
u8, u16, u32, u64 {
|
||||
str_int := val.str()
|
||||
unsafe { buf.push_many(str_int.str, str_int.len) }
|
||||
}
|
||||
f32, f64 {
|
||||
$if !nofloat ? {
|
||||
str_float := val.str()
|
||||
unsafe { buf.push_many(str_float.str, str_float.len) }
|
||||
if str_float[str_float.len - 1] == `.` {
|
||||
buf << json2.zero_rune
|
||||
}
|
||||
return
|
||||
}
|
||||
buf << json2.zero_rune
|
||||
}
|
||||
map[string]Any {
|
||||
buf << json2.curly_open_rune
|
||||
mut i := 0
|
||||
for k, v in val {
|
||||
e.encode_newline(level, mut buf)!
|
||||
e.encode_string(k, mut buf)!
|
||||
buf << json2.colon_rune
|
||||
if e.newline != 0 {
|
||||
buf << ` `
|
||||
}
|
||||
e.encode_value_with_level(v, level + 1, mut buf)!
|
||||
if i < val.len - 1 {
|
||||
buf << json2.comma_rune
|
||||
}
|
||||
i++
|
||||
}
|
||||
e.encode_newline(level - 1, mut buf)!
|
||||
buf << json2.curly_close_rune
|
||||
}
|
||||
[]Any {
|
||||
buf << `[`
|
||||
for i in 0 .. val.len {
|
||||
e.encode_newline(level, mut buf)!
|
||||
e.encode_value_with_level(val[i], level + 1, mut buf)!
|
||||
if i < val.len - 1 {
|
||||
buf << json2.comma_rune
|
||||
}
|
||||
}
|
||||
e.encode_newline(level - 1, mut buf)!
|
||||
buf << `]`
|
||||
}
|
||||
time.Time {
|
||||
str_time := val.format_rfc3339()
|
||||
buf << `"`
|
||||
unsafe { buf.push_many(str_time.str, str_time.len) }
|
||||
buf << `"`
|
||||
}
|
||||
Null {
|
||||
unsafe { buf.push_many(json2.null_in_bytes.str, json2.null_in_bytes.len) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (e &Encoder) encode_map[T](value T, level int, mut buf []u8) ! {
|
||||
buf << json2.curly_open_rune
|
||||
mut idx := 0
|
||||
@ -135,7 +61,18 @@ fn (e &Encoder) encode_map[T](value T, level int, mut buf []u8) ! {
|
||||
if e.newline != 0 {
|
||||
buf << ` `
|
||||
}
|
||||
e.encode_value_with_level(v, level + 1, mut buf)!
|
||||
|
||||
// workaround to avoid `cannot convert 'struct x__json2__Any' to 'struct string'`
|
||||
$if v is $sumtype {
|
||||
$for variant_value in v.variants {
|
||||
if v is variant_value {
|
||||
e.encode_value_with_level(v, level + 1, mut buf)!
|
||||
}
|
||||
}
|
||||
} $else {
|
||||
e.encode_value_with_level(v, level + 1, mut buf)!
|
||||
}
|
||||
|
||||
if idx < value.len - 1 {
|
||||
buf << json2.comma_rune
|
||||
}
|
||||
@ -147,31 +84,35 @@ fn (e &Encoder) encode_map[T](value T, level int, mut buf []u8) ! {
|
||||
}
|
||||
|
||||
fn (e &Encoder) encode_value_with_level[T](val T, level int, mut buf []u8) ! {
|
||||
$if T is string {
|
||||
$if val is $option {
|
||||
workaround := val
|
||||
if workaround != none {
|
||||
e.encode_value_with_level(val, level, mut buf)!
|
||||
}
|
||||
} $else $if T is string {
|
||||
e.encode_string(val, mut buf)!
|
||||
} $else $if T is Any {
|
||||
e.encode_any(val, level, mut buf)!
|
||||
} $else $if T is $sumtype {
|
||||
if val.str() != 'unknown sum type value' {
|
||||
$for v in val.variants {
|
||||
if val is v {
|
||||
e.encode_value_with_level(val, level, mut buf)!
|
||||
}
|
||||
$for v in val.variants {
|
||||
if val is v {
|
||||
e.encode_value_with_level(val, level, mut buf)!
|
||||
}
|
||||
}
|
||||
} $else $if T is $alias {
|
||||
// TODO
|
||||
} $else $if T is time.Time {
|
||||
parsed_time := time.parse(val.str()) or { time.Time{} }
|
||||
e.encode_string(parsed_time.format_rfc3339(), mut buf)!
|
||||
str_value := val.format_rfc3339()
|
||||
buf << json2.quote_rune
|
||||
unsafe { buf.push_many(str_value.str, str_value.len) }
|
||||
buf << json2.quote_rune
|
||||
} $else $if T is $map {
|
||||
e.encode_map(val, level, mut buf)!
|
||||
} $else $if T is []Any {
|
||||
// TODO test
|
||||
e.encode_any(val, level, mut buf)!
|
||||
} $else $if T is $array {
|
||||
e.encode_array(val, level, mut buf)!
|
||||
} $else $if T is Encodable {
|
||||
str_value := val.json_str()
|
||||
unsafe { buf.push_many(str_value.str, str_value.len) }
|
||||
} $else $if T is Null {
|
||||
unsafe { buf.push_many(json2.null_in_bytes.str, json2.null_in_bytes.len) }
|
||||
} $else $if T is $struct {
|
||||
e.encode_struct(val, level, mut buf)!
|
||||
} $else $if T is $enum {
|
||||
@ -180,8 +121,6 @@ fn (e &Encoder) encode_value_with_level[T](val T, level int, mut buf []u8) ! {
|
||||
} $else $if T is $int || T is $float || T is bool {
|
||||
str_int := val.str()
|
||||
unsafe { buf.push_many(str_int.str, str_int.len) }
|
||||
} $else $if T is Null {
|
||||
unsafe { buf.push_many(json2.null_in_bytes.str, json2.null_in_bytes.len) }
|
||||
} $else {
|
||||
return error('cannot encode value with ${typeof(val).name} type')
|
||||
}
|
||||
@ -203,6 +142,7 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
|
||||
}
|
||||
$for field in U.fields {
|
||||
mut ignore_field := false
|
||||
|
||||
value := val.$(field.name)
|
||||
|
||||
is_nil := val.$(field.name).str() == '&nil'
|
||||
@ -215,10 +155,9 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
|
||||
}
|
||||
}
|
||||
|
||||
$if field.is_option {
|
||||
is_none := value == none
|
||||
|
||||
if !is_none {
|
||||
$if value is $option {
|
||||
workaround := val.$(field.name)
|
||||
if workaround != none { // smartcast
|
||||
e.encode_newline(level, mut buf)!
|
||||
if json_name != '' {
|
||||
e.encode_string(json_name, mut buf)!
|
||||
@ -230,61 +169,12 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
|
||||
if e.newline != 0 {
|
||||
buf << ` `
|
||||
}
|
||||
|
||||
$if field.typ is ?string {
|
||||
e.encode_string(val.$(field.name) ?.str(), mut buf)!
|
||||
} $else $if field.typ is ?bool {
|
||||
if value ?.str() == json2.true_in_string {
|
||||
unsafe { buf.push_many(json2.true_in_string.str, json2.true_in_string.len) }
|
||||
} else {
|
||||
unsafe { buf.push_many(json2.false_in_string.str, json2.false_in_string.len) }
|
||||
}
|
||||
} $else $if field.typ is ?f32 || field.typ is ?f64 || field.typ is ?i8
|
||||
|| field.typ is ?i16 || field.typ is ?int || field.typ is ?i64
|
||||
|| field.typ is ?u8 || field.typ is ?u16 || field.typ is ?u32
|
||||
|| field.typ is ?u64 {
|
||||
str_value := val.$(field.name) ?.str()
|
||||
unsafe { buf.push_many(str_value.str, str_value.len) }
|
||||
} $else $if field.typ is ?time.Time {
|
||||
option_value := val.$(field.name) as ?time.Time
|
||||
parsed_time := option_value as time.Time
|
||||
e.encode_string(parsed_time.format_rfc3339(), mut buf)!
|
||||
} $else $if field.is_array {
|
||||
e.encode_array(value, level + 1, mut buf)!
|
||||
} $else $if field.is_struct {
|
||||
e.encode_struct(value, level + 1, mut buf)!
|
||||
} $else $if field.is_enum {
|
||||
// FIXME - checker and cast error
|
||||
// wr.write(int(val.$(field.name)?).str().bytes())!
|
||||
return error('type ${typeof(val).name} cannot be encoded yet')
|
||||
} $else $if field.is_alias {
|
||||
match field.unaliased_typ {
|
||||
typeof[string]().idx {
|
||||
e.encode_string(value.str(), mut buf)!
|
||||
}
|
||||
typeof[bool]().idx {}
|
||||
typeof[f32]().idx, typeof[f64]().idx, typeof[i8]().idx, typeof[i16]().idx,
|
||||
typeof[int]().idx, typeof[i64]().idx, typeof[u8]().idx, typeof[u16]().idx,
|
||||
typeof[u32]().idx, typeof[u64]().idx {
|
||||
str_value := value.str()
|
||||
unsafe { buf.push_many(str_value.str, str_value.len) }
|
||||
}
|
||||
typeof[[]int]().idx {
|
||||
// FIXME - error: could not infer generic type `U` in call to `encode_array`
|
||||
// e.encode_array(value, level, mut buf)!
|
||||
}
|
||||
else {
|
||||
// e.encode_value_with_level(value, level + 1, mut buf)!
|
||||
}
|
||||
}
|
||||
} $else {
|
||||
return error('type ${typeof(val).name} cannot be array encoded')
|
||||
}
|
||||
e.encode_value_with_level(value, level, mut buf)!
|
||||
} else {
|
||||
ignore_field = true
|
||||
}
|
||||
} $else {
|
||||
is_none := val.$(field.name).str() == 'unknown sum type value'
|
||||
is_none := val.$(field.name).str() == 'unknown sum type value' // assert json.encode(StructType[SumTypes]{}) == '{}'
|
||||
if !is_none && !is_nil {
|
||||
e.encode_newline(level, mut buf)!
|
||||
if json_name != '' {
|
||||
@ -410,15 +300,10 @@ fn (e &Encoder) encode_array[U](val []U, level int, mut buf []u8) ! {
|
||||
} $else $if U is $struct {
|
||||
e.encode_struct(val[i], level + 1, mut buf)!
|
||||
} $else $if U is $sumtype {
|
||||
// TODO test
|
||||
$if U is Any {
|
||||
e.encode_any(val[i], level + 1, mut buf)!
|
||||
} $else {
|
||||
// TODO
|
||||
}
|
||||
e.encode_value_with_level(val[i], level + 1, mut buf)!
|
||||
} $else $if U is $enum {
|
||||
// TODO test
|
||||
e.encode_any(i64(val[i]), level + 1, mut buf)!
|
||||
e.encode_value_with_level(val[i], level + 1, mut buf)!
|
||||
} $else {
|
||||
return error('type ${typeof(val).name} cannot be array encoded')
|
||||
}
|
||||
|
@ -179,4 +179,10 @@ fn test_encode_time() {
|
||||
assert json.encode({
|
||||
'bro': json.Any(time.Time{})
|
||||
}) == '{"bro":"0000-00-00T00:00:00.000Z"}'
|
||||
|
||||
assert json.encode({
|
||||
'bro': time.Time{}
|
||||
}) == '{"bro":"0000-00-00T00:00:00.000Z"}'
|
||||
|
||||
assert json.encode(time.Time{}) == '"0000-00-00T00:00:00.000Z"'
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ fn test_fast_raw_decode() {
|
||||
s := '{"name":"Peter","age":28,"salary":95000.5,"title":2}'
|
||||
o := json.fast_raw_decode(s) or {
|
||||
assert false
|
||||
json.Any(json.null)
|
||||
json.Any('')
|
||||
}
|
||||
str := o.str()
|
||||
assert str == '{"name":"Peter","age":"28","salary":"95000.5","title":"2"}'
|
||||
|
@ -272,7 +272,6 @@ pub struct Association {
|
||||
price APrice
|
||||
}
|
||||
|
||||
//! FIX: returning null
|
||||
fn test_encoding_struct_with_pointers() {
|
||||
value := Association{
|
||||
association: &Association{
|
||||
@ -299,10 +298,11 @@ 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"}}}'
|
||||
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{
|
||||
@ -332,7 +332,111 @@ fn test_nested_type() {
|
||||
pets: 'little boo'
|
||||
}
|
||||
}
|
||||
extra: {
|
||||
'2': {
|
||||
'n1': 2
|
||||
'n2': 4
|
||||
'n3': 8
|
||||
'n4': 16
|
||||
}
|
||||
'3': {
|
||||
'n1': 3
|
||||
'n2': 9
|
||||
'n3': 27
|
||||
'n4': 81
|
||||
}
|
||||
}
|
||||
}
|
||||
out := json.encode(data)
|
||||
assert out == data_expected
|
||||
}
|
||||
|
||||
struct Human {
|
||||
name string
|
||||
}
|
||||
|
||||
struct Item {
|
||||
tag string
|
||||
}
|
||||
|
||||
enum Animal {
|
||||
dog // Will be encoded as `0`
|
||||
cat
|
||||
}
|
||||
|
||||
type Entity = Animal | Human | Item | string | time.Time
|
||||
|
||||
struct SomeGame {
|
||||
title string
|
||||
player Entity
|
||||
other []Entity
|
||||
}
|
||||
|
||||
fn test_encode_decode_sumtype() {
|
||||
t := time.now()
|
||||
game := SomeGame{
|
||||
title: 'Super Mega Game'
|
||||
player: Human{'Monke'}
|
||||
other: [
|
||||
Entity(Item{'Pen'}),
|
||||
Item{'Cookie'},
|
||||
Animal.cat,
|
||||
'Stool',
|
||||
t,
|
||||
]
|
||||
}
|
||||
|
||||
enc := json.encode(game)
|
||||
|
||||
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 json.encode(foo) == '{"name":"Bob"}'
|
||||
// assert json.encode_pretty(foo) == '{
|
||||
// "name": "Bob"
|
||||
// }'
|
||||
// }
|
||||
|
||||
struct Foo31 {
|
||||
name string
|
||||
age ?int
|
||||
}
|
||||
|
||||
fn test_option_instead_of_omit_empty() {
|
||||
foo := Foo31{
|
||||
name: 'Bob'
|
||||
}
|
||||
assert json.encode_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 json.encode(data)
|
||||
}
|
||||
|
||||
// fn test_encode_sumtype_defined_ahead() {
|
||||
// ret := create_game_packet(&GamePacketData(GPScale{}))
|
||||
// assert ret == '{"value":0}'
|
||||
// }
|
||||
|
@ -55,7 +55,7 @@ struct Item {
|
||||
}
|
||||
|
||||
enum Animal {
|
||||
dog // Will be encoded as `0`
|
||||
dog
|
||||
cat
|
||||
}
|
||||
|
||||
@ -69,22 +69,7 @@ struct SomeGame {
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
fn test_encode_decode_sumtype() {
|
||||
t := time.now()
|
||||
game := SomeGame{
|
||||
title: 'Super Mega Game'
|
||||
player: Human{'Monke'}
|
||||
other: [
|
||||
Entity(Item{'Pen'}),
|
||||
Item{'Cookie'},
|
||||
Animal.cat,
|
||||
'Stool',
|
||||
t,
|
||||
]
|
||||
}
|
||||
|
||||
enc := json.encode(game)
|
||||
|
||||
assert enc == '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},1,"Stool",{"_type":"Time","value":${t.unix_time()}}]}'
|
||||
enc := '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},1,"Stool",{"_type":"Time","value":${t.unix_time()}}]}'
|
||||
|
||||
dec := json.decode[SomeGame](enc)!
|
||||
|
||||
@ -137,6 +122,8 @@ fn test_encode_decode_time() {
|
||||
assert s.contains('"reg_date":1608621780')
|
||||
user2 := json.decode[User2](s)!
|
||||
assert user2.reg_date.str() == '2020-12-22 07:23:00'
|
||||
}
|
||||
|
||||
struct City {
|
||||
name string
|
||||
}
|
||||
@ -220,8 +207,7 @@ fn test_nested_type() {
|
||||
}
|
||||
}
|
||||
}
|
||||
out := json.encode(data)
|
||||
assert out == data_expected
|
||||
|
||||
data2 := json.decode[Data](data_expected)!
|
||||
assert data2.countries.len == data.countries.len
|
||||
for i in 0 .. 1 {
|
||||
@ -330,40 +316,3 @@ fn test_decode_missing_maps_field() {
|
||||
assert '${info.items}' == '[]'
|
||||
assert '${info.maps}' == '{}'
|
||||
}
|
||||
|
||||
struct Foo3 {
|
||||
name string
|
||||
age int @[omitempty]
|
||||
}
|
||||
|
||||
//! BUGFIX - .from_json(res)
|
||||
fn test_omit_empty() {
|
||||
foo := Foo3{'Bob', 0}
|
||||
assert json.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 json.encode(data)
|
||||
}
|
||||
|
||||
//! FIX: returning null
|
||||
fn test_encode_sumtype_defined_ahead() {
|
||||
ret := create_game_packet(&GamePacketData(GPScale{}))
|
||||
assert ret == '{"value":0,"_type":"GPScale"}'
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user