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

feat:(decoder) support skip mismatche-typed value #325

Merged
merged 12 commits into from Nov 4, 2022
21 changes: 18 additions & 3 deletions README.md
Expand Up @@ -181,16 +181,17 @@ ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"
### Compact Format
Sonic encodes primitive objects (struct/map...) as compact-format JSON by default, except marshaling `json.RawMessage` or `json.Marshaler`: sonic ensures validating their output JSON but **DONOT** compacting them for performance concerns. We provide the option `encoder.CompactMarshaler` to add compacting process.

### Print Syntax Error
### Print Error
If there invalid syntax in input JSON, sonic will return `decoder.SyntaxError`, which supports pretty-printing of error position
```go
import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/decoder"

var data interface{}
err := sonic.Unmarshal("[[[}]]", &data)
err := sonic.UnmarshalString("[[[}]]", &data)
if err != nil {
/*one line by default*/
println(e.Error())) // "Syntax error at index 3: invalid char\n\n\t[[[}]]\n\t...^..\n"
println(e.Error()) // "Syntax error at index 3: invalid char\n\n\t[[[}]]\n\t...^..\n"
/*pretty print*/
if e, ok := err.(decoder.SyntaxError); ok {
/*Syntax error at index 3: invalid char
Expand All @@ -199,10 +200,24 @@ if err != nil {
...^..
*/
print(e.Description())
}else if me, ok := err.(decoder.MismatchTypeError); ok {
print(e.Description()
}
}
```
If there a **mismatch-typed** value for a given key, sonic will report `decoder.MismatchTypeError` (if there are many, report the last one), but still skip wrong the value and keep decoding next JSON.
```go
import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/decoder"

var data = struct{
A int
B int
}{}
err := UnmarshalString(`{"A":"1","B":1}`, &data)
println(err.Error()) // Mismatch type int with value string "at index 5: mismatched type with value\n\n\t{\"A\":\"1\",\"B\":1}\n\t.....^.........\n"
fmt.Printf("%+v", data) // {A:0 B:1}
```
### Ast.Node
Sonic/ast.Node is a completely self-contained AST for JSON. It implements serialization and deserialization both and provides robust APIs for obtaining and modification of generic data.
#### Get/Index
Expand Down
14 changes: 10 additions & 4 deletions bench.py
Expand Up @@ -19,8 +19,7 @@
import subprocess
import argparse

repeat_time = 100
gbench_prefix = "SONIC_NO_ASYNC_GC=1 go test -benchmem -run=none -count=%d "%(repeat_time)
gbench_prefix = "SONIC_NO_ASYNC_GC=1 go test -benchmem -run=none "

def run(cmd):
print(cmd)
Expand Down Expand Up @@ -87,8 +86,8 @@ def compare(args):
run("%s %s ./... 2>&1 | tee %s" %(gbench_prefix, args, main))

# diff the result
benchstat = "go get golang.org/x/perf/cmd/benchstat && go install golang.org/x/perf/cmd/benchstat"
run( "%s && benchstat -sort=delta %s %s"%(benchstat, main, target))
# benchstat = "go get golang.org/x/perf/cmd/benchstat && go install golang.org/x/perf/cmd/benchstat"
run( "benchstat -sort=delta %s %s"%(main, target))
run("git checkout -- .")

# restore branch
Expand All @@ -105,6 +104,8 @@ def main():
help='Compare with the main benchmarking')
argparser.add_argument('-t', '--times', dest='times', required=False,
help='benchmark the times')
argparser.add_argument('-r', '--repeat_times', dest='count', required=False,
help='benchmark the count')
args = argparser.parse_args()

if args.filter:
Expand All @@ -114,6 +115,11 @@ def main():

if args.times:
gbench_args += " -benchtime=%s"%(args.times)

if args.count:
gbench_args += " -count=%s"%(args.count)
else:
gbench_args += " -count=10"

if args.compare:
target = compare(gbench_args)
Expand Down
24 changes: 23 additions & 1 deletion decode_test.go
Expand Up @@ -1116,6 +1116,7 @@ func TestMarshalEmbeds(t *testing.T) {

func TestUnmarshal(t *testing.T) {
for i, tt := range unmarshalTests {
t.Log(i, tt.in)
if !json.Valid([]byte(tt.in)) {
continue
}
Expand Down Expand Up @@ -2005,7 +2006,6 @@ var decodeTypeErrorTests = []struct {
dest interface{}
src string
}{
{new(string), `{"user": "name"}`}, // issue 4628.
{new(error), `{}`}, // issue 4222
{new(error), `[]`},
{new(error), `""`},
Expand All @@ -2025,6 +2025,28 @@ func TestUnmarshalTypeError(t *testing.T) {
}
}

var decodeMismatchErrorTests = []struct {
dest interface{}
src string
}{
{new(int), `{}`},
{new(string), `{}`},
{new(bool), `{}`},
{new([]byte), `{}`},
}

func TestMismatchTypeError(t *testing.T) {
for _, item := range decodeMismatchErrorTests {
err := Unmarshal([]byte(item.src), item.dest)
if _, ok := err.(*decoder.MismatchTypeError); !ok {
if _, ok = err.(decoder.SyntaxError); !ok {
t.Errorf("expected mismatch error for Unmarshal(%q, type %T): got %T",
item.src, item.dest, err)
}
}
}
}

var unmarshalSyntaxTests = []string{
"tru",
"fals",
Expand Down