Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnmarshalTypeError hook support ??? #5

Open
skybosi opened this issue Sep 27, 2023 · 3 comments
Open

UnmarshalTypeError hook support ??? #5

skybosi opened this issue Sep 27, 2023 · 3 comments

Comments

@skybosi
Copy link

skybosi commented Sep 27, 2023

package main

import (
	"encoding/json"
	"fmt"

	"github.com/titanous/json5"
)

type (
	R2 struct {
		Code     int         `json:"code,string"`
		FloatNum float32     `json:"float_num,string"`
		Code2    int         `json:",string"`
		Msg      string      `json:"msg,omitempty"`
		Data     interface{} `json:",omitempty"`
		Data2    interface{} `json:",omitempty"`
		Id       int         `json:"-"`
		Id2      int         `json:"-,"`
	}
)

func main() {
	r2, _ := json.Marshal(R2{Code: 0, Msg: "", Data: "字符串数据", Data2: nil, Code2: 999, Id: 1000, Id2: 2000})
	fmt.Println(string(r2)) // {"code":"0","Code2":"999","Data":"字符串数据","-":2000}

	rs2 := `{code:"666w", float_num:'11.12', "Code2":"999","Data":"字符串数据","-":2000}`
	rsr := &R2{}
	err := json5.Unmarshal([]byte(rs2), rsr)
	if err == nil {
		fmt.Println(rsr)
	} else {
		fmt.Println(err)
	}
}

result:
{"code":"0","float_num":"0","Code2":"999","Data":"字符串数据","-":2000}
json: cannot unmarshal number 666w into Go value of type int

can we get ,parse int failed, use default value ?
number:
int, int32, int64, uint32, uint64 ... -> 0
float32 float64 -> 0.0

maybe , you can provide a hook, while ParseInt , ParseFloat

		n, err := strconv.ParseInt(h, 16, 64)
		if err != nil {
                         // add hook here, if not add hook, use default  error handler
			return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)}
		}
@skybosi
Copy link
Author

skybosi commented Sep 27, 2023

other chose, can provide a error handle interface, while Unmarshal error, call interface method, get result

@skybosi
Copy link
Author

skybosi commented Sep 27, 2023

//////////// append ///////////

// 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
}

//////////// rewrite ///////////

// error aborts the decoding by panicking with err.
func (d *decodeState) error(err error) {
	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 e := unmarshalErrHandlerHook(err); e == nil {
		d.savedError = e
	} else {
		if d.savedError == nil {
			d.savedError = err
		}
	}
}

for example here:

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"`
	}
	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 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")
	}
}

@skybosi
Copy link
Author

skybosi commented Sep 28, 2023

add pr: #6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant