Skip to content

Commit

Permalink
Merge pull request #8 from tfrench-uber/json
Browse files Browse the repository at this point in the history
Unmarshal: support non-nested json tags
  • Loading branch information
m7shapan authored Dec 31, 2021
2 parents d01bec8 + 718353e commit 52c13ef
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 9 deletions.
2 changes: 1 addition & 1 deletion etc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"strings"
)

func validTag(filed reflect.StructField) bool {
func validTag(filed reflect.StructField, tag string) bool {
return !(filed.Tag.Get(tag) == "" ||
filed.Tag.Get(tag) == "-")
}
Expand Down
29 changes: 23 additions & 6 deletions unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ package njson
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"

"github.com/tidwall/gjson"
)

const tag string = "njson"
const (
njsonTag = "njson"
jsonTag = "json"
)

var jsonNumberType = reflect.TypeOf(json.Number(""))

// Unmarshal used to unmarshal nested json using "njson" tag
func Unmarshal(data []byte, v interface{}) (err error) {

// catch code panic and return error message
defer func() {
if r := recover(); r != nil {
Expand All @@ -24,7 +28,7 @@ func Unmarshal(data []byte, v interface{}) (err error) {
case error:
err = x
default:
err = errors.New("Unknown panic")
err = fmt.Errorf("unknown panic: %v", r)
}
}
}()
Expand All @@ -34,12 +38,25 @@ func Unmarshal(data []byte, v interface{}) (err error) {
for i := 0; i < elem.NumField(); i++ {
field := elem.Field(i)

if !(validTag(typeOfT.Field(i)) && field.CanSet()) {
// Check that the tag is either "json" or "njson", and can be set
if (!validTag(typeOfT.Field(i), njsonTag) && !validTag(typeOfT.Field(i), jsonTag)) || !field.CanSet() {
continue
}

// Assume "njson" by default, but change to "json" if tag matches
fieldName := typeOfT.Field(i).Tag.Get(njsonTag)
if validTag(typeOfT.Field(i), jsonTag) {
fieldName = typeOfT.Field(i).Tag.Get(jsonTag)

// Only support true "json" tags:
// if a tag is nested, it must use the "njson" tag
if len(strings.Split(fieldName, ".")) > 1 {
return fmt.Errorf("invalid json tag: %s", fieldName)
}
}

// get field value by tag
result := gjson.GetBytes(data, typeOfT.Field(i).Tag.Get(tag))
result := gjson.GetBytes(data, fieldName)

// if field type json.Number
if v != nil && field.Kind() == reflect.String && field.Type() == jsonNumberType {
Expand All @@ -60,7 +77,7 @@ func Unmarshal(data []byte, v interface{}) (err error) {
}
}

return nil
return
}

func unmarshalSlice(results []gjson.Result, field reflect.Type) interface{} {
Expand Down
94 changes: 92 additions & 2 deletions unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ func TestUnmarshalSlices(t *testing.T) {
[
[
601, 602, 603
],
]
]
],
Expand Down Expand Up @@ -429,7 +429,7 @@ func TestUnmarshalComplex(t *testing.T) {
],
"time_1": "2021-01-11T23:56:51.141Z",
"time_2": "2021-01-11T23:56:51.141+01:00",
"time_3": "2021-01-11T23:56:51.141-01:00",
"time_3": "2021-01-11T23:56:51.141-01:00"
}
`

Expand Down Expand Up @@ -550,3 +550,93 @@ func TestUnmarshalMoreComplex(t *testing.T) {
}

}

func TestUnmarshalJson(t *testing.T) {
t.Run("success", func(t *testing.T) {
json := `
{
"name": {"first": "Mohamed", "last": "Shapan"},
"age": 26,
"friends": [
{"first": "Asma", "age": 26},
{"first": "Ahmed", "age": 25},
{"first": "Mahmoud", "age": 30}
]
}`

type Name struct {
First string `njson:"first"`
Last string `njson:"last"`
}

type User struct {
Name Name `njson:"name"`
Age int `json:"age"`
Friends []Name `json:"friends"`
}

actual := User{}

err := Unmarshal([]byte(json), &actual)
if err != nil {
t.Error(err)
}

var friends []Name
friends = append(friends, Name{
First: "Asma",
})

friends = append(friends, Name{
First: "Ahmed",
})

friends = append(friends, Name{
First: "Mahmoud",
})

expected := User{
Name: Name{
First: "Mohamed",
Last: "Shapan",
},
Age: 26,
Friends: friends,
}

diff := cmp.Diff(expected, actual)
if diff != "" {
t.Error(diff)
}
})

t.Run("fail", func(t *testing.T) {
json := `
{
"name": {"first": "Mohamed", "last": "Shapan"},
"age": 26,
"friends": [
{"first": "Asma", "age": 26},
{"first": "Ahmed", "age": 25},
{"first": "Mahmoud", "age": 30}
]
}`

type Name struct {
First string `njson:"first"`
Last string `njson:"last"`
}

type User struct {
Name string `json:"name.first"`
Age int `json:"age"`
Friends []Name `json:"friends"`
}

actual := User{}

if err := Unmarshal([]byte(json), &actual); err == nil {
t.Error("error should not be nil")
}
})
}

0 comments on commit 52c13ef

Please sign in to comment.