diff --git a/README.md b/README.md index 4c5eaa8..c0b5559 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ v := struct { Name: "John", } -parsed := Parse(v) +parsed, err := Parse(v) ``` The `parsed` variable will now contain a map with the same fields as the input struct: @@ -35,7 +35,7 @@ map[string]interface{}{ The output format of the `Parse` function depends on the input type: -- Struct: the output is a map with the same fields as the input struct. The keys of the map are the field names, and the values are the field types represented as strings. +- Struct: the output is a map with the same fields as the input struct. The keys of the map are the field names (if `json` tags is being used, the name attribute will be the key), and the values are the field types represented as strings. - Map: the output is a map with the same keys as the input map. The values of the map are the corresponding types of the input map values, represented as strings. @@ -50,16 +50,16 @@ Here are some examples of input and output for the `Parse` function: ```go // Struct v := struct { - Age int + Age int `json:"age"` Name string }{ Age: 30, Name: "John", } -parsed := Parse(v) +parsed, err := Parse(v) // parsed = map[string]interface{}{ -// "Age": "int", +// "age": "int", // "Name": "string", // } @@ -69,7 +69,7 @@ v := map[string]int{ "bar": 69, } -parsed := Parse(v) +parsed, err := Parse(v) // parsed = map[string]interface{}{ // "foo": "int", // "bar": "int", @@ -81,7 +81,7 @@ v := []interface{}{ 42, } -parsed := Parse(v) +parsed, err := Parse(v) // parsed = []interface{}{ // "string", // "int", @@ -91,6 +91,6 @@ parsed := Parse(v) // Primitive types v := true -parsed := Parse(v) +parsed, err := Parse(v) // parsed = "bool" ``` \ No newline at end of file diff --git a/go.mod b/go.mod index 64b3d5d..80c7db7 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/claudioluciano/simple_json_schema_like go 1.18 + +require github.com/fatih/structtag v1.2.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e2c02dc --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= diff --git a/parser.go b/parser.go index c71c62c..a005bd7 100644 --- a/parser.go +++ b/parser.go @@ -3,6 +3,8 @@ package simplejsonschemalike import ( "fmt" "reflect" + + "github.com/fatih/structtag" ) const timeStringKind = "time.Time" @@ -23,17 +25,16 @@ const timeStringKind = "time.Time" // // The function returns an interface{} value that can be asserted to the appropriate type // after the function call. -func Parse(in interface{}) interface{} { +func Parse(in interface{}) (interface{}, error) { v := reflect.ValueOf(in) - return parse(v) } -func parse(v reflect.Value) interface{} { +func parse(v reflect.Value) (interface{}, error) { switch v.Kind() { case reflect.Struct: if ok := isTime(v); ok { - return "date-time" + return "date-time", nil } return parseStruct(v) @@ -46,7 +47,7 @@ func parse(v reflect.Value) interface{} { case reflect.Slice, reflect.Array: return parseSlice(v) default: - return v.Type().String() + return v.Type().String(), nil } } @@ -58,7 +59,7 @@ func parseNil(v reflect.Value) interface{} { return v.Type().String() } -func parseStruct(v reflect.Value) map[string]interface{} { +func parseStruct(v reflect.Value) (map[string]interface{}, error) { m := make(map[string]interface{}) for i := 0; i < v.NumField(); i++ { @@ -68,38 +69,72 @@ func parseStruct(v reflect.Value) map[string]interface{} { continue } - m[field.Name] = parse(v.Field(i)) + name := field.Name + tagName, err := getNameFromTag(field) + if err != nil { + return nil, err + } + if tagName != "" { + name = tagName + } + + v, err := parse(v.Field(i)) + if err != nil { + return nil, err + } + + m[name] = v } - return m + return m, nil } -func parseInterface(v reflect.Value) interface{} { +func getNameFromTag(field reflect.StructField) (string, error) { + tags, err := structtag.Parse(string(field.Tag)) + if err != nil { + return "", err + } + + jsonTag, err := tags.Get("json") + if err != nil { + // json tag does not exist + return "", nil + } + + return jsonTag.Name, nil +} + +func parseInterface(v reflect.Value) (interface{}, error) { field := v.Elem() return parse(field) } -func parsePrt(v reflect.Value) interface{} { +func parsePrt(v reflect.Value) (interface{}, error) { if v.IsNil() { - return parseNil(v) + return parseNil(v), nil } field := v.Elem() return parse(field) } -func parseMap(v reflect.Value) interface{} { +func parseMap(v reflect.Value) (interface{}, error) { m := make(map[string]interface{}) for _, key := range v.MapKeys() { value := v.MapIndex(key) - m[key.String()] = parse(value) + v, err := parse(value) + if err != nil { + return nil, err + } + + m[key.String()] = v } - return m + return m, nil } -func parseSlice(v reflect.Value) interface{} { +func parseSlice(v reflect.Value) (interface{}, error) { if v.Len() == 0 { el := v.Type().Elem() elType := el.String() @@ -108,17 +143,21 @@ func parseSlice(v reflect.Value) interface{} { elType = "any" } - return fmt.Sprintf("[%v]", elType) + return fmt.Sprintf("[%v]", elType), nil } var m []interface{} for i := 0; i < v.Len(); i++ { field := v.Index(i) - t := parse(field) + t, err := parse(field) + if err != nil { + return nil, err + } + m = append(m, t) } - return m + return m, nil } func isTime(v reflect.Value) bool { diff --git a/parser_test.go b/parser_test.go index 157c2dc..603e3e1 100644 --- a/parser_test.go +++ b/parser_test.go @@ -111,7 +111,7 @@ func TestParse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - actual := Parse(tt.input) + actual, _ := Parse(tt.input) expectedMap := make(map[string]interface{}) expectedBytes, _ := json.Marshal(actual)