diff --git a/vlib/x/json2/decoder.v b/vlib/x/json2/decoder.v index d255a5430bc13a..024ba80ee48810 100644 --- a/vlib/x/json2/decoder.v +++ b/vlib/x/json2/decoder.v @@ -142,113 +142,120 @@ fn decode_struct[T](_ T, res map[string]Any) !T { mut typ := T{} $if T is $struct { $for field in T.fields { + mut skip_field := false mut json_name := field.name + for attr in field.attrs { if attr.contains('json: ') { json_name = attr.replace('json: ', '') + if json_name == '-' { + skip_field = true + } break } } - $if field.is_enum { - if v := res[json_name] { - typ.$(field.name) = v.int() - } else { - $if field.is_option { - typ.$(field.name) = none + if !skip_field { + $if field.is_enum { + if v := res[json_name] { + typ.$(field.name) = v.int() + } else { + $if field.is_option { + typ.$(field.name) = none + } } - } - } $else $if field.typ is u8 { - typ.$(field.name) = res[json_name]!.u64() - } $else $if field.typ is u16 { - typ.$(field.name) = res[json_name]!.u64() - } $else $if field.typ is u32 { - typ.$(field.name) = res[json_name]!.u64() - } $else $if field.typ is u64 { - typ.$(field.name) = res[json_name]!.u64() - } $else $if field.typ is int { - typ.$(field.name) = res[json_name]!.int() - } $else $if field.typ is i8 { - typ.$(field.name) = res[json_name]!.int() - } $else $if field.typ is i16 { - typ.$(field.name) = res[json_name]!.int() - } $else $if field.typ is i32 { - typ.$(field.name) = i32(res[field.name]!.int()) - } $else $if field.typ is i64 { - typ.$(field.name) = res[json_name]!.i64() - } $else $if field.typ is ?u8 { - if json_name in res { - typ.$(field.name) = ?u8(res[json_name]!.i64()) - } - } $else $if field.typ is ?i8 { - if json_name in res { - typ.$(field.name) = ?i8(res[json_name]!.i64()) - } - } $else $if field.typ is ?u16 { - if json_name in res { - typ.$(field.name) = ?u16(res[json_name]!.i64()) - } - } $else $if field.typ is ?i16 { - if json_name in res { - typ.$(field.name) = ?i16(res[json_name]!.i64()) - } - } $else $if field.typ is ?u32 { - if json_name in res { - typ.$(field.name) = ?u32(res[json_name]!.i64()) - } - } $else $if field.typ is ?i32 { - if json_name in res { - typ.$(field.name) = ?i32(res[json_name]!.i64()) - } - } $else $if field.typ is ?u64 { - if json_name in res { - typ.$(field.name) = ?u64(res[json_name]!.i64()) - } - } $else $if field.typ is ?i64 { - if json_name in res { - typ.$(field.name) = ?i64(res[json_name]!.i64()) - } - } $else $if field.typ is ?int { - if json_name in res { - typ.$(field.name) = ?int(res[json_name]!.i64()) - } - } $else $if field.typ is f32 { - typ.$(field.name) = res[json_name]!.f32() - } $else $if field.typ is ?f32 { - if json_name in res { + } $else $if field.typ is u8 { + typ.$(field.name) = res[json_name]!.u64() + } $else $if field.typ is u16 { + typ.$(field.name) = res[json_name]!.u64() + } $else $if field.typ is u32 { + typ.$(field.name) = res[json_name]!.u64() + } $else $if field.typ is u64 { + typ.$(field.name) = res[json_name]!.u64() + } $else $if field.typ is int { + typ.$(field.name) = res[json_name]!.int() + } $else $if field.typ is i8 { + typ.$(field.name) = res[json_name]!.int() + } $else $if field.typ is i16 { + typ.$(field.name) = res[json_name]!.int() + } $else $if field.typ is i32 { + typ.$(field.name) = i32(res[field.name]!.int()) + } $else $if field.typ is i64 { + typ.$(field.name) = res[json_name]!.i64() + } $else $if field.typ is ?u8 { + if json_name in res { + typ.$(field.name) = ?u8(res[json_name]!.i64()) + } + } $else $if field.typ is ?i8 { + if json_name in res { + typ.$(field.name) = ?i8(res[json_name]!.i64()) + } + } $else $if field.typ is ?u16 { + if json_name in res { + typ.$(field.name) = ?u16(res[json_name]!.i64()) + } + } $else $if field.typ is ?i16 { + if json_name in res { + typ.$(field.name) = ?i16(res[json_name]!.i64()) + } + } $else $if field.typ is ?u32 { + if json_name in res { + typ.$(field.name) = ?u32(res[json_name]!.i64()) + } + } $else $if field.typ is ?i32 { + if json_name in res { + typ.$(field.name) = ?i32(res[json_name]!.i64()) + } + } $else $if field.typ is ?u64 { + if json_name in res { + typ.$(field.name) = ?u64(res[json_name]!.i64()) + } + } $else $if field.typ is ?i64 { + if json_name in res { + typ.$(field.name) = ?i64(res[json_name]!.i64()) + } + } $else $if field.typ is ?int { + if json_name in res { + typ.$(field.name) = ?int(res[json_name]!.i64()) + } + } $else $if field.typ is f32 { typ.$(field.name) = res[json_name]!.f32() - } - } $else $if field.typ is f64 { - typ.$(field.name) = res[json_name]!.f64() - } $else $if field.typ is ?f64 { - if json_name in res { + } $else $if field.typ is ?f32 { + if json_name in res { + typ.$(field.name) = res[json_name]!.f32() + } + } $else $if field.typ is f64 { typ.$(field.name) = res[json_name]!.f64() - } - } $else $if field.typ is bool { - typ.$(field.name) = res[json_name]!.bool() - } $else $if field.typ is ?bool { - if json_name in res { + } $else $if field.typ is ?f64 { + if json_name in res { + typ.$(field.name) = res[json_name]!.f64() + } + } $else $if field.typ is bool { typ.$(field.name) = res[json_name]!.bool() - } - } $else $if field.typ is string { - typ.$(field.name) = res[json_name]!.str() - } $else $if field.typ is ?string { - if json_name in res { + } $else $if field.typ is ?bool { + if json_name in res { + typ.$(field.name) = res[json_name]!.bool() + } + } $else $if field.typ is string { typ.$(field.name) = res[json_name]!.str() - } - } $else $if field.typ is time.Time { - typ.$(field.name) = res[json_name]!.to_time()! - } $else $if field.typ is ?time.Time { - if json_name in res { + } $else $if field.typ is ?string { + if json_name in res { + typ.$(field.name) = res[json_name]!.str() + } + } $else $if field.typ is time.Time { typ.$(field.name) = res[json_name]!.to_time()! + } $else $if field.typ is ?time.Time { + if json_name in res { + typ.$(field.name) = res[json_name]!.to_time()! + } + } $else $if field.is_array { + } $else $if field.is_struct { + typ.$(field.name) = decode_struct(typ.$(field.name), res[field.name]!.as_map())! + } $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 { - } $else $if field.is_struct { - typ.$(field.name) = decode_struct(typ.$(field.name), res[field.name]!.as_map())! - } $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 { diff --git a/vlib/x/json2/encoder.v b/vlib/x/json2/encoder.v index 40049c7a264f87..b95ebaf990b52d 100644 --- a/vlib/x/json2/encoder.v +++ b/vlib/x/json2/encoder.v @@ -187,135 +187,147 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! { } $for field in U.fields { mut ignore_field := false + mut skip_field := false value := val.$(field.name) is_nil := val.$(field.name).str() == '&nil' mut json_name := '' + for attr in field.attrs { if attr.contains('json: ') { json_name = attr.replace('json: ', '') + if json_name == '-' { + ignore_field = true + skip_field = true + } break } } - $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)! - } else { - e.encode_string(field.name, mut buf)! - } - buf << json2.colon_rune + if skip_field { + i++ + fields_len-- + } else { + $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)! + } else { + e.encode_string(field.name, mut buf)! + } + buf << json2.colon_rune - if e.newline != 0 { - buf << ` ` - } - e.encode_value_with_level(value, level, mut buf)! - } else { - ignore_field = true - } - } $else { - 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 != '' { - e.encode_string(json_name, mut buf)! + if e.newline != 0 { + buf << ` ` + } + e.encode_value_with_level(value, level, mut buf)! } else { - e.encode_string(field.name, mut buf)! + ignore_field = true } - buf << json2.colon_rune - - if e.newline != 0 { - buf << ` ` - } - } - - $if field.indirections != 0 { - if val.$(field.name) != unsafe { nil } { - $if field.indirections == 1 { - e.encode_value_with_level(*val.$(field.name), level + 1, mut buf)! - } - $if field.indirections == 2 { - e.encode_value_with_level(**val.$(field.name), level + 1, mut - buf)! + } $else { + 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 != '' { + e.encode_string(json_name, mut buf)! + } else { + e.encode_string(field.name, mut buf)! } - $if field.indirections == 3 { - e.encode_value_with_level(***val.$(field.name), level + 1, mut - buf)! + buf << json2.colon_rune + + if e.newline != 0 { + buf << ` ` } } - } $else $if field.typ is string { - e.encode_string(val.$(field.name).str(), mut buf)! - } $else $if field.typ is time.Time { - str_value := val.$(field.name).format_rfc3339() - buf << json2.quote_rune - unsafe { buf.push_many(str_value.str, str_value.len) } - buf << json2.quote_rune - } $else $if field.typ is bool { - if value { - 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 in [$float, $int] { - str_value := val.$(field.name).str() - unsafe { buf.push_many(str_value.str, str_value.len) } - } $else $if field.is_array { - // TODO - replace for `field.typ is $array` - e.encode_array(value, level + 1, mut buf)! - } $else $if field.typ is $array { - // e.encode_array(value, level + 1, mut buf)! // FIXME - error: could not infer generic type `U` in call to `encode_array` - } $else $if field.typ is $struct { - e.encode_struct(value, level + 1, mut buf)! - } $else $if field.is_map { - e.encode_map(value, level + 1, mut buf)! - } $else $if field.is_enum { - // TODO - replace for `field.typ is $enum` - // str_value := int(val.$(field.name)).str() - // unsafe { buf.push_many(str_value.str, str_value.len) } - e.encode_value_with_level(val.$(field.name), level + 1, mut buf)! - } $else $if field.typ is $enum { - } $else $if field.typ is $sumtype { - field_value := val.$(field.name) - if field_value.str() != 'unknown sum type value' { - $for v in field_value.variants { - if field_value is v { - e.encode_value_with_level(field_value, level, mut buf)! + + $if field.indirections != 0 { + if val.$(field.name) != unsafe { nil } { + $if field.indirections == 1 { + e.encode_value_with_level(*val.$(field.name), level + 1, mut + buf)! + } + $if field.indirections == 2 { + e.encode_value_with_level(**val.$(field.name), level + 1, mut + buf)! + } + $if field.indirections == 3 { + e.encode_value_with_level(***val.$(field.name), level + 1, mut + buf)! } } - } - } $else $if field.typ is $alias { - $if field.unaliased_typ is string { + } $else $if field.typ is string { e.encode_string(val.$(field.name).str(), mut buf)! - } $else $if field.unaliased_typ is time.Time { - parsed_time := time.parse(val.$(field.name).str()) or { time.Time{} } - e.encode_string(parsed_time.format_rfc3339(), mut buf)! - } $else $if field.unaliased_typ is bool { - if val.$(field.name).str() == json2.true_in_string { + } $else $if field.typ is time.Time { + str_value := val.$(field.name).format_rfc3339() + buf << json2.quote_rune + unsafe { buf.push_many(str_value.str, str_value.len) } + buf << json2.quote_rune + } $else $if field.typ is bool { + if value { 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.unaliased_typ in [$float, $int] { + } $else $if field.typ in [$float, $int] { str_value := val.$(field.name).str() unsafe { buf.push_many(str_value.str, str_value.len) } - } $else $if field.unaliased_typ is $array { - // TODO - } $else $if field.unaliased_typ is $struct { + } $else $if field.is_array { + // TODO - replace for `field.typ is $array` + e.encode_array(value, level + 1, mut buf)! + } $else $if field.typ is $array { + // e.encode_array(value, level + 1, mut buf)! // FIXME - error: could not infer generic type `U` in call to `encode_array` + } $else $if field.typ is $struct { e.encode_struct(value, level + 1, mut buf)! - } $else $if field.unaliased_typ is $enum { - // TODO - } $else $if field.unaliased_typ is $sumtype { - // TODO + } $else $if field.is_map { + e.encode_map(value, level + 1, mut buf)! + } $else $if field.is_enum { + // TODO - replace for `field.typ is $enum` + // str_value := int(val.$(field.name)).str() + // unsafe { buf.push_many(str_value.str, str_value.len) } + e.encode_value_with_level(val.$(field.name), level + 1, mut buf)! + } $else $if field.typ is $enum { + } $else $if field.typ is $sumtype { + field_value := val.$(field.name) + if field_value.str() != 'unknown sum type value' { + $for v in field_value.variants { + if field_value is v { + e.encode_value_with_level(field_value, level, mut buf)! + } + } + } + } $else $if field.typ is $alias { + $if field.unaliased_typ is string { + e.encode_string(val.$(field.name).str(), mut buf)! + } $else $if field.unaliased_typ is time.Time { + parsed_time := time.parse(val.$(field.name).str()) or { time.Time{} } + e.encode_string(parsed_time.format_rfc3339(), mut buf)! + } $else $if field.unaliased_typ is bool { + if val.$(field.name).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.unaliased_typ in [$float, $int] { + str_value := val.$(field.name).str() + unsafe { buf.push_many(str_value.str, str_value.len) } + } $else $if field.unaliased_typ is $array { + // TODO + } $else $if field.unaliased_typ is $struct { + e.encode_struct(value, level + 1, mut buf)! + } $else $if field.unaliased_typ is $enum { + // TODO + } $else $if field.unaliased_typ is $sumtype { + // TODO + } $else { + return error('the alias ${typeof(val).name} cannot be encoded') + } } $else { - return error('the alias ${typeof(val).name} cannot be encoded') + return error('type ${typeof(val).name} cannot be array encoded') } - } $else { - return error('type ${typeof(val).name} cannot be array encoded') } } diff --git a/vlib/x/json2/tests/decode_struct_test.v b/vlib/x/json2/tests/decode_struct_test.v index 7327570cd17b81..6cac67414bd624 100644 --- a/vlib/x/json2/tests/decode_struct_test.v +++ b/vlib/x/json2/tests/decode_struct_test.v @@ -45,6 +45,35 @@ 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: '-'] +} + fn test_types() { assert json.decode[StructType[string]]('{"val": ""}')!.val == '' assert json.decode[StructType[string]]('{"val": "0"}')!.val == '0' @@ -126,3 +155,38 @@ fn test_types() { 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 + } +} diff --git a/vlib/x/json2/tests/encode_struct_test.v b/vlib/x/json2/tests/encode_struct_test.v index 7c4f782a8eac5d..f06a569cbc7340 100644 --- a/vlib/x/json2/tests/encode_struct_test.v +++ b/vlib/x/json2/tests/encode_struct_test.v @@ -44,6 +44,30 @@ 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: '-'] +} + fn test_types() { assert json.encode(StructType[string]{}) == '{"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]]}' } +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() { assert json.encode(StructType[StringAlias]{}) == '{"val":""}' assert json.encode(StructType[StringAlias]{ val: '' }) == '{"val":""}'