mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
json2: encode map (#16928)
This commit is contained in:
parent
bfb0932588
commit
979066856b
@ -252,3 +252,24 @@ fn test_sumtypes() {
|
|||||||
}
|
}
|
||||||
}) == '{"val":{"val":1}}'
|
}) == '{"val":{"val":1}}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_maps() {
|
||||||
|
assert json.encode(StructType[map[string]map[string]int]{}) == '{"val":{}}'
|
||||||
|
assert json.encode(StructType[map[string]string]{
|
||||||
|
val: {
|
||||||
|
'1': '1'
|
||||||
|
}
|
||||||
|
}) == '{"val":{"1":"1"}}'
|
||||||
|
assert json.encode(StructType[map[string]int]{
|
||||||
|
val: {
|
||||||
|
'1': 1
|
||||||
|
}
|
||||||
|
}) == '{"val":{"1":1}}'
|
||||||
|
// assert json.encode(StructType[map[string]map[string]int]{
|
||||||
|
// val: {
|
||||||
|
// 'a': {
|
||||||
|
// '1': 1
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }) == '{"val":{"a":{"1":1}}}'
|
||||||
|
}
|
||||||
|
@ -39,6 +39,10 @@ const quote_bytes = [u8(`"`)]
|
|||||||
const escaped_chars = [(r'\b').bytes(), (r'\f').bytes(), (r'\n').bytes(),
|
const escaped_chars = [(r'\b').bytes(), (r'\f').bytes(), (r'\n').bytes(),
|
||||||
(r'\r').bytes(), (r'\t').bytes()]
|
(r'\r').bytes(), (r'\t').bytes()]
|
||||||
|
|
||||||
|
const curly_open = [u8(`{`)]
|
||||||
|
|
||||||
|
const curly_close = [u8(`}`)]
|
||||||
|
|
||||||
// encode_value encodes a value to the specific writer.
|
// encode_value encodes a value to the specific writer.
|
||||||
pub fn (e &Encoder) encode_value[T](val T, mut wr io.Writer) ! {
|
pub fn (e &Encoder) encode_value[T](val T, mut wr io.Writer) ! {
|
||||||
e.encode_value_with_level[T](val, 1, mut wr)!
|
e.encode_value_with_level[T](val, 1, mut wr)!
|
||||||
@ -83,7 +87,7 @@ fn (e &Encoder) encode_any(val Any, level int, mut wr io.Writer) ! {
|
|||||||
wr.write(json2.zero_in_bytes)!
|
wr.write(json2.zero_in_bytes)!
|
||||||
}
|
}
|
||||||
map[string]Any {
|
map[string]Any {
|
||||||
wr.write([u8(`{`)])!
|
wr.write(json2.curly_open)!
|
||||||
mut i := 0
|
mut i := 0
|
||||||
for k, v in val {
|
for k, v in val {
|
||||||
e.encode_newline(level, mut wr)!
|
e.encode_newline(level, mut wr)!
|
||||||
@ -99,7 +103,7 @@ fn (e &Encoder) encode_any(val Any, level int, mut wr io.Writer) ! {
|
|||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
e.encode_newline(level - 1, mut wr)!
|
e.encode_newline(level - 1, mut wr)!
|
||||||
wr.write([u8(`}`)])!
|
wr.write(json2.curly_close)!
|
||||||
}
|
}
|
||||||
[]Any {
|
[]Any {
|
||||||
wr.write([u8(`[`)])!
|
wr.write([u8(`[`)])!
|
||||||
@ -128,6 +132,8 @@ fn (e &Encoder) encode_value_with_level[T](val T, level int, mut wr io.Writer) !
|
|||||||
} $else $if T is map[string]Any {
|
} $else $if T is map[string]Any {
|
||||||
// weird quirk but val is destructured immediately to Any
|
// weird quirk but val is destructured immediately to Any
|
||||||
e.encode_any(val, level, mut wr)!
|
e.encode_any(val, level, mut wr)!
|
||||||
|
} $else $if T is $map {
|
||||||
|
// FIXME - `e.encode_struct` can not encode `map[string]map[string]int` type
|
||||||
} $else $if T is []Any {
|
} $else $if T is []Any {
|
||||||
e.encode_any(val, level, mut wr)!
|
e.encode_any(val, level, mut wr)!
|
||||||
} $else $if T is Encodable {
|
} $else $if T is Encodable {
|
||||||
@ -145,7 +151,7 @@ fn (e &Encoder) encode_value_with_level[T](val T, level int, mut wr io.Writer) !
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (e &Encoder) encode_struct[U](val U, level int, mut wr io.Writer) ! {
|
fn (e &Encoder) encode_struct[U](val U, level int, mut wr io.Writer) ! {
|
||||||
wr.write([u8(`{`)])!
|
wr.write(json2.curly_open)!
|
||||||
mut i := 0
|
mut i := 0
|
||||||
mut fields_len := 0
|
mut fields_len := 0
|
||||||
$for field in U.fields {
|
$for field in U.fields {
|
||||||
@ -254,6 +260,24 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut wr io.Writer) ! {
|
|||||||
// e.encode_array(value, level + 1, mut wr)! // FIXME - error: could not infer generic type `U` in call to `encode_array`
|
// e.encode_array(value, level + 1, mut wr)! // FIXME - error: could not infer generic type `U` in call to `encode_array`
|
||||||
} $else $if field.typ is $struct {
|
} $else $if field.typ is $struct {
|
||||||
e.encode_struct(value, level + 1, mut wr)!
|
e.encode_struct(value, level + 1, mut wr)!
|
||||||
|
} $else $if field.is_map {
|
||||||
|
wr.write(json2.curly_open)!
|
||||||
|
mut idx := 0
|
||||||
|
for k, v in value {
|
||||||
|
e.encode_newline(level, mut wr)!
|
||||||
|
e.encode_string(k.str(), mut wr)!
|
||||||
|
wr.write(json2.colon_bytes)!
|
||||||
|
if e.newline != 0 {
|
||||||
|
wr.write(json2.space_bytes)!
|
||||||
|
}
|
||||||
|
e.encode_value_with_level(v, level + 1, mut wr)!
|
||||||
|
if idx < value.len - 1 {
|
||||||
|
wr.write(json2.comma_bytes)!
|
||||||
|
}
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
e.encode_newline(level, mut wr)!
|
||||||
|
wr.write(json2.curly_close)!
|
||||||
} $else $if field.is_enum {
|
} $else $if field.is_enum {
|
||||||
// TODO - replace for `field.typ is $enum`
|
// TODO - replace for `field.typ is $enum`
|
||||||
wr.write(int(val.$(field.name)).str().bytes())!
|
wr.write(int(val.$(field.name)).str().bytes())!
|
||||||
@ -356,7 +380,7 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut wr io.Writer) ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.encode_newline(level - 1, mut wr)!
|
e.encode_newline(level - 1, mut wr)!
|
||||||
wr.write([u8(`}`)])!
|
wr.write(json2.curly_close)!
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (e &Encoder) encode_array[U](val []U, level int, mut wr io.Writer) ! {
|
fn (e &Encoder) encode_array[U](val []U, level int, mut wr io.Writer) ! {
|
||||||
|
@ -20,115 +20,119 @@ pub fn fast_raw_decode(src string) !Any {
|
|||||||
|
|
||||||
// decode is a generic function that decodes a JSON string into the target type.
|
// decode is a generic function that decodes a JSON string into the target type.
|
||||||
pub fn decode[T](src string) !T {
|
pub fn decode[T](src string) !T {
|
||||||
res := raw_decode(src)!.as_map()
|
|
||||||
mut typ := T{}
|
mut typ := T{}
|
||||||
$for field in T.fields {
|
$if T is $struct {
|
||||||
mut json_name := field.name
|
res := raw_decode(src)!.as_map()
|
||||||
for attr in field.attrs {
|
$for field in T.fields {
|
||||||
if attr.contains('json: ') {
|
mut json_name := field.name
|
||||||
json_name = attr.replace('json: ', '')
|
for attr in field.attrs {
|
||||||
break
|
if attr.contains('json: ') {
|
||||||
|
json_name = attr.replace('json: ', '')
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$if field.is_enum {
|
$if field.is_enum {
|
||||||
typ.$(field.name) = if key := res[field.name] {
|
typ.$(field.name) = if key := res[field.name] {
|
||||||
key.int()
|
key.int()
|
||||||
} else {
|
} else {
|
||||||
res[json_name]!.int()
|
res[json_name]!.int()
|
||||||
}
|
}
|
||||||
} $else $if field.typ is u8 {
|
} $else $if field.typ is u8 {
|
||||||
typ.$(field.name) = res[json_name]!.u64()
|
typ.$(field.name) = res[json_name]!.u64()
|
||||||
} $else $if field.typ is u16 {
|
} $else $if field.typ is u16 {
|
||||||
typ.$(field.name) = res[json_name]!.u64()
|
typ.$(field.name) = res[json_name]!.u64()
|
||||||
} $else $if field.typ is u32 {
|
} $else $if field.typ is u32 {
|
||||||
typ.$(field.name) = res[json_name]!.u64()
|
typ.$(field.name) = res[json_name]!.u64()
|
||||||
} $else $if field.typ is u64 {
|
} $else $if field.typ is u64 {
|
||||||
typ.$(field.name) = res[json_name]!.u64()
|
typ.$(field.name) = res[json_name]!.u64()
|
||||||
} $else $if field.typ is int {
|
} $else $if field.typ is int {
|
||||||
typ.$(field.name) = res[json_name]!.int()
|
typ.$(field.name) = res[json_name]!.int()
|
||||||
} $else $if field.typ is i8 {
|
} $else $if field.typ is i8 {
|
||||||
typ.$(field.name) = res[json_name]!.int()
|
typ.$(field.name) = res[json_name]!.int()
|
||||||
} $else $if field.typ is i16 {
|
} $else $if field.typ is i16 {
|
||||||
typ.$(field.name) = res[json_name]!.int()
|
typ.$(field.name) = res[json_name]!.int()
|
||||||
} $else $if field.typ is i32 {
|
} $else $if field.typ is i32 {
|
||||||
typ.$(field.name) = i32(res[field.name]!.int())
|
typ.$(field.name) = i32(res[field.name]!.int())
|
||||||
} $else $if field.typ is i64 {
|
} $else $if field.typ is i64 {
|
||||||
typ.$(field.name) = res[json_name]!.i64()
|
typ.$(field.name) = res[json_name]!.i64()
|
||||||
} $else $if field.typ is ?u8 {
|
} $else $if field.typ is ?u8 {
|
||||||
if json_name in res {
|
if json_name in res {
|
||||||
typ.$(field.name) = ?u8(res[json_name]!.i64())
|
typ.$(field.name) = ?u8(res[json_name]!.i64())
|
||||||
}
|
}
|
||||||
} $else $if field.typ is ?i8 {
|
} $else $if field.typ is ?i8 {
|
||||||
if json_name in res {
|
if json_name in res {
|
||||||
typ.$(field.name) = ?i8(res[json_name]!.i64())
|
typ.$(field.name) = ?i8(res[json_name]!.i64())
|
||||||
}
|
}
|
||||||
} $else $if field.typ is ?u16 {
|
} $else $if field.typ is ?u16 {
|
||||||
if json_name in res {
|
if json_name in res {
|
||||||
typ.$(field.name) = ?u16(res[json_name]!.i64())
|
typ.$(field.name) = ?u16(res[json_name]!.i64())
|
||||||
}
|
}
|
||||||
} $else $if field.typ is ?i16 {
|
} $else $if field.typ is ?i16 {
|
||||||
if json_name in res {
|
if json_name in res {
|
||||||
typ.$(field.name) = ?i16(res[json_name]!.i64())
|
typ.$(field.name) = ?i16(res[json_name]!.i64())
|
||||||
}
|
}
|
||||||
} $else $if field.typ is ?u32 {
|
} $else $if field.typ is ?u32 {
|
||||||
if json_name in res {
|
if json_name in res {
|
||||||
typ.$(field.name) = ?u32(res[json_name]!.i64())
|
typ.$(field.name) = ?u32(res[json_name]!.i64())
|
||||||
}
|
}
|
||||||
} $else $if field.typ is ?i32 {
|
} $else $if field.typ is ?i32 {
|
||||||
if json_name in res {
|
if json_name in res {
|
||||||
typ.$(field.name) = ?i32(res[json_name]!.i64())
|
typ.$(field.name) = ?i32(res[json_name]!.i64())
|
||||||
}
|
}
|
||||||
} $else $if field.typ is ?u64 {
|
} $else $if field.typ is ?u64 {
|
||||||
if json_name in res {
|
if json_name in res {
|
||||||
typ.$(field.name) = ?u64(res[json_name]!.i64())
|
typ.$(field.name) = ?u64(res[json_name]!.i64())
|
||||||
}
|
}
|
||||||
} $else $if field.typ is ?i64 {
|
} $else $if field.typ is ?i64 {
|
||||||
if json_name in res {
|
if json_name in res {
|
||||||
typ.$(field.name) = ?i64(res[json_name]!.i64())
|
typ.$(field.name) = ?i64(res[json_name]!.i64())
|
||||||
}
|
}
|
||||||
} $else $if field.typ is ?int {
|
} $else $if field.typ is ?int {
|
||||||
if json_name in res {
|
if json_name in res {
|
||||||
typ.$(field.name) = ?int(res[json_name]!.i64())
|
typ.$(field.name) = ?int(res[json_name]!.i64())
|
||||||
}
|
}
|
||||||
} $else $if field.typ is f32 {
|
} $else $if field.typ is f32 {
|
||||||
typ.$(field.name) = res[json_name]!.f32()
|
|
||||||
} $else $if field.typ is ?f32 {
|
|
||||||
if json_name in res {
|
|
||||||
typ.$(field.name) = res[json_name]!.f32()
|
typ.$(field.name) = res[json_name]!.f32()
|
||||||
}
|
} $else $if field.typ is ?f32 {
|
||||||
} $else $if field.typ is f64 {
|
if json_name in res {
|
||||||
typ.$(field.name) = res[json_name]!.f64()
|
typ.$(field.name) = res[json_name]!.f32()
|
||||||
} $else $if field.typ is ?f64 {
|
}
|
||||||
if json_name in res {
|
} $else $if field.typ is f64 {
|
||||||
typ.$(field.name) = res[json_name]!.f64()
|
typ.$(field.name) = res[json_name]!.f64()
|
||||||
}
|
} $else $if field.typ is ?f64 {
|
||||||
} $else $if field.typ is bool {
|
if json_name in res {
|
||||||
typ.$(field.name) = res[json_name]!.bool()
|
typ.$(field.name) = res[json_name]!.f64()
|
||||||
} $else $if field.typ is ?bool {
|
}
|
||||||
if json_name in res {
|
} $else $if field.typ is bool {
|
||||||
typ.$(field.name) = res[json_name]!.bool()
|
typ.$(field.name) = res[json_name]!.bool()
|
||||||
}
|
} $else $if field.typ is ?bool {
|
||||||
} $else $if field.typ is string {
|
if json_name in res {
|
||||||
typ.$(field.name) = res[json_name]!.str()
|
typ.$(field.name) = res[json_name]!.bool()
|
||||||
} $else $if field.typ is ?string {
|
}
|
||||||
if json_name in res {
|
} $else $if field.typ is string {
|
||||||
typ.$(field.name) = res[json_name]!.str()
|
typ.$(field.name) = res[json_name]!.str()
|
||||||
}
|
} $else $if field.typ is ?string {
|
||||||
} $else $if field.typ is time.Time {
|
if json_name in res {
|
||||||
typ.$(field.name) = res[field.name]!.to_time()!
|
typ.$(field.name) = res[json_name]!.str()
|
||||||
} $else $if field.typ is ?time.Time {
|
}
|
||||||
if json_name in res {
|
} $else $if field.typ is time.Time {
|
||||||
typ.$(field.name) = res[field.name]!.to_time()!
|
typ.$(field.name) = res[field.name]!.to_time()!
|
||||||
|
} $else $if field.typ is ?time.Time {
|
||||||
|
if json_name in res {
|
||||||
|
typ.$(field.name) = res[field.name]!.to_time()!
|
||||||
|
}
|
||||||
|
} $else $if field.is_array {
|
||||||
|
// typ.$(field.name) = res[field.name]!.arr()
|
||||||
|
} $else $if field.is_struct {
|
||||||
|
} $else $if field.is_alias {
|
||||||
|
} $else $if field.is_map {
|
||||||
|
} $else {
|
||||||
|
return error("The type of `${field.name}` can't be decoded. Please open an issue at https://github.com/vlang/v/issues/new/choose")
|
||||||
}
|
}
|
||||||
} $else $if field.is_array {
|
|
||||||
// typ.$(field.name) = res[field.name]!.arr()
|
|
||||||
} $else $if field.is_struct {
|
|
||||||
} $else $if field.is_alias {
|
|
||||||
} $else $if field.is_map {
|
|
||||||
} $else {
|
|
||||||
return error("The type of `${field.name}` can't be decoded. Please open an issue at https://github.com/vlang/v/issues/new/choose")
|
|
||||||
}
|
}
|
||||||
|
} $else $if T is $map {
|
||||||
|
return error('Decode map is not allowed for now')
|
||||||
}
|
}
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,8 @@ mut:
|
|||||||
reg_date time.Time
|
reg_date time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// // User struct needs to be `pub mut` for now in order to access and manipulate values
|
// User struct needs to be `pub mut` for now in order to access and manipulate values
|
||||||
struct User {
|
pub struct User {
|
||||||
pub mut:
|
pub mut:
|
||||||
age int
|
age int
|
||||||
nums []int
|
nums []int
|
||||||
@ -264,3 +264,56 @@ fn test_encode_alias_field() {
|
|||||||
})
|
})
|
||||||
assert s == '{"sub":{"a":1}}'
|
assert s == '{"sub":{"a":1}}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct City {
|
||||||
|
mut:
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Country {
|
||||||
|
mut:
|
||||||
|
cities []City
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
mut:
|
||||||
|
countries []Country
|
||||||
|
users map[string]User
|
||||||
|
}
|
||||||
|
|
||||||
|
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 := 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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out := json.encode(data)
|
||||||
|
assert out == data_expected
|
||||||
|
}
|
||||||
|
@ -112,6 +112,31 @@ pub mut:
|
|||||||
pets string [json: 'pet_animals'; raw]
|
pets string [json: 'pet_animals'; raw]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_parse_user() {
|
||||||
|
s := '{"age": 10, "nums": [1,2,3], "type": 1, "lastName": "Johnson", "IsRegistered": true, "pet_animals": {"name": "Bob", "animal": "Dog"}}'
|
||||||
|
u2 := json.decode[User2](s)!
|
||||||
|
u := json.decode[User](s)!
|
||||||
|
assert u.age == 10
|
||||||
|
assert u.last_name == 'Johnson'
|
||||||
|
assert u.is_registered == true
|
||||||
|
assert u.nums.len == 3
|
||||||
|
assert u.nums[0] == 1
|
||||||
|
assert u.nums[1] == 2
|
||||||
|
assert u.nums[2] == 3
|
||||||
|
assert u.typ == 1
|
||||||
|
assert u.pets == '{"name":"Bob","animal":"Dog"}'
|
||||||
|
}
|
||||||
|
|
||||||
|
//! BUGFIX - .from_json(res)
|
||||||
|
fn test_encode_decode_time() {
|
||||||
|
user := User2{
|
||||||
|
age: 25
|
||||||
|
reg_date: time.new_time(year: 2020, month: 12, day: 22, hour: 7, minute: 23)
|
||||||
|
}
|
||||||
|
s := json.encode(user)
|
||||||
|
assert s.contains('"reg_date":1608621780')
|
||||||
|
user2 := json.decode[User2](s)!
|
||||||
|
assert user2.reg_date.str() == '2020-12-22 07:23:00'
|
||||||
struct City {
|
struct City {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user