x.json2: add skippable field attr @[json: '-'] (improve backwards compatibility with the json module) (#20892)

This commit is contained in:
johnpgr 2024-02-26 16:05:38 -03:00 committed by GitHub
parent 71bd94aa9e
commit c6048d50bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 322 additions and 192 deletions

View File

@ -142,14 +142,20 @@ fn decode_struct[T](_ T, res map[string]Any) !T {
mut typ := T{} mut typ := T{}
$if T is $struct { $if T is $struct {
$for field in T.fields { $for field in T.fields {
mut skip_field := false
mut json_name := field.name mut json_name := field.name
for attr in field.attrs { for attr in field.attrs {
if attr.contains('json: ') { if attr.contains('json: ') {
json_name = attr.replace('json: ', '') json_name = attr.replace('json: ', '')
if json_name == '-' {
skip_field = true
}
break break
} }
} }
if !skip_field {
$if field.is_enum { $if field.is_enum {
if v := res[json_name] { if v := res[json_name] {
typ.$(field.name) = v.int() typ.$(field.name) = v.int()
@ -251,6 +257,7 @@ fn decode_struct[T](_ T, res map[string]Any) !T {
return error("The type of `${field.name}` can't be decoded. Please open an issue at https://github.com/vlang/v/issues/new/choose") 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 { } $else $if T is $map {
for k, v in res { for k, v in res {
// // TODO - make this work to decode types like `map[string]StructType[bool]` // // TODO - make this work to decode types like `map[string]StructType[bool]`

View File

@ -187,19 +187,29 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
} }
$for field in U.fields { $for field in U.fields {
mut ignore_field := false mut ignore_field := false
mut skip_field := false
value := val.$(field.name) value := val.$(field.name)
is_nil := val.$(field.name).str() == '&nil' is_nil := val.$(field.name).str() == '&nil'
mut json_name := '' mut json_name := ''
for attr in field.attrs { for attr in field.attrs {
if attr.contains('json: ') { if attr.contains('json: ') {
json_name = attr.replace('json: ', '') json_name = attr.replace('json: ', '')
if json_name == '-' {
ignore_field = true
skip_field = true
}
break break
} }
} }
if skip_field {
i++
fields_len--
} else {
$if value is $option { $if value is $option {
workaround := val.$(field.name) workaround := val.$(field.name)
if workaround != none { // smartcast if workaround != none { // smartcast
@ -237,7 +247,8 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
$if field.indirections != 0 { $if field.indirections != 0 {
if val.$(field.name) != unsafe { nil } { if val.$(field.name) != unsafe { nil } {
$if field.indirections == 1 { $if field.indirections == 1 {
e.encode_value_with_level(*val.$(field.name), level + 1, mut buf)! e.encode_value_with_level(*val.$(field.name), level + 1, mut
buf)!
} }
$if field.indirections == 2 { $if field.indirections == 2 {
e.encode_value_with_level(**val.$(field.name), level + 1, mut e.encode_value_with_level(**val.$(field.name), level + 1, mut
@ -318,6 +329,7 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
return error('type ${typeof(val).name} cannot be array encoded') return error('type ${typeof(val).name} cannot be array encoded')
} }
} }
}
if i < fields_len - 1 && !ignore_field { if i < fields_len - 1 && !ignore_field {
if !is_nil { if !is_nil {

View File

@ -45,6 +45,35 @@ mut:
val &T 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: '-']
}
fn test_types() { fn test_types() {
assert json.decode[StructType[string]]('{"val": ""}')!.val == '' assert json.decode[StructType[string]]('{"val": ""}')!.val == ''
assert json.decode[StructType[string]]('{"val": "0"}')!.val == '0' assert json.decode[StructType[string]]('{"val": "0"}')!.val == '0'
@ -126,3 +155,38 @@ fn test_types() {
assert true 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
}
}

View File

@ -44,6 +44,30 @@ mut:
val &T 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: '-']
}
fn test_types() { fn test_types() {
assert json.encode(StructType[string]{}) == '{"val":""}' assert json.encode(StructType[string]{}) == '{"val":""}'
assert json.encode(StructType[string]{ val: '' }) == '{"val":""}' assert json.encode(StructType[string]{ val: '' }) == '{"val":""}'
@ -211,6 +235,29 @@ fn test_option_array() {
// }) == '{"val":[[0,1],[0,2,3],[2],[5,1]]}' // }) == '{"val":[[0,1],[0,2,3],[2],[5,1]]}'
} }
fn test_skipped_fields() {
assert json.encode(StructTypeSkippedFields[string]{
val: ''
val1: ''
val2: ''
val3: ''
}) == '{"val1":"","val3":""}'
assert json.encode(StructTypeSkippedFields2[string]{
val: ''
val1: ''
val2: ''
val3: ''
}) == '{"val":"","val2":""}'
assert json.encode(StructTypeSkippedFields3[string]{
val: ''
val1: ''
val2: ''
val3: ''
}) == '{}'
}
fn test_alias() { fn test_alias() {
assert json.encode(StructType[StringAlias]{}) == '{"val":""}' assert json.encode(StructType[StringAlias]{}) == '{"val":""}'
assert json.encode(StructType[StringAlias]{ val: '' }) == '{"val":""}' assert json.encode(StructType[StringAlias]{ val: '' }) == '{"val":""}'