Skip to content

Commit

Permalink
json: fix option sumtype handling (#20186)
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp committed Dec 19, 2023
1 parent d474f69 commit db6ae6e
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 14 deletions.
57 changes: 57 additions & 0 deletions 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
}])
}
}
}
40 changes: 26 additions & 14 deletions vlib/v/gen/c/json.v
Expand Up @@ -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");')
Expand All @@ -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}));')
Expand Down Expand Up @@ -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}')
Expand Down Expand Up @@ -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}')
}
}
Expand Down Expand Up @@ -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}')
}

Expand Down

0 comments on commit db6ae6e

Please sign in to comment.