json: decode arrays

pull/4729/head
Alexander Medvednikov 2020-05-05 13:23:29 +02:00
parent 869444cec6
commit ef6c418eb6
4 changed files with 72 additions and 30 deletions

View File

@ -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}'

View File

@ -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) {

View File

@ -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 {

View File

@ -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);
}
'
}