From db6ae6ee9ba56c17e4a04da85f02e50f1db2a8fd Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Tue, 19 Dec 2023 09:11:32 -0300 Subject: [PATCH] json: fix option sumtype handling (#20186) --- vlib/json/json_sumtype_test.v | 57 +++++++++++++++++++++++++++++++++++ vlib/v/gen/c/json.v | 40 +++++++++++++++--------- 2 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 vlib/json/json_sumtype_test.v diff --git a/vlib/json/json_sumtype_test.v b/vlib/json/json_sumtype_test.v new file mode 100644 index 00000000000000..979abf059b1fe8 --- /dev/null +++ b/vlib/json/json_sumtype_test.v @@ -0,0 +1,57 @@ +import json + +type Prices = Price | []Price + +pub struct ShopResponseData { + attributes Attributes +} + +struct Attributes { + price ?Prices +} + +struct Price { + net f64 +} + +fn test_main() { + data := '{"attributes": {"price": [{"net": 1, "_type": "Price"}, {"net": 2, "_type": "Price"}]}}' + entity := json.decode(ShopResponseData, data) or { panic(err) } + assert entity == ShopResponseData{ + attributes: Attributes{ + price: Prices([Price{ + net: 1 + }, Price{ + net: 2 + }]) + } + } + + data2 := '{"attributes": {"price": {"net": 1, "_type": "Price"}}}' + entity2 := json.decode(ShopResponseData, data2) or { panic(err) } + assert entity2 == ShopResponseData{ + attributes: Attributes{ + price: Prices(Price{ + net: 1 + }) + } + } + + data3 := json.encode(ShopResponseData{ + attributes: Attributes{ + price: Prices([Price{ + net: 1.2 + }]) + } + }) + assert data3 == '{"attributes":{"price":[{"net":1.2,"_type":"Price"}]}}' + + entity3 := json.decode(ShopResponseData, data3) or { panic(err) } + assert entity3 == ShopResponseData{ + attributes: Attributes{ + price: Prices([Price{ + net: 1.2 + }]) + } + } +} diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v index 4bc569d4ad02c3..0e526d5712731d 100644 --- a/vlib/v/gen/c/json.v +++ b/vlib/v/gen/c/json.v @@ -364,8 +364,8 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st // DECODING (inline) $if !json_no_inline_sumtypes ? { type_tmp := g.new_tmp_var() - dec.writeln('\tif (cJSON_IsObject(root)) {') - dec.writeln('\t\tcJSON* ${type_tmp} = js_get(root, "_type");') + dec.writeln('\tif (cJSON_IsObject(root) || (cJSON_IsArray(root) && cJSON_IsObject(root->child))) {') + dec.writeln('\t\tcJSON* ${type_tmp} = cJSON_IsObject(root) ? js_get(root, "_type") : js_get(root->child, "_type");') dec.writeln('\t\tif (${type_tmp} != 0) {') dec.writeln('\t\t\tchar* ${type_var} = cJSON_GetStringValue(${type_tmp});') // dec.writeln('\t\t\tcJSON_DeleteItemFromObjectCaseSensitive(root, "_type");') @@ -391,11 +391,7 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st g.definitions.writeln('static inline ${sym.cname} ${variant_typ}_to_sumtype_${sym.cname}(${variant_typ}* x);') // ENCODING - if is_option { - enc.writeln('\tif (${var_data}${field_op}_typ == ${variant.idx()}) {') - } else { - enc.writeln('\tif (val${field_op}_typ == ${variant.idx()}) {') - } + enc.writeln('\tif (${var_data}${field_op}_typ == ${variant.idx()}) {') $if json_no_inline_sumtypes ? { if variant_sym.kind == .enum_ { enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name('u64')}(*${var_data}${field_op}_${variant_typ}));') @@ -425,10 +421,16 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st } } else if variant_sym.name == 'time.Time' { enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));') - enc.writeln('\t\tcJSON_AddItemToObject(o, "value", ${js_enc_name('i64')}(val${field_op}_${variant_typ}->__v_unix));') + enc.writeln('\t\tcJSON_AddItemToObject(o, "value", ${js_enc_name('i64')}(${var_data}${field_op}_${variant_typ}->__v_unix));') } else { - enc.writeln('\t\to = ${js_enc_name(variant_typ)}(*val${field_op}_${variant_typ});') - enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));') + enc.writeln('\t\to = ${js_enc_name(variant_typ)}(*${var_data}${field_op}_${variant_typ});') + if variant_sym.kind == .array { + enc.writeln('\t\tif (cJSON_IsObject(o->child)) {') + enc.writeln('\t\t\tcJSON_AddItemToObject(o->child, "_type", cJSON_CreateString("${unmangled_variant_name}"));') + enc.writeln('\t\t}') + } else { + enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));') + } } } enc.writeln('\t}') @@ -474,13 +476,17 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st dec.writeln('\t\t\t\t${prefix}res = ${variant_typ}_to_sumtype_${sym.cname}(&${tmp});') dec.writeln('\t\t\t}') } else if !is_js_prim(variant_typ) && variant_sym.kind != .enum_ { - dec.writeln('\t\t\tif (strcmp("${unmangled_variant_name}", ${type_var}) == 0) {') + dec.writeln('\t\t\tif (strcmp("${unmangled_variant_name}", ${type_var}) == 0 && ${variant_sym.kind == .array} == cJSON_IsArray(root)) {') dec.writeln('\t\t\t\t${result_name}_${variant_typ} ${tmp} = ${js_dec_name(variant_typ)}(root);') dec.writeln('\t\t\t\tif (${tmp}.is_error) {') dec.writeln('\t\t\t\t\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = ${tmp}.err, .data = {0} };') dec.writeln('\t\t\t\t}') - dec.writeln('\t\t\t\t${prefix}res = ${variant_typ}_to_sumtype_${sym.cname}((${variant_typ}*)${tmp}.data);') + if is_option { + dec.writeln('\t\t\t\t_option_ok(&(${sym.cname}[]){ ${variant_typ}_to_sumtype_${sym.cname}((${variant_typ}*)${tmp}.data) }, (${option_name}*)&res, sizeof(${sym.cname}));') + } else { + dec.writeln('\t\t\t\t${prefix}res = ${variant_typ}_to_sumtype_${sym.cname}((${variant_typ}*)${tmp}.data);') + } dec.writeln('\t\t\t}') } } @@ -546,15 +552,21 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st 'cJSON_IsString(root->child)' } else if var_t.ends_with('bool') { 'cJSON_IsBool(root->child)' + } else if g.table.sym(g.table.value_type(ast.Type(variant_symbols[i].idx))).kind == .struct_ { + 'cJSON_IsObject(root->child)' } else { 'cJSON_IsNumber(root->child)' } dec.writeln('\t\tif (cJSON_IsArray(root) && ${judge_elem_typ}) {') dec.writeln('\t\t\t${result_name}_${var_t} ${tmp} = ${js_dec_name(var_t)}(root);') dec.writeln('\t\t\tif (${tmp}.is_error) {') - dec.writeln('\t\t\t\treturn (${result_name}_${sym.cname}){ .is_error = true, .err = ${tmp}.err, .data = {0} };') + dec.writeln('\t\t\t\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = ${tmp}.err, .data = {0} };') dec.writeln('\t\t\t}') - dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}((${var_t}*)${tmp}.data);') + if utyp.has_flag(.option) { + dec.writeln('\t\t\t_option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}((${var_t}*)${tmp}.data) }, &${prefix}res, sizeof(${sym.cname}));') + } else { + dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}((${var_t}*)${tmp}.data);') + } dec.writeln('\t\t}') }