diff --git a/decode.go b/decode.go index b1512ce..74c6ac3 100644 --- a/decode.go +++ b/decode.go @@ -88,6 +88,9 @@ var ( ) func extractFromPath(path *Path, data []byte, optFuncs ...DecodeOptionFunc) ([][]byte, error) { + if path.path.RootSelectorOnly { + return [][]byte{data}, nil + } src := make([]byte, len(data)+1) // append nul byte to the end copy(src, data) diff --git a/internal/decoder/map.go b/internal/decoder/map.go index 4ee8e5c..7a6eea3 100644 --- a/internal/decoder/map.go +++ b/internal/decoder/map.go @@ -265,8 +265,9 @@ func (d *mapDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]b if err != nil { return nil, 0, err } - cursor = skipWhiteSpace(buf, c) + cursor = c } + cursor = skipWhiteSpace(buf, cursor) if buf[cursor] == '}' { cursor++ return ret, cursor, nil diff --git a/internal/decoder/path.go b/internal/decoder/path.go index 33ddcd5..a15ff69 100644 --- a/internal/decoder/path.go +++ b/internal/decoder/path.go @@ -30,6 +30,7 @@ func (b *PathBuilder) Build(buf []rune) (*Path, error) { } return &Path{ node: node, + RootSelectorOnly: node == nil, SingleQuotePathSelector: b.singleQuotePathSelector, DoubleQuotePathSelector: b.doubleQuotePathSelector, }, nil @@ -57,8 +58,8 @@ func (b *PathBuilder) build(buf []rune) (PathNode, error) { } func (b *PathBuilder) buildNextCharIfExists(buf []rune, cursor int) (int, error) { - if len(buf) > cursor+1 { - offset, err := b.buildNext(buf[cursor+1:]) + if len(buf) > cursor { + offset, err := b.buildNext(buf[cursor:]) if err != nil { return 0, err } @@ -166,14 +167,16 @@ func (b *PathBuilder) buildQuoteSelector(buf []rune, sel QuotePathSelector) (int } selector := buf[:cursor] b.addSelectorNode(string(selector)) - return b.buildNextCharIfExists(buf, cursor+1) + b.singleQuotePathSelector = true + return b.buildNextCharIfExists(buf, cursor+2) case '"': if sel != DoubleQuotePathSelector { return 0, errors.ErrInvalidPath("found single quote character in field selector with double quote context") } selector := buf[:cursor] b.addSelectorNode(string(selector)) - return b.buildNextCharIfExists(buf, cursor) + b.doubleQuotePathSelector = true + return b.buildNextCharIfExists(buf, cursor+1) } } return 0, errors.ErrInvalidPath("couldn't find quote character in selector quote path context") @@ -256,7 +259,7 @@ func (b *PathBuilder) buildIndex(buf []rune) (int, error) { return 0, errors.ErrInvalidPath("%q is unexpected index path", buf[:cursor]) } b.addIndexNode(int(index)) - return b.buildNextCharIfExists(buf, cursor) + return b.buildNextCharIfExists(buf, cursor+1) } } return 0, errors.ErrInvalidPath("couldn't find right bracket character in index path context") @@ -311,18 +314,32 @@ const ( type Path struct { node PathNode + RootSelectorOnly bool SingleQuotePathSelector bool DoubleQuotePathSelector bool } func (p *Path) Field(sel string) (PathNode, bool, error) { + if p.node == nil { + return nil, false, nil + } return p.node.Field(sel) } func (p *Path) Get(src, dst reflect.Value) error { + if p.node == nil { + return nil + } return p.node.Get(src, dst) } +func (p *Path) String() string { + if p.node == nil { + return "$" + } + return p.node.String() +} + type PathNode interface { fmt.Stringer Index(idx int) (PathNode, bool, error) diff --git a/internal/decoder/slice.go b/internal/decoder/slice.go index ec4dcd3..30a23e4 100644 --- a/internal/decoder/slice.go +++ b/internal/decoder/slice.go @@ -350,14 +350,16 @@ func (d *sliceDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][ return nil, 0, err } ret = append(ret, buf[start:end]) + cursor = end } } else { c, err := skipValue(buf, cursor, depth) if err != nil { return nil, 0, err } - cursor = skipWhiteSpace(buf, c) + cursor = c } + cursor = skipWhiteSpace(buf, cursor) switch buf[cursor] { case ']': cursor++ diff --git a/path.go b/path.go index 68bb5a2..38abce7 100644 --- a/path.go +++ b/path.go @@ -33,6 +33,11 @@ type Path struct { path *decoder.Path } +// RootSelectorOnly whether only the root selector ($) is used. +func (p *Path) RootSelectorOnly() bool { + return p.path.RootSelectorOnly +} + // UsedSingleQuotePathSelector whether single quote-based escaping was done when building the JSON Path. func (p *Path) UsedSingleQuotePathSelector() bool { return p.path.SingleQuotePathSelector @@ -48,6 +53,11 @@ func (p *Path) Extract(data []byte, optFuncs ...DecodeOptionFunc) ([][]byte, err return extractFromPath(p, data, optFuncs...) } +// PathString returns original JSON Path string. +func (p *Path) PathString() string { + return p.path.String() +} + // Unmarshal extract and decode the value of the part corresponding to JSON Path from the input data. func (p *Path) Unmarshal(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error { contents, err := extractFromPath(p, data, optFuncs...)