mirror of
https://github.com/vlang/v.git
synced 2025-09-15 23:42:28 +03:00
json: use @[required]
to disallow parsing nulls (#23218)
This commit is contained in:
parent
1ca902fcc4
commit
63fff1dcd4
3 changed files with 64 additions and 11 deletions
|
@ -4692,7 +4692,7 @@ struct User {
|
||||||
last_name string @[json: lastName]
|
last_name string @[json: lastName]
|
||||||
}
|
}
|
||||||
|
|
||||||
data := '{ "name": "Frodo", "lastName": "Baggins", "age": 25 }'
|
data := '{ "name": "Frodo", "lastName": "Baggins", "age": 25, "nullable": null }'
|
||||||
user := json.decode(User, data) or {
|
user := json.decode(User, data) or {
|
||||||
eprintln('Failed to decode json, error: ${err}')
|
eprintln('Failed to decode json, error: ${err}')
|
||||||
return
|
return
|
||||||
|
|
|
@ -516,12 +516,9 @@ fn test_encode_alias_field() {
|
||||||
a: 1
|
a: 1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
println(s)
|
|
||||||
assert s == '{"sub":{"a":1}}'
|
assert s == '{"sub":{"a":1}}'
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
struct APrice {}
|
struct APrice {}
|
||||||
|
|
||||||
struct Association {
|
struct Association {
|
||||||
|
@ -538,3 +535,55 @@ fn test_encoding_struct_with_pointers() {
|
||||||
}
|
}
|
||||||
assert json.encode(value) == '{"association":{"price":{}},"price":{}}'
|
assert json.encode(value) == '{"association":{"price":{}},"price":{}}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ChildNullish {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NullishStruct {
|
||||||
|
name string
|
||||||
|
lastname string
|
||||||
|
age int
|
||||||
|
salary f32
|
||||||
|
child ChildNullish
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RequiredStruct {
|
||||||
|
name string @[required]
|
||||||
|
lastname string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_required() {
|
||||||
|
nullish_one := json.decode(NullishStruct, '{"name":"Peter", "lastname":null, "age":28,"salary":95000.5,"title":"worker"}')!
|
||||||
|
assert nullish_one.name == 'Peter'
|
||||||
|
assert nullish_one.lastname == ''
|
||||||
|
assert nullish_one.age == 28
|
||||||
|
assert nullish_one.salary == 95000.5
|
||||||
|
assert nullish_one.child.name == ''
|
||||||
|
|
||||||
|
nullish_two := json.decode(NullishStruct, '{"name":"Peter", "lastname": "Parker", "age":28,"salary":95000.5,"title":"worker"}')!
|
||||||
|
assert nullish_two.name == 'Peter'
|
||||||
|
assert nullish_two.lastname == 'Parker'
|
||||||
|
assert nullish_two.age == 28
|
||||||
|
assert nullish_two.salary == 95000.5
|
||||||
|
assert nullish_two.child.name == ''
|
||||||
|
|
||||||
|
nullish_third := json.decode(NullishStruct, '{"name":"Peter", "age":28,"salary":95000.5,"title":"worker", "child": {"name":"Nullish"}}')!
|
||||||
|
assert nullish_third.name == 'Peter'
|
||||||
|
assert nullish_third.lastname == ''
|
||||||
|
assert nullish_third.age == 28
|
||||||
|
assert nullish_third.salary == 95000.5
|
||||||
|
assert nullish_third.child.name == 'Nullish'
|
||||||
|
|
||||||
|
required_struct := json.decode(RequiredStruct, '{"name":"Peter", "lastname": "Parker"}')!
|
||||||
|
assert required_struct.name == 'Peter'
|
||||||
|
assert required_struct.lastname == 'Parker'
|
||||||
|
|
||||||
|
required_struct_err := json.decode(RequiredStruct, '{"name": null, "lastname": "Parker"}') or {
|
||||||
|
assert err.msg() == "type mismatch for field 'name', expecting `string` type, got: null"
|
||||||
|
RequiredStruct{
|
||||||
|
name: 'Peter'
|
||||||
|
lastname: 'Parker'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -627,8 +627,12 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) gen_prim_type_validation(name string, typ ast.Type, tmp string, ret_styp string, mut dec strings.Builder) {
|
fn (mut g Gen) gen_prim_type_validation(name string, typ ast.Type, tmp string, is_required bool, ret_styp string, mut dec strings.Builder) {
|
||||||
none_check := if typ.has_flag(.option) { 'cJSON_IsNull(jsonroot_${tmp}) || ' } else { '' }
|
none_check := if !is_required {
|
||||||
|
'cJSON_IsNull(jsonroot_${tmp}) || '
|
||||||
|
} else {
|
||||||
|
''
|
||||||
|
}
|
||||||
type_check := if typ.is_int() || typ.is_float() {
|
type_check := if typ.is_int() || typ.is_float() {
|
||||||
'${none_check}cJSON_IsNumber(jsonroot_${tmp}) || (cJSON_IsString(jsonroot_${tmp}) && strlen(jsonroot_${tmp}->valuestring))'
|
'${none_check}cJSON_IsNumber(jsonroot_${tmp}) || (cJSON_IsString(jsonroot_${tmp}) && strlen(jsonroot_${tmp}->valuestring))'
|
||||||
} else if typ.is_string() {
|
} else if typ.is_string() {
|
||||||
|
@ -724,7 +728,7 @@ fn (mut g Gen) gen_struct_enc_dec(utyp ast.Type, type_info ast.TypeInfo, styp st
|
||||||
if utyp.has_flag(.option) {
|
if utyp.has_flag(.option) {
|
||||||
dec.writeln('\t\tres.state = 0;')
|
dec.writeln('\t\tres.state = 0;')
|
||||||
}
|
}
|
||||||
g.gen_prim_type_validation(field.name, field.typ, tmp, '${result_name}_${styp}', mut
|
g.gen_prim_type_validation(field.name, field.typ, tmp, is_required, '${result_name}_${styp}', mut
|
||||||
dec)
|
dec)
|
||||||
dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${dec_name}(jsonroot_${tmp});')
|
dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${dec_name}(jsonroot_${tmp});')
|
||||||
if field.has_default_expr {
|
if field.has_default_expr {
|
||||||
|
@ -791,8 +795,8 @@ fn (mut g Gen) gen_struct_enc_dec(utyp ast.Type, type_info ast.TypeInfo, styp st
|
||||||
tmp := g.new_tmp_var()
|
tmp := g.new_tmp_var()
|
||||||
gen_js_get(styp, tmp, name, mut dec, is_required)
|
gen_js_get(styp, tmp, name, mut dec, is_required)
|
||||||
dec.writeln('\tif (jsonroot_${tmp}) {')
|
dec.writeln('\tif (jsonroot_${tmp}) {')
|
||||||
g.gen_prim_type_validation(field.name, parent_type, tmp, '${result_name}_${styp}', mut
|
g.gen_prim_type_validation(field.name, parent_type, tmp, is_required,
|
||||||
dec)
|
'${result_name}_${styp}', mut dec)
|
||||||
dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${parent_dec_name} (jsonroot_${tmp});')
|
dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${parent_dec_name} (jsonroot_${tmp});')
|
||||||
if field.has_default_expr {
|
if field.has_default_expr {
|
||||||
dec.writeln('\t} else {')
|
dec.writeln('\t} else {')
|
||||||
|
@ -832,8 +836,8 @@ fn (mut g Gen) gen_struct_enc_dec(utyp ast.Type, type_info ast.TypeInfo, styp st
|
||||||
gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required)
|
gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required)
|
||||||
dec.writeln('\tif (jsonroot_${tmp}) {')
|
dec.writeln('\tif (jsonroot_${tmp}) {')
|
||||||
if is_js_prim(g.styp(field.typ.clear_option_and_result())) {
|
if is_js_prim(g.styp(field.typ.clear_option_and_result())) {
|
||||||
g.gen_prim_type_validation(field.name, field.typ, tmp, '${result_name}_${styp}', mut
|
g.gen_prim_type_validation(field.name, field.typ, tmp, is_required,
|
||||||
dec)
|
'${result_name}_${styp}', mut dec)
|
||||||
}
|
}
|
||||||
if field.typ.has_flag(.option) {
|
if field.typ.has_flag(.option) {
|
||||||
dec.writeln('\t\tvmemcpy(&${prefix}${op}${c_name(field.name)}, (${field_type}*)${tmp}.data, sizeof(${field_type}));')
|
dec.writeln('\t\tvmemcpy(&${prefix}${op}${c_name(field.name)}, (${field_type}*)${tmp}.data, sizeof(${field_type}));')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue