diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 6ec9c0d27..3d57316b3 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -366,12 +366,63 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: opt_write_end(w, opt, '}') or_return case runtime.Type_Info_Struct: + is_omitempty :: proc(v: any) -> bool { + v := v + if v == nil { + return true + } + ti := runtime.type_info_core(type_info_of(v.id)) + #partial switch info in ti.variant { + case runtime.Type_Info_String: + switch x in v { + case string: + return x == "" + case cstring: + return x == nil || x == "" + } + case runtime.Type_Info_Any: + return v.(any) == nil + case runtime.Type_Info_Type_Id: + return v.(typeid) == nil + case runtime.Type_Info_Pointer, + runtime.Type_Info_Multi_Pointer, + runtime.Type_Info_Procedure: + return (^rawptr)(v.data)^ == nil + case runtime.Type_Info_Dynamic_Array: + return (^runtime.Raw_Dynamic_Array)(v.data).len == 0 + case runtime.Type_Info_Slice: + return (^runtime.Raw_Slice)(v.data).len == 0 + case runtime.Type_Info_Union, + runtime.Type_Info_Bit_Set, + runtime.Type_Info_Soa_Pointer: + return reflect.is_nil(v) + case runtime.Type_Info_Map: + return (^runtime.Raw_Map)(v.data).len == 0 + } + return false + } + marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) { ti := runtime.type_info_base(type_info_of(v.id)) info := ti.variant.(runtime.Type_Info_Struct) for name, i in info.names { - json_name := reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json") - json_name = json_name_from_tag_value(json_name) + omitempty := false + + json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json")) + for flag in strings.split_iterator(&extra, ",") { + switch flag { + case "omitempty": + omitempty = true + } + } + + id := info.types[i].id + data := rawptr(uintptr(v.data) + info.offsets[i]) + the_value := any{data, id} + + if is_omitempty(the_value) { + continue + } opt_write_iteration(w, opt, i) or_return if json_name != "" { @@ -379,18 +430,15 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: } else { // Marshal the fields of 'using _: T' fields directly into the parent struct if info.usings[i] && name == "_" { - id := info.types[i].id - data := rawptr(uintptr(v.data) + info.offsets[i]) - marshal_struct_fields(w, any{data, id}, opt) or_return + marshal_struct_fields(w, the_value, opt) or_return continue } else { opt_write_key(w, opt, name) or_return } } - id := info.types[i].id - data := rawptr(uintptr(v.data) + info.offsets[i]) - marshal_to_writer(w, any{data, id}, opt) or_return + + marshal_to_writer(w, the_value, opt) or_return } return } diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 7a5679368..b2052e43c 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -344,10 +344,11 @@ unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_loca } @(private) -json_name_from_tag_value :: proc(value: string) -> (json_name: string) { +json_name_from_tag_value :: proc(value: string) -> (json_name, extra: string) { json_name = value if comma_index := strings.index_byte(json_name, ','); comma_index >= 0 { json_name = json_name[:comma_index] + extra = json_name[comma_index:] } return } @@ -393,7 +394,8 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm for field, field_idx in fields { tag_value := string(reflect.struct_tag_get(field.tag, "json")) - if key == json_name_from_tag_value(tag_value) { + json_name, _ := json_name_from_tag_value(tag_value) + if key == json_name { use_field_idx = field_idx break }