From b3a34917ab4b79812ce56b1fb8cd1768ef46927b Mon Sep 17 00:00:00 2001 From: skybosi Date: Thu, 28 Sep 2023 10:48:57 +0800 Subject: [PATCH 1/4] Update decode.go --- decode.go | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 7 deletions(-) diff --git a/decode.go b/decode.go index 005431f..1411552 100644 --- a/decode.go +++ b/decode.go @@ -20,6 +20,23 @@ import ( "unicode/utf8" ) +// UnmarshalErrHandler while decode error, will inject this handler, catch error. +// if you want to ignore this error, you can return nil +type UnmarshalErrHandler func(err error) error + +var ( + usedUnmarshalErrHandlerHook atomic.Bool + unmarshalErrHandlerHook UnmarshalErrHandler = func(err error) error { return err } +) + +// UseUnmarshalErrHandler register a yourself error handler, only once +func UseUnmarshalErrHandler(handler UnmarshalErrHandler) { + if usedUnmarshalErrHandlerHook.Swap(true) { + return + } + unmarshalErrHandlerHook = handler +} + // Unmarshal parses the JSON-encoded data and stores the result // in the value pointed to by v. // @@ -308,14 +325,22 @@ func (d *decodeState) init(data []byte) *decodeState { // error aborts the decoding by panicking with err. func (d *decodeState) error(err error) { - panic(err) + if e := unmarshalErrHandlerHook(err); e != nil { + panic(err) + } else { + d.savedError = e + } } // saveError saves the first err it is called with, // for reporting at the end of the unmarshal. func (d *decodeState) saveError(err error) { - if d.savedError == nil { - d.savedError = err + if e := unmarshalErrHandlerHook(err); e == nil { + d.savedError = e + } else { + if d.savedError == nil { + d.savedError = err + } } } @@ -900,10 +925,9 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted, unq } case '"', '\'': // string - s := item + s := string(item) if !unquotedString { - var ok bool - s, ok = unquoteBytes(item) + ss, ok := unquoteBytes([]byte(item)) if !ok { if fromQuoted { d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) @@ -911,6 +935,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted, unq d.error(errPhase) } } + s = string(ss) } switch v.Kind() { default: @@ -921,7 +946,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted, unq break } b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) - n, err := base64.StdEncoding.Decode(b, s) + n, err := base64.StdEncoding.Decode(b, []byte(s)) if err != nil { d.saveError(err) break @@ -935,6 +960,50 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted, unq } else { d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + var n int64 + var err error + if h, ok := hexString(s); ok { + n, err = strconv.ParseInt(h, 16, 64) + } else { + n, err = strconv.ParseInt(s, 10, 64) + } + if err != nil || v.OverflowInt(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetInt(n) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + var n uint64 + var err error + if h, ok := hexString(s); ok { + n, err = strconv.ParseUint(h, 16, 64) + } else { + n, err = strconv.ParseUint(s, 10, 64) + } + if err != nil || v.OverflowUint(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetUint(n) + case reflect.Float32, reflect.Float64: + var n float64 + var err error + if h, ok := hexString(s); ok { + var nn int64 + nn, err = strconv.ParseInt(h, 16, 64) + n = float64(nn) + if s[0] == '-' && nn == 0 { + n = -n + } + } else { + n, err = strconv.ParseFloat(s, v.Type().Bits()) + } + if err != nil || v.OverflowFloat(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetFloat(n) } default: // number From 0f5e967d0988fac2ba96bb52a1a28ca5d9896471 Mon Sep 17 00:00:00 2001 From: skybosi Date: Thu, 28 Sep 2023 10:49:18 +0800 Subject: [PATCH 2/4] Update decode_test.go --- decode_test.go | 86 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/decode_test.go b/decode_test.go index 9c33775..6b5b657 100644 --- a/decode_test.go +++ b/decode_test.go @@ -665,6 +665,76 @@ var wrongStringTests = []wrongStringTest{ {`{"result":123}`, `json: invalid use of ,string struct tag, trying to unmarshal unquoted value into string`}, } +func TestErrHandler(t *testing.T) { + type ErrHandlerStruct struct { + VarName string `json:"varname,omitempty"` + Required string `json:"required,omitempty"` + Mode string `json:"mode,omitempty"` + + Title string `json:"title,omitempty"` + Value string `json:"value,omitempty"` + ImageUrl string `json:"imageUrl,omitempty"` + Size int `json:"size,omitempty,string"` + } + data := `[ + { + title: '我的头像', + value: '"{{ .AvatarUrl}}"', + imageUrl: 'https://www.AvatarUrlImage.com', + VarName: 666, + }, + { + title: '我的昵称', + value: "{{.NickName}}", + size: '{{.Size}}', + imageUrl: 'https://www.NickNameImage.com' + } +]` + UseUnmarshalErrHandler(func(err error) error { + if err != nil { + switch { + case strings.Contains(err.Error(), "json: invalid use of ,string"): + return nil + case strings.Contains(err.Error(), "json: cannot unmarshal number"): + return nil + case strings.Contains(err.Error(), "json: cannot unmarshal string"): + return nil + case strings.Contains(err.Error(), "json: cannot unmarshal number into Go value of type string"): + return nil + } + } + return err + }) + + var res []*ErrHandlerStruct + dec := NewDecoder(strings.NewReader(data)) + err := dec.Decode(&res) + if err == nil { + fmt.Printf("NewDecoder result: %+v\n", res) + } else { + fmt.Printf("NewDecoder result: %+v err: %v\n", res, err) + } + if len(res) != 2 { + t.Errorf("Decode: result len is not match") + } + if err != nil { + t.Errorf("Decode: error not nil") + } + var res2 []*ErrHandlerStruct + err2 := Unmarshal([]byte(data), &res2) + if err2 == nil { + fmt.Printf("Unmarshal result: %+v\n", res2) + } else { + fmt.Printf("Unmarshal result: %+v err: %v\n", res2, err2) + } + if len(res2) != 2 { + t.Errorf("Unmarshal: result len is not match") + } + if err2 != nil { + t.Errorf("Unmarshal: error not nil") + } +} + // If people misuse the ,string modifier, the error message should be // helpful, telling the user that they're doing it wrong. func TestErrorMessageFromMisusedString(t *testing.T) { @@ -673,14 +743,14 @@ func TestErrorMessageFromMisusedString(t *testing.T) { var s WrongString err := NewDecoder(r).Decode(&s) got := fmt.Sprintf("%v", err) - if got != tt.err { + if !usedUnmarshalErrHandlerHook.Load() && got != tt.err { t.Errorf("%d. got err = %q, want %q", n, got, tt.err) } } } func noSpace(c rune) rune { - if isSpace(byte(c)) { //only used for ascii + if isSpace(byte(c)) { // only used for ascii return -1 } return c @@ -1037,7 +1107,7 @@ func TestEmptyString(t *testing.T) { dec := NewDecoder(strings.NewReader(data)) var t2 T2 err := dec.Decode(&t2) - if err == nil { + if !usedUnmarshalErrHandlerHook.Load() && err == nil { t.Fatal("Decode: did not return error") } if t2.Number1 != 1 { @@ -1175,7 +1245,7 @@ var decodeTypeErrorTests = []struct { func TestUnmarshalTypeError(t *testing.T) { for _, item := range decodeTypeErrorTests { err := Unmarshal([]byte(item.src), item.dest) - if _, ok := err.(*UnmarshalTypeError); !ok { + if _, ok := err.(*UnmarshalTypeError); !ok && !usedUnmarshalErrHandlerHook.Load() { t.Errorf("expected type error for Unmarshal(%q, type %T): got %T", item.src, item.dest, err) } @@ -1337,12 +1407,14 @@ func TestInvalidUnmarshalText(t *testing.T) { buf := []byte(`123`) for _, tt := range invalidUnmarshalTextTests { err := Unmarshal(buf, tt.v) - if err == nil { + if !usedUnmarshalErrHandlerHook.Load() && err == nil { t.Errorf("Unmarshal expecting error, got nil") continue } - if got := err.Error(); got != tt.want { - t.Errorf("Unmarshal = %q; want %q", got, tt.want) + if !usedUnmarshalErrHandlerHook.Load() { + if got := err.Error(); got != tt.want { + t.Errorf("Unmarshal = %q; want %q", got, tt.want) + } } } } From 576def1d824b25e2f3d59dbf2e8c8457c368cea0 Mon Sep 17 00:00:00 2001 From: skybosi Date: Thu, 28 Sep 2023 10:52:13 +0800 Subject: [PATCH 3/4] Update decode.go --- decode.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/decode.go b/decode.go index 1411552..59651f9 100644 --- a/decode.go +++ b/decode.go @@ -15,6 +15,7 @@ import ( "reflect" "runtime" "strconv" + "sync/atomic" "unicode" "unicode/utf16" "unicode/utf8" @@ -848,7 +849,7 @@ var numberType = reflect.TypeOf(Number("")) func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted, unquotedString bool) { // Check for unmarshaler. if len(item) == 0 { - //Empty string given + // Empty string given d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) return } From a6d67e281b70d7cf465815eb05efa83655b693c5 Mon Sep 17 00:00:00 2001 From: skybosi Date: Thu, 28 Sep 2023 10:52:27 +0800 Subject: [PATCH 4/4] Update json5_test.go --- json5_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json5_test.go b/json5_test.go index 4e8eeb1..fd5b896 100644 --- a/json5_test.go +++ b/json5_test.go @@ -121,7 +121,7 @@ func TestJSON5Decode(t *testing.T) { } } _, err = parseJSON5() - if err == nil { + if err == nil && !usedUnmarshalErrHandlerHook.Load() { t.Errorf("expected JSON5 parsing to fail") return nil }