Skip to content

Commit

Permalink
feat: Add parser
Browse files Browse the repository at this point in the history
  • Loading branch information
claudioluciano committed Jun 27, 2022
1 parent b5e6284 commit 9ffc03b
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 0 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/claudioluciano/simple_json_schema_like

go 1.18
5 changes: 5 additions & 0 deletions mock_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package simplejsonschemalike

const (
mockTest1 string = `{"Int":"int","Map":{"Key1":"string","Key2":"int","Key3":"bool"},"SliceOfString":"[string]","String":"string","Struct":{"Int":"int","Map":{"Key1":"string","Key2":"int","Key3":"bool"},"SliceOfString":"[string]","String":"string"},"StructPtr":{"Int":"int","Map":{"Key1":"string","Key2":"int","Key3":"bool"},"SliceOfString":"[string]","String":"string"},"Time":"DateTime"}`
)
138 changes: 138 additions & 0 deletions parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package simplejsonschemalike

import (
"fmt"
"reflect"
"unicode"
)

const timeStringKind = "time.Time"

/*Parse receive anything and try to parse to a JSON where with the type of the field.
Something like this
{
"Int": "int",
"Map": {
"Key1": "string",
"Key2": "int",
"Key3": "bool"
},
"SliceOfString": "[string]",
"String": "string",
"Struct": {
"Int": "int",
"Map": {
"Key1": "string",
"Key2": "int",
"Key3": "bool"
},
"SliceOfString": "[string]",
"String": "string"
},
"StructPtr": {
"Int": "int",
"Map": {
"Key1": "string",
"Key2": "int",
"Key3": "bool"
},
"SliceOfString": "[string]",
"String": "string"
}
}
*/
func Parse(in interface{}) interface{} {
v := reflect.ValueOf(in)

return parse(v)
}

func parse(v reflect.Value) interface{} {
switch v.Kind() {
case reflect.Struct:
if ok := isTime(v); ok {
return "DateTime"
}

return parseStruct(v)
case reflect.Interface:
return parseInterface(v)
case reflect.Ptr:
return parsePrt(v)
case reflect.Map:
return parseMap(v)
case reflect.Slice:
return fmt.Sprintf("[%v]", parseSlice(v))
default:
vv := v.Type().String()

return vv
}
}

func parseNil(v reflect.Value) interface{} {
if ok := isTime(v); ok {
return "DateTime"
}

return v.Type().String()
}

func parseStruct(v reflect.Value) map[string]interface{} {
m := make(map[string]interface{})

for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
structfield := v.Type().Field(i)
fieldName := structfield.Name

if !exportedField(structfield.Name) {
continue
}

m[fieldName] = parse(field)
}

return m
}

func parseInterface(v reflect.Value) interface{} {
field := v.Elem()
return parse(field)
}

func parsePrt(v reflect.Value) interface{} {
if v.IsNil() {
return parseNil(v)
}

field := v.Elem()
return parse(field)
}

func parseMap(v reflect.Value) interface{} {
m := make(map[string]interface{})

for _, key := range v.MapKeys() {
value := v.MapIndex(key)

m[key.String()] = parse(value)
}

return m
}

func parseSlice(v reflect.Value) interface{} {
return v.Type().Elem()
}

func isTime(v reflect.Value) bool {
t := v.Type().String()
return t == timeStringKind || t == fmt.Sprintf("*%v", timeStringKind)
}

func exportedField(name string) bool {
r := []rune(name)

return unicode.IsUpper(r[0]) && unicode.IsLetter(r[0])
}
89 changes: 89 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package simplejsonschemalike

import (
"encoding/json"
"fmt"
"testing"
"time"
)

func TestParse(t *testing.T) {
type inn struct {
String string
Int int
SliceOfString []string
Map map[string]interface{}
}

type in struct {
String string
Int int
SliceOfString []string
Map map[string]interface{}
Time *time.Time
Struct inn
StructPtr *inn
}

type args struct {
in interface{}
}

tests := []struct {
name string
args args
want string
}{
{
name: "",
args: args{
in: in{
String: "",
Int: 0,
SliceOfString: []string{},
Map: map[string]interface{}{
"Key1": "",
"Key2": 0,
"Key3": true,
},
Struct: inn{
String: "",
Int: 0,
SliceOfString: []string{},
Map: map[string]interface{}{
"Key1": "",
"Key2": 0,
"Key3": true,
},
},
StructPtr: &inn{
String: "",
Int: 0,
SliceOfString: []string{},
Map: map[string]interface{}{
"Key1": "",
"Key2": 0,
"Key3": true,
},
},
},
},
want: mockTest1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Parse(tt.args.in)
if !equals(got, tt.want) {
t.Errorf("Parse() = %v, want %v", got, tt.want)
}
})
}
}

func equals(value interface{}, jsonValue string) bool {
b, _ := json.Marshal(value)

fmt.Println(string(b))
return string(b) == jsonValue
}

0 comments on commit 9ffc03b

Please sign in to comment.