From d8927ad0edafb01fe131f80ce0f8dc5fa560e894 Mon Sep 17 00:00:00 2001 From: Oleg Kovalov Date: Sun, 24 Sep 2023 15:40:32 +0200 Subject: [PATCH] Add unmarshal funcs (#8) --- jsn.go | 30 +++++++++++++++++++++++++ jsn_test.go | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 jsn_test.go diff --git a/jsn.go b/jsn.go index b760e73..e15eac7 100644 --- a/jsn.go +++ b/jsn.go @@ -1,5 +1,12 @@ package jsn +import ( + "bytes" + "encoding/json" + "errors" + "io" +) + // A represents JSON array. type A = []any @@ -15,3 +22,26 @@ type N string func (n N) MarshalJSON() ([]byte, error) { return []byte(n), nil } + +// Unmarshal only 1 JSON entity from the input. +// Disallows unknown fields if the argument is a struct. +func Unmarshal(b []byte, v any) error { + return UnmarshalFrom(bytes.NewReader(b), v) +} + +// UnmarshalFrom only 1 JSON entity from the input. +// Disallows unknown fields if the argument is a struct. +func UnmarshalFrom(r io.Reader, v any) error { + d := json.NewDecoder(r) + d.DisallowUnknownFields() + + if err := d.Decode(v); err != nil { + return err + } + if d.More() { + return errMore + } + return nil +} + +var errMore = errors.New("body must contain only one JSON entity") diff --git a/jsn_test.go b/jsn_test.go new file mode 100644 index 0000000..1720c8c --- /dev/null +++ b/jsn_test.go @@ -0,0 +1,65 @@ +package jsn + +import ( + "reflect" + "testing" +) + +func TestUnmarshal(t *testing.T) { + testCases := []struct { + input string + want any + errStr string + }{ + { + input: `"abc"`, + want: "abc", + }, + { + input: `123`, + want: float64(123), + }, + { + input: `{"abc": 123}`, + want: map[string]any{"abc": float64(123)}, + }, + { + input: `{"abc": 123} `, + want: map[string]any{"abc": float64(123)}, + }, + { + input: ` ["abc", 123] `, + want: []any{"abc", float64(123)}, + }, + { + input: `{"abc": 123}a`, + errStr: "body must contain only one JSON entity", + }, + { + input: `[123]{"a":"b"}`, + errStr: "body must contain only one JSON entity", + }, + { + input: `{"abc`, + errStr: "unexpected EOF", + }, + } + + for _, tc := range testCases { + var val any + err := Unmarshal([]byte(tc.input), &val) + if err != nil { + if tc.errStr == "" { + t.Fatal(err) + } + if have := err.Error(); have != tc.errStr { + t.Fatalf("\nhave: %+v\nwant: %+v\n", have, tc.errStr) + } + continue + } + + if !reflect.DeepEqual(val, tc.want) { + t.Fatalf("\nhave: %+v\nwant: %+v\n", val, tc.want) + } + } +}