diff --git a/jsonutil/encoding.go b/jsonutil/encoding.go new file mode 100644 index 000000000..da05a3246 --- /dev/null +++ b/jsonutil/encoding.go @@ -0,0 +1,64 @@ +package jsonutil + +import ( + "bytes" + "encoding/json" + "io" +) + +// MustString encode data to json string, will panic on error +func MustString(v any) string { + bs, err := json.Marshal(v) + if err != nil { + panic(err) + } + return string(bs) +} + +// Encode data to json bytes. alias of json.Marshal +func Encode(v any) ([]byte, error) { + return json.Marshal(v) +} + +// EncodePretty encode data to pretty JSON bytes. +func EncodePretty(v any) ([]byte, error) { + return json.MarshalIndent(v, "", " ") +} + +// EncodeString encode data to JSON string. +func EncodeString(v any) (string, error) { + bs, err := json.MarshalIndent(v, "", " ") + return string(bs), err +} + +// EncodeToWriter encode data to json and write to writer. +func EncodeToWriter(v any, w io.Writer) error { + return json.NewEncoder(w).Encode(v) +} + +// EncodeUnescapeHTML data to json bytes. will close escape HTML +func EncodeUnescapeHTML(v any) ([]byte, error) { + buf := &bytes.Buffer{} + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + + if err := enc.Encode(v); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// Decode json bytes to data ptr. alias of json.Unmarshal +func Decode(bts []byte, ptr any) error { + return json.Unmarshal(bts, ptr) +} + +// DecodeString json string to data ptr. +func DecodeString(str string, ptr any) error { + return json.Unmarshal([]byte(str), ptr) +} + +// DecodeReader decode JSON from io reader. +func DecodeReader(r io.Reader, ptr any) error { + return json.NewDecoder(r).Decode(ptr) +} diff --git a/jsonutil/encoding_test.go b/jsonutil/encoding_test.go new file mode 100644 index 000000000..fa88d5ec0 --- /dev/null +++ b/jsonutil/encoding_test.go @@ -0,0 +1,77 @@ +package jsonutil_test + +import ( + "bytes" + "strings" + "testing" + + "github.com/gookit/goutil/jsonutil" + "github.com/gookit/goutil/testutil/assert" +) + +func TestEncode(t *testing.T) { + bts, err := jsonutil.Encode(testUser) + assert.NoErr(t, err) + assert.Eq(t, `{"name":"inhere","age":200}`, string(bts)) + + bts, err = jsonutil.Encode(&testUser) + assert.NoErr(t, err) + assert.Eq(t, `{"name":"inhere","age":200}`, string(bts)) + + assert.Eq(t, `{"name":"inhere","age":200}`, jsonutil.MustString(testUser)) + + assert.Panics(t, func() { + jsonutil.MustString(invalid) + }) + + bts, err = jsonutil.Encode(&testUser) + assert.NoErr(t, err) + assert.Eq(t, `{"name":"inhere","age":200}`, string(bts)) +} + +func TestEncodeUnescapeHTML(t *testing.T) { + bts, err := jsonutil.EncodeUnescapeHTML(&testUser) + assert.NoErr(t, err) + assert.Eq(t, `{"name":"inhere","age":200} +`, string(bts)) + + _, err = jsonutil.EncodeUnescapeHTML(invalid) + assert.Err(t, err) +} + +func TestEncodeToWriter(t *testing.T) { + buf := &bytes.Buffer{} + + err := jsonutil.EncodeToWriter(testUser, buf) + assert.NoErr(t, err) + assert.Eq(t, `{"name":"inhere","age":200} +`, buf.String()) +} + +func TestDecode(t *testing.T) { + str := `{"name":"inhere","age":200}` + usr := &user{} + err := jsonutil.Decode([]byte(str), usr) + + assert.NoErr(t, err) + assert.Eq(t, "inhere", usr.Name) + assert.Eq(t, 200, usr.Age) +} + +func TestDecodeString(t *testing.T) { + str := `{"name":"inhere","age":200}` + usr := &user{} + err := jsonutil.DecodeString(str, usr) + + assert.NoErr(t, err) + assert.Eq(t, "inhere", usr.Name) + assert.Eq(t, 200, usr.Age) + + // DecodeReader + usr = &user{} + err = jsonutil.DecodeReader(strings.NewReader(str), usr) + + assert.NoErr(t, err) + assert.Eq(t, "inhere", usr.Name) + assert.Eq(t, 200, usr.Age) +} diff --git a/jsonutil/jsonutil.go b/jsonutil/jsonutil.go index c7d392d8e..36337291a 100644 --- a/jsonutil/jsonutil.go +++ b/jsonutil/jsonutil.go @@ -4,7 +4,6 @@ package jsonutil import ( "bytes" "encoding/json" - "io" "os" "regexp" "strings" @@ -13,7 +12,7 @@ import ( // WriteFile write data to JSON file func WriteFile(filePath string, data any) error { - jsonBytes, err := Encode(data) + jsonBytes, err := json.Marshal(data) if err != nil { return err } @@ -46,54 +45,20 @@ func Pretty(v any) (string, error) { return string(out), err } -// Encode data to json bytes. -func Encode(v any) ([]byte, error) { - return json.Marshal(v) -} - -// EncodePretty encode pretty JSON data to json bytes. -func EncodePretty(v any) ([]byte, error) { - return json.MarshalIndent(v, "", " ") -} - -// EncodeToWriter encode data to writer. -func EncodeToWriter(v any, w io.Writer) error { - return json.NewEncoder(w).Encode(v) -} - -// EncodeUnescapeHTML data to json bytes. will close escape HTML -func EncodeUnescapeHTML(v any) ([]byte, error) { - buf := &bytes.Buffer{} - enc := json.NewEncoder(buf) - enc.SetEscapeHTML(false) - - if err := enc.Encode(v); err != nil { - return nil, err +// MustPretty data to JSON string, will panic on error +func MustPretty(v any) string { + out, err := json.MarshalIndent(v, "", " ") + if err != nil { + panic(err) } - - return buf.Bytes(), nil -} - -// Decode json bytes to data ptr. -func Decode(bts []byte, ptr any) error { - return json.Unmarshal(bts, ptr) -} - -// DecodeString json string to data ptr. -func DecodeString(str string, ptr any) error { - return json.Unmarshal([]byte(str), ptr) -} - -// DecodeReader decode JSON from io reader. -func DecodeReader(r io.Reader, ptr any) error { - return json.NewDecoder(r).Decode(ptr) + return string(out) } // Mapping src data(map,struct) to dst struct use json tags. // // On src, dst both is struct, equivalent to merging two structures (src should be a subset of dsc) func Mapping(src, dst any) error { - bts, err := Encode(src) + bts, err := json.Marshal(src) if err != nil { return err } diff --git a/jsonutil/jsonutil_test.go b/jsonutil/jsonutil_test.go index dc955309b..682720f6d 100644 --- a/jsonutil/jsonutil_test.go +++ b/jsonutil/jsonutil_test.go @@ -1,7 +1,6 @@ package jsonutil_test import ( - "bytes" "testing" "github.com/gookit/goutil/jsonutil" @@ -14,6 +13,7 @@ type user struct { } var testUser = user{"inhere", 200} +var invalid = jsonutil.Encode func TestPretty(t *testing.T) { tests := []any{ @@ -36,76 +36,96 @@ func TestPretty(t *testing.T) { assert.Eq(t, want, string(bts)) } -func TestEncode(t *testing.T) { - bts, err := jsonutil.Encode(testUser) - assert.NoErr(t, err) - assert.Eq(t, `{"name":"inhere","age":200}`, string(bts)) +func TestMustPretty(t *testing.T) { + type T1 struct { + A1 int `json:"a1"` + B1 string `json:"b1"` + } - bts, err = jsonutil.Encode(&testUser) - assert.NoErr(t, err) - assert.Eq(t, `{"name":"inhere","age":200}`, string(bts)) + type T2 struct { + T1 // embedded without json tag + C T1 `json:"c"` // as field and with json tag + A int `json:"a"` + B string `json:"b"` + } - bts, err = jsonutil.EncodeUnescapeHTML(&testUser) - assert.NoErr(t, err) - assert.Eq(t, `{"name":"inhere","age":200} -`, string(bts)) -} + t1 := T1{A1: 1, B1: "b1-at-T1"} + t2 := T2{T1: t1, A: 1, B: "b-at-T2", C: t1} + + str := jsonutil.MustPretty(t2) + assert.StrContains(t, str, `"c": {`) + assert.NotContains(t, str, `"T1": {`) + // fmt.Println(str) + /*Output: + { + "a1": 1, + "b1": "b1-at-T1", + "c": { + "a1": 1, + "b1": "b1-at-T1" + }, + "a": 1, + "b": "b-at-T2" + } + */ -func TestEncodeUnescapeHTML(t *testing.T) { - bts, err := jsonutil.Encode(&testUser) - assert.NoErr(t, err) - assert.Eq(t, `{"name":"inhere","age":200}`, string(bts)) -} + type T3 struct { + T1 `json:"t1"` // with json tag + A int `json:"a"` + B string `json:"b"` + } -func TestEncodeToWriter(t *testing.T) { - buf := &bytes.Buffer{} + t3 := T3{T1: t1, A: 1, B: "b-at-T3"} + str = jsonutil.MustPretty(t3) + assert.StrContains(t, str, `"t1": {`) + // fmt.Println(str) + /*Output: + { + "t1": { + "a1": 1, + "b1": "b1-at-T1" + }, + "a": 1, + "b": "b-at-T3" + } + */ - err := jsonutil.EncodeToWriter(testUser, buf) - assert.NoErr(t, err) - assert.Eq(t, `{"name":"inhere","age":200} -`, buf.String()) + assert.Panics(t, func() { + jsonutil.MustPretty(jsonutil.Encode) + }) } -func TestDecode(t *testing.T) { - str := `{"name":"inhere","age":200}` - usr := &user{} - err := jsonutil.Decode([]byte(str), usr) - - assert.NoErr(t, err) - assert.Eq(t, "inhere", usr.Name) - assert.Eq(t, 200, usr.Age) -} +func TestMapping(t *testing.T) { + mp := map[string]any{"name": "inhere", "age": 200} -func TestDecodeString(t *testing.T) { - str := `{"name":"inhere","age":200}` usr := &user{} - err := jsonutil.DecodeString(str, usr) - + err := jsonutil.Mapping(mp, usr) assert.NoErr(t, err) assert.Eq(t, "inhere", usr.Name) assert.Eq(t, 200, usr.Age) + + assert.Err(t, jsonutil.Mapping(invalid, usr)) } func TestWriteReadFile(t *testing.T) { - user := struct { - Name string `json:"name"` - Age int `json:"age"` - }{"inhere", 200} + usr := user{"inhere", 200} - err := jsonutil.WriteFile("testdata/test.json", &user) + err := jsonutil.WriteFile("testdata/test.json", &usr) assert.NoErr(t, err) + assert.Err(t, jsonutil.WriteFile("testdata/test.json", jsonutil.Encode)) - err = jsonutil.WritePretty("testdata/test2.json", &user) + err = jsonutil.WritePretty("testdata/test2.json", &usr) assert.NoErr(t, err) + assert.Err(t, jsonutil.WritePretty("testdata/test2.json", jsonutil.Encode)) - // err = jsonutil.WritePretty("/path/to/not-exist.json", &user) - // assert.Err(t, err) - - err = jsonutil.ReadFile("testdata/test.json", &user) + err = jsonutil.ReadFile("testdata/test.json", &usr) assert.NoErr(t, err) - assert.Eq(t, "inhere", user.Name) - assert.Eq(t, 200, user.Age) + assert.Eq(t, "inhere", usr.Name) + assert.Eq(t, 200, usr.Age) + + err = jsonutil.ReadFile("testdata/not-exist.json", &usr) + assert.Err(t, err) } func TestIsJsonFast(t *testing.T) {