Skip to content

Commit

Permalink
✨ feat: jsonutil - add new func MustPretty, MustString and add more u…
Browse files Browse the repository at this point in the history
…nit tests
  • Loading branch information
inhere committed Jul 25, 2023
1 parent 59dd067 commit 6eea491
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 92 deletions.
64 changes: 64 additions & 0 deletions jsonutil/encoding.go
Original file line number Diff line number Diff line change
@@ -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)
}
77 changes: 77 additions & 0 deletions jsonutil/encoding_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
51 changes: 8 additions & 43 deletions jsonutil/jsonutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package jsonutil
import (
"bytes"
"encoding/json"
"io"
"os"
"regexp"
"strings"
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
118 changes: 69 additions & 49 deletions jsonutil/jsonutil_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package jsonutil_test

import (
"bytes"
"testing"

"github.com/gookit/goutil/jsonutil"
Expand All @@ -14,6 +13,7 @@ type user struct {
}

var testUser = user{"inhere", 200}
var invalid = jsonutil.Encode

func TestPretty(t *testing.T) {
tests := []any{
Expand All @@ -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) {
Expand Down

0 comments on commit 6eea491

Please sign in to comment.