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

Support JSON Path for decoding #250

Merged
merged 1 commit into from
Nov 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ cover-html: cover

.PHONY: lint
lint: golangci-lint
golangci-lint run
$(BIN_DIR)/golangci-lint run

golangci-lint: | $(BIN_DIR)
@{ \
Expand Down
24 changes: 24 additions & 0 deletions benchmarks/path_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package benchmark

import (
"testing"

gojson "github.com/goccy/go-json"
)

func Benchmark_Decode_SmallStruct_UnmarshalPath_GoJson(b *testing.B) {
path, err := gojson.CreatePath("$.st")
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
for i := 0; i < b.N; i++ {
var v int
if err := path.Unmarshal(SmallFixture, &v); err != nil {
b.Fatal(err)
}
if v != 1 {
b.Fatal("failed to unmarshal path")
}
}
}
28 changes: 28 additions & 0 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,34 @@ func unmarshalContext(ctx context.Context, data []byte, v interface{}, optFuncs
return validateEndBuf(src, cursor)
}

var (
pathDecoder = decoder.NewPathDecoder()
)

func extractFromPath(path *Path, data []byte, optFuncs ...DecodeOptionFunc) ([][]byte, error) {
src := make([]byte, len(data)+1) // append nul byte to the end
copy(src, data)

ctx := decoder.TakeRuntimeContext()
ctx.Buf = src
ctx.Option.Flags = 0
ctx.Option.Flags |= decoder.PathOption
ctx.Option.Path = path.path
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
paths, cursor, err := pathDecoder.DecodePath(ctx, 0, 0)
if err != nil {
decoder.ReleaseRuntimeContext(ctx)
return nil, err
}
decoder.ReleaseRuntimeContext(ctx)
if err := validateEndBuf(src, cursor); err != nil {
return nil, err
}
return paths, nil
}

func unmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
src := make([]byte, len(data)+1) // append nul byte to the end
copy(src, data)
Expand Down
2 changes: 2 additions & 0 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ type UnmarshalTypeError = errors.UnmarshalTypeError
type UnsupportedTypeError = errors.UnsupportedTypeError

type UnsupportedValueError = errors.UnsupportedValueError

type PathError = errors.PathError
4 changes: 4 additions & 0 deletions internal/decoder/anonymous_field.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ func (d *anonymousFieldDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
p = *(*unsafe.Pointer)(p)
return d.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+d.offset))
}

func (d *anonymousFieldDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return d.dec.DecodePath(ctx, cursor, depth)
}
5 changes: 5 additions & 0 deletions internal/decoder/array.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package decoder

import (
"fmt"
"unsafe"

"github.com/goccy/go-json/internal/errors"
Expand Down Expand Up @@ -167,3 +168,7 @@ func (d *arrayDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
}
}
}

func (d *arrayDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: array decoder does not support decode path")
}
Loading