Skip to content
Open
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
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
module github.com/PaesslerAG/jsonpath

go 1.15
go 1.23.4

require (
github.com/PaesslerAG/gval v1.2.2
github.com/google/go-cmp v0.5.9
gopkg.in/yaml.v3 v3.0.1
)

require github.com/shopspring/decimal v1.3.1 // indirect
195 changes: 190 additions & 5 deletions jsonpath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
type jsonpathTest struct {
name string
path string
data string
data any
lang gval.Language
reorder bool
want interface{}
Expand All @@ -24,8 +24,8 @@ type jsonpathTest struct {
wantParseErr bool
}

type obj = map[string]interface{}
type arr = []interface{}
type obj = map[string]any
type arr = []any

func TestJsonPath(t *testing.T) {

Expand All @@ -48,6 +48,15 @@ func TestJsonPath(t *testing.T) {
"$": obj{"a": "aa"},
},
},
{
name: "root map",
path: "$",
data: obj{"a": "aa"},
want: obj{"a": "aa"},
wantWithPaths: obj{
"$": obj{"a": "aa"},
},
},
{
name: "simple select array",
path: "$[1]",
Expand All @@ -57,6 +66,15 @@ func TestJsonPath(t *testing.T) {
`$["1"]`: "hey",
},
},
{
name: "simple select array2",
path: "$[1]",
data: arr{7, "hey"},
want: "hey",
wantWithPaths: obj{
`$["1"]`: "hey",
},
},
{
name: "negative select array",
path: "$[-1]",
Expand All @@ -66,6 +84,15 @@ func TestJsonPath(t *testing.T) {
`$["-1"]`: "hey",
},
},
{
name: "negative select array2",
path: "$[-1]",
data: arr{7, "hey"},
want: "hey",
wantWithPaths: obj{
`$["-1"]`: "hey",
},
},
{
name: "negative select on short array",
path: "$[-2]",
Expand All @@ -84,6 +111,15 @@ func TestJsonPath(t *testing.T) {
`$["1"]`: "aa",
},
},
{
name: "simple select object2",
path: "$[1]",
data: obj{"1": "aa"},
want: "aa",
wantWithPaths: obj{
`$["1"]`: "aa",
},
},
{
name: "simple select out of bounds",
path: "$[1]",
Expand All @@ -93,12 +129,27 @@ func TestJsonPath(t *testing.T) {
`$["1"]`: nil,
},
},
{
name: "simple select out of bounds2",
path: "$[1]",
data: arr{"hey"},
want: nil,
wantWithPaths: obj{
`$["1"]`: nil,
},
},
{
name: "simple select unknown key",
path: "$[1]",
data: `{"2":"aa"}`,
wantErr: true,
},
{
name: "simple select unknown key2",
path: "$[1]",
data: obj{"2": "aa"},
wantErr: true,
},
{
name: "select array",
path: "$[3].a",
Expand All @@ -108,6 +159,15 @@ func TestJsonPath(t *testing.T) {
`$["3"]["a"]`: "bb",
},
},
{
name: "select array2",
path: "$[3].a",
data: arr{55, 41, 70, obj{"a": "bb"}},
want: "bb",
wantWithPaths: obj{
`$["3"]["a"]`: "bb",
},
},
{
name: "select object",
path: "$[3].a",
Expand All @@ -117,6 +177,24 @@ func TestJsonPath(t *testing.T) {
`$["3"]["a"]`: "aa",
},
},
{
name: "select object2",
path: "$[3].a",
data: obj{"3": obj{"a": "aa"}},
want: "aa",
wantWithPaths: obj{
`$["3"]["a"]`: "aa",
},
},
{
name: "select object3",
path: "$[3].a",
data: map[string]obj{"3": {"a": "aa"}},
want: "aa",
wantWithPaths: obj{
`$["3"]["a"]`: "aa",
},
},
{
name: "range array",
path: "$[2:6].a",
Expand All @@ -126,13 +204,36 @@ func TestJsonPath(t *testing.T) {
`$["3"]["a"]`: "bb",
},
},
{
name: "range array2",
path: "$[2:6].a",
data: arr{55, 41, 70, obj{"a": "bb"}},
want: arr{"bb"},
wantWithPaths: obj{
`$["3"]["a"]`: "bb",
},
},
{
name: "range object", //no range over objects
path: "$[2:6].a",
data: `{"3":{"a":"aa"}}`,
want: arr{},
wantWithPaths: obj{},
},
{
name: "range object2", //no range over objects
path: "$[2:6].a",
data: obj{"3": obj{"a": "aa"}},
want: arr{},
wantWithPaths: obj{},
},
{
name: "range object3", //no range over objects
path: "$[2:6].a",
data: map[string]obj{"3": {"a": "aa"}},
want: arr{},
wantWithPaths: obj{},
},
{
name: "range multi match",
path: "$[2:6].a",
Expand All @@ -148,6 +249,36 @@ func TestJsonPath(t *testing.T) {
`$["5"]["a"]`: "b3",
},
},
{
name: "range multi match2",
path: "$[2:6].a",
data: arr{obj{"a": "xx"}, 41, obj{"a": "b1"}, obj{"a": "b2"}, 55, obj{"a": "b3"}, obj{"a": "x2"}},
want: arr{
"b1",
"b2",
"b3",
},
wantWithPaths: obj{
`$["2"]["a"]`: "b1",
`$["3"]["a"]`: "b2",
`$["5"]["a"]`: "b3",
},
},
{
name: "range multi match3",
path: "$[2:6].a",
data: []obj{{"a": "xx"}, {"b": 41}, {"a": "b1"}, {"a": "b2"}, {"b": 55}, {"a": "b3"}, {"a": "x2"}},
want: arr{
"b1",
"b2",
"b3",
},
wantWithPaths: obj{
`$["2"]["a"]`: "b1",
`$["3"]["a"]`: "b2",
`$["5"]["a"]`: "b3",
},
},
{
name: "range all",
path: "$[:]",
Expand All @@ -165,6 +296,23 @@ func TestJsonPath(t *testing.T) {
`$["3"]`: obj{"a": "bb"},
},
},
{
name: "range all2",
path: "$[:]",
data: arr{55, 41, 70, obj{"a": "bb"}},
want: arr{
55,
41,
70,
obj{"a": "bb"},
},
wantWithPaths: obj{
`$["0"]`: 55,
`$["1"]`: 41,
`$["2"]`: 70,
`$["3"]`: obj{"a": "bb"},
},
},
{
name: "range all even",
path: "$[::2]",
Expand All @@ -178,6 +326,19 @@ func TestJsonPath(t *testing.T) {
`$["2"]`: 70.,
},
},
{
name: "range all even2",
path: "$[::2]",
data: arr{55, 41, 70, obj{"a": "bb"}},
want: arr{
55,
70,
},
wantWithPaths: obj{
`$["0"]`: 55,
`$["2"]`: 70,
},
},
{
name: "range all even reverse",
path: "$[::-2]",
Expand All @@ -191,6 +352,19 @@ func TestJsonPath(t *testing.T) {
`$["1"]`: 41.,
},
},
{
name: "range all even reverse2",
path: "$[::-2]",
data: arr{55, 41, 70, obj{"a": "bb"}},
want: arr{
obj{"a": "bb"},
41,
},
wantWithPaths: obj{
`$["3"]`: obj{"a": "bb"},
`$["1"]`: 41,
},
},
{
name: "range reverse",
path: "$[2:6:-1].a",
Expand Down Expand Up @@ -664,7 +838,11 @@ func TestJsonPath(t *testing.T) {
},
},
}
runCase := ""
for _, tt := range tests {
if runCase != "" && tt.name != runCase {
continue
}
tt.lang = jsonpath.Language()
t.Run(tt.name, tt.test)
}
Expand All @@ -678,8 +856,15 @@ func (tt jsonpathTest) test(t *testing.T) {
if tt.wantParseErr {
return
}
var v interface{}
err = json.Unmarshal([]byte(tt.data), &v)
var v any
switch d := tt.data.(type) {
case string:
err = json.Unmarshal([]byte(d), &v)
case []byte:
err = json.Unmarshal(d, &v)
default:
v = d
}
if err != nil {
t.Fatalf("[%s]: could not parse json input: %v", tt.name, err)
}
Expand Down
11 changes: 6 additions & 5 deletions path.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,12 @@ func (p *ambiguousPath) visitMatchs(ctx context.Context, r interface{}, visit pa
})
}

func (p *ambiguousPath) branchMatcher(ctx context.Context, r interface{}, m ambiguousMatcher) ambiguousMatcher {
return func(k, v interface{}) {
p.branch(ctx, r, v, m)
}
}
// unused
// func (p *ambiguousPath) branchMatcher(ctx context.Context, r interface{}, m ambiguousMatcher) ambiguousMatcher {
// return func(k, v interface{}) {
// p.branch(ctx, r, v, m)
// }
// }

func (p *ambiguousPath) withPlainSelector(selector plainSelector) path {
p.ending = append(p.ending, selector)
Expand Down
4 changes: 1 addition & 3 deletions placeholder.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ func parseJSONObject(ctx context.Context, p *gval.Parser) (gval.Evaluable, error
return nil, err
}
if p.Scan() != ':' {
if err != nil {
return nil, p.Expected("object", ':')
}
return nil, p.Expected("object", ':')
}
e, err := parseJSONObjectElement(ctx, p, hasWildcard, key)
if err != nil {
Expand Down
Loading