Browse Source

Add `json:"name,omitempty"`

gingerBill 1 year ago
parent
commit
fd487f66bc
2 changed files with 60 additions and 10 deletions
  1. 56 8
      core/encoding/json/marshal.odin
  2. 4 2
      core/encoding/json/unmarshal.odin

+ 56 - 8
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
 		opt_write_end(w, opt, '}') or_return
 
 
 	case runtime.Type_Info_Struct:
 	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) {
 		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))
 			ti := runtime.type_info_base(type_info_of(v.id))
 			info := ti.variant.(runtime.Type_Info_Struct)
 			info := ti.variant.(runtime.Type_Info_Struct)
 			for name, i in info.names {
 			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
 				opt_write_iteration(w, opt, i) or_return
 				if json_name != "" {
 				if json_name != "" {
@@ -379,18 +430,15 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 				} else {
 				} else {
 					// Marshal the fields of 'using _: T' fields directly into the parent struct
 					// Marshal the fields of 'using _: T' fields directly into the parent struct
 					if info.usings[i] && name == "_" {
 					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
 						continue
 					} else {
 					} else {
 						opt_write_key(w, opt, name) or_return
 						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
 			return
 		}
 		}

+ 4 - 2
core/encoding/json/unmarshal.odin

@@ -344,10 +344,11 @@ unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_loca
 }
 }
 
 
 @(private)
 @(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
 	json_name = value
 	if comma_index := strings.index_byte(json_name, ','); comma_index >= 0 {
 	if comma_index := strings.index_byte(json_name, ','); comma_index >= 0 {
 		json_name = json_name[:comma_index]
 		json_name = json_name[:comma_index]
+		extra = json_name[comma_index:]
 	}
 	}
 	return
 	return
 }
 }
@@ -393,7 +394,8 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
 			
 			
 			for field, field_idx in fields {
 			for field, field_idx in fields {
 				tag_value := string(reflect.struct_tag_get(field.tag, "json"))
 				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
 					use_field_idx = field_idx
 					break
 					break
 				}
 				}