From ef6c418eb6468b78e7154c9d417e5bf8a7fe0665 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 5 May 2020 13:23:29 +0200 Subject: [PATCH] json: decode arrays --- vlib/json/json_test.v | 10 +++++ vlib/v/gen/cgen.v | 5 ++- vlib/v/gen/fn.v | 1 + vlib/v/gen/json.v | 86 +++++++++++++++++++++++++++++-------------- 4 files changed, 72 insertions(+), 30 deletions(-) diff --git a/vlib/json/json_test.v b/vlib/json/json_test.v index 0ad8c0fb1a..a394d9bdfe 100644 --- a/vlib/json/json_test.v +++ b/vlib/json/json_test.v @@ -16,6 +16,11 @@ fn test_simple() { assert y.age == 28 } +struct User { + age int + nums []int + } + /* struct User { age int @@ -24,12 +29,15 @@ struct User { is_registered bool [json:IsRegistered] typ int [json:'type'] } +*/ fn test_parse_user() { s := '{"age": 10, "nums": [1,2,3], "type": 0, "lastName": "Johnson", "IsRegistered": true}' u := json.decode(User, s) or { exit(1) } + println(u) + /* assert u.age == 10 assert u.last_name == 'Johnson' assert u.is_registered == true @@ -38,8 +46,10 @@ fn test_parse_user() { assert u.nums[1] == 2 assert u.nums[2] == 3 assert u.typ == 0 + */ } +/* fn test_encode_user(){ usr := User{ age: 10, nums: [1,2,3], last_name: 'Johnson', is_registered: true, typ: 0} expected := '{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0}' diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index bf3af6458c..958338dd3e 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -89,6 +89,7 @@ mut: threaded_fns []string // for generating unique wrapper types and fns for `go xxx()` array_fn_definitions []string // array equality functions that have been defined is_json_fn bool // inside json.encode() + json_types []string // to avoid json gen duplicates pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name attr string is_builtin_mod bool @@ -2470,7 +2471,8 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { } else if node.expr_types[i] == table.bool_type { g.expr(expr) g.write(' ? _SLIT("true") : _SLIT("false")') - } else if node.expr_types[i].is_number() || node.expr_types[i].is_pointer() || specs[i] == `d` { + } else if node.expr_types[i].is_number() || node.expr_types[i].is_pointer() || specs[i] == + `d` { if node.expr_types[i].is_signed() && specs[i] in [`x`, `X`, `o`] { // convert to unsigned first befors C's integer propagation strikes if node.expr_types[i] == table.i8_type { @@ -3445,7 +3447,6 @@ fn (g &Gen) interface_table() string { // i.e. cctype is always just Cat, not Cat_ptr: cctype := g.cc_type(st) // Speaker_Cat_index = 0 - interface_index_name := '_${interface_name}_${cctype}_index' cast_functions.writeln(' _Interface I_${cctype}_to_Interface_${interface_name}(${cctype}* x) { diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index b9c8f12ebd..cd6eea3fd7 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -399,6 +399,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { // `json.decode(User, s)` => json.decode_User(s) sym := g.table.get_type_symbol(ast_type.typ) name += '_' + sym.name + g.gen_json_for_type(ast_type.typ) } } if node.is_c { diff --git a/vlib/v/gen/json.v b/vlib/v/gen/json.v index 79f50a97ac..abc23d0958 100644 --- a/vlib/v/gen/json.v +++ b/vlib/v/gen/json.v @@ -26,9 +26,13 @@ fn (mut g Gen) gen_json_for_type(typ table.Type) { return } if sym.kind == .array { + // return + } + if sym.name in g.json_types { return } - // println('gen_json_for_type($typ.name)') + g.json_types << sym.name + // println('gen_json_for_type($sym.name)') // decode_TYPE funcs receive an actual cJSON* object to decode // cJSON_Parse(str) call is added by the compiler // Code gen decoder @@ -52,37 +56,41 @@ Option ${dec_fn_name}(cJSON* root) { enc.writeln(' cJSON* ${enc_fn_name}($styp val) { \tcJSON *o = cJSON_CreateObject();') - // Handle arrays if sym.kind == .array { - // dec += p.decode_array(t) - // enc += p.encode_array(t) - } - // Range through fields - if !(sym.info is table.Struct) { - verror('json: $sym.name is not struct') - } - info := sym.info as table.Struct - for field in info.fields { - if field.attr == 'skip' { - continue + // Handle arrays + value_type := g.table.value_type(typ) + g.gen_json_for_type(value_type) + dec.writeln(g.decode_array(value_type)) + // enc += g.encode_array(t) + } else { + // Structs. Range through fields + if !(sym.info is table.Struct) { + verror('json: $sym.name is not struct') } - name := if field.attr.starts_with('json:') { field.attr[5..] } else { field.name } - field_type := g.typ(field.typ) - enc_name := js_enc_name(field_type) - if field.attr == 'raw' { - dec.writeln(' res->$field.name = tos2(cJSON_PrintUnformatted(' + 'js_get(root, "$name")));') - } else { - // Now generate decoders for all field types in this struct - // need to do it here so that these functions are generated first - g.gen_json_for_type(field.typ) - dec_name := js_dec_name(field_type) - if is_js_prim(field_type) { - dec.writeln(' res . $field.name = $dec_name (js_get(' + 'root, "$name"));') - } else { - dec.writeln(' $dec_name (js_get(root, "$name"), & (res . $field.name));') + info := sym.info as table.Struct + for field in info.fields { + if field.attr == 'skip' { + continue } + name := if field.attr.starts_with('json:') { field.attr[5..] } else { field.name } + field_type := g.typ(field.typ) + enc_name := js_enc_name(field_type) + if field.attr == 'raw' { + dec.writeln(' res->$field.name = tos2(cJSON_PrintUnformatted(' + 'js_get(root, "$name")));') + } else { + // Now generate decoders for all field types in this struct + // need to do it here so that these functions are generated first + g.gen_json_for_type(field.typ) + dec_name := js_dec_name(field_type) + if is_js_prim(field_type) { + dec.writeln(' res . $field.name = $dec_name (js_get(root, "$name"));') + } else { + // dec.writeln(' $dec_name (js_get(root, "$name"), & (res . $field.name));') + dec.writeln(' res . $field.name = *($field_type*) $dec_name (js_get(root,"$name")).data;') + } + } + enc.writeln('\tcJSON_AddItemToObject(o, "$name", ${enc_name}(val.$field.name));') } - enc.writeln('\tcJSON_AddItemToObject(o, "$name", ${enc_name}(val.$field.name));') } // cJSON_delete // p.cgen.fns << '$dec return opt_ok(res); \n}' @@ -106,3 +114,25 @@ fn is_js_prim(typ string) bool { return typ == 'int' || typ == 'string' || typ == 'bool' || typ == 'f32' || typ == 'f64' || typ == 'i8' || typ == 'i16' || typ == 'i64' || typ == 'u16' || typ == 'u32' || typ == 'u64' } + +fn (mut g Gen) decode_array(value_type table.Type) string { + styp := g.typ(value_type) + fn_name := js_dec_name(styp) + // If we have `[]Profile`, have to register a Profile en(de)coder first + g.gen_json_for_type(value_type) + mut s := '' + if is_js_prim(styp) { + s = '$styp val = ${fn_name}(jsval); ' + } else { + s = '\t$styp val; ${fn_name}(jsval, &val); ' + } + return ' +res = __new_array(0, 0, sizeof($styp)); +const cJSON *jsval = NULL; +cJSON_ArrayForEach(jsval, root) +{ +$s + array_push(&res, &val); +} +' +}