oracle: add max nesting depth to JSONPath filter
This commit is contained in:
parent
1c30d8c395
commit
6890688b8f
3 changed files with 85 additions and 6 deletions
|
@ -31,7 +31,6 @@ func TestFilter(t *testing.T) {
|
|||
}{
|
||||
{"[]", "$.Name"},
|
||||
{`["Acme Co"]`, "$.Manufacturers[0].Name"},
|
||||
{`["Acme Co"]`, "$..Manufacturers[0].Name"},
|
||||
{`[50]`, "$.Manufacturers[0].Products[0].Price"},
|
||||
{`["Elbow Grease"]`, "$.Manufacturers[1].Products[0].Name"},
|
||||
{`[{"Name":"Elbow Grease","Price":99.95}]`, "$.Manufacturers[1].Products[0]"},
|
||||
|
|
|
@ -13,8 +13,9 @@ type (
|
|||
|
||||
// pathParser combines JSONPath and a position to start parsing from.
|
||||
pathParser struct {
|
||||
s string
|
||||
i int
|
||||
s string
|
||||
i int
|
||||
depth int
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -32,10 +33,15 @@ const (
|
|||
pathNumber
|
||||
)
|
||||
|
||||
const maxNestingDepth = 6
|
||||
|
||||
// Get returns substructures of value selected by path.
|
||||
// The result is always non-nil unless path is invalid.
|
||||
func Get(path string, value interface{}) ([]interface{}, bool) {
|
||||
p := pathParser{s: path}
|
||||
p := pathParser{
|
||||
depth: maxNestingDepth,
|
||||
s: path,
|
||||
}
|
||||
|
||||
typ, _ := p.nextToken()
|
||||
if typ != pathRoot {
|
||||
|
@ -177,6 +183,11 @@ func (p *pathParser) processDot(objs []interface{}) ([]interface{}, bool) {
|
|||
// descend descends 1 level down.
|
||||
// It flattens arrays and returns map values for maps.
|
||||
func (p *pathParser) descend(objs []interface{}) ([]interface{}, bool) {
|
||||
if p.depth <= 0 {
|
||||
return nil, false
|
||||
}
|
||||
p.depth--
|
||||
|
||||
var values []interface{}
|
||||
for i := range objs {
|
||||
switch obj := objs[i].(type) {
|
||||
|
@ -209,7 +220,7 @@ func (p *pathParser) descendRecursive(objs []interface{}) ([]interface{}, bool)
|
|||
var values []interface{}
|
||||
|
||||
for len(objs) > 0 {
|
||||
newObjs, _ := p.descendByIdent(objs, val)
|
||||
newObjs, _ := p.descendByIdentAux(objs, false, val)
|
||||
values = append(values, newObjs...)
|
||||
objs, _ = p.descend(objs)
|
||||
}
|
||||
|
@ -219,6 +230,17 @@ func (p *pathParser) descendRecursive(objs []interface{}) ([]interface{}, bool)
|
|||
|
||||
// descendByIdent performs map's field access by name.
|
||||
func (p *pathParser) descendByIdent(objs []interface{}, names ...string) ([]interface{}, bool) {
|
||||
return p.descendByIdentAux(objs, true, names...)
|
||||
}
|
||||
|
||||
func (p *pathParser) descendByIdentAux(objs []interface{}, checkDepth bool, names ...string) ([]interface{}, bool) {
|
||||
if checkDepth {
|
||||
if p.depth <= 0 {
|
||||
return nil, false
|
||||
}
|
||||
p.depth--
|
||||
}
|
||||
|
||||
var values []interface{}
|
||||
for i := range objs {
|
||||
obj, ok := objs[i].(map[string]interface{})
|
||||
|
@ -237,6 +259,11 @@ func (p *pathParser) descendByIdent(objs []interface{}, names ...string) ([]inte
|
|||
|
||||
// descendByIndex performs array access by index.
|
||||
func (p *pathParser) descendByIndex(objs []interface{}, indices ...int) ([]interface{}, bool) {
|
||||
if p.depth <= 0 {
|
||||
return nil, false
|
||||
}
|
||||
p.depth--
|
||||
|
||||
var values []interface{}
|
||||
for i := range objs {
|
||||
obj, ok := objs[i].([]interface{})
|
||||
|
@ -382,6 +409,11 @@ func (p *pathParser) processSlice(objs []interface{}, start int) ([]interface{},
|
|||
|
||||
// descendByRange is similar to descend but skips maps and returns sub-slices for arrays.
|
||||
func (p *pathParser) descendByRange(objs []interface{}, start, end int) ([]interface{}, bool) {
|
||||
if p.depth <= 0 {
|
||||
return nil, false
|
||||
}
|
||||
p.depth--
|
||||
|
||||
var values []interface{}
|
||||
for i := range objs {
|
||||
arr, ok := objs[i].([]interface{})
|
||||
|
|
|
@ -98,7 +98,6 @@ func TestDescendByIdent(t *testing.T) {
|
|||
{"$.*.name", `["small","big"]`},
|
||||
{"$..store.name", `["big"]`},
|
||||
{"$.store..name", `["big","ppp","sub1","sub2"]`},
|
||||
{"$..sub[*].name", `["sub1","sub2"]`},
|
||||
{"$..sub.name", `[]`},
|
||||
{"$..sub..name", `["sub1","sub2"]`},
|
||||
}
|
||||
|
@ -107,6 +106,28 @@ func TestDescendByIdent(t *testing.T) {
|
|||
tc.testUnmarshalGet(t, js)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("big depth", func(t *testing.T) {
|
||||
js := `{"a":{"b":{"c":{"d":{"e":{"f":{"g":1}}}}}}}`
|
||||
t.Run("single field", func(t *testing.T) {
|
||||
t.Run("max", func(t *testing.T) {
|
||||
p := pathTestCase{"$.a.b.c.d.e.f", `[{"g":1}]`}
|
||||
p.testUnmarshalGet(t, js)
|
||||
})
|
||||
|
||||
_, ok := unmarshalGet(t, js, "$.a.b.c.d.e.f.g")
|
||||
require.False(t, ok)
|
||||
})
|
||||
t.Run("wildcard", func(t *testing.T) {
|
||||
t.Run("max", func(t *testing.T) {
|
||||
p := pathTestCase{"$.*.*.*.*.*.*", `[{"g":1}]`}
|
||||
p.testUnmarshalGet(t, js)
|
||||
})
|
||||
|
||||
_, ok := unmarshalGet(t, js, "$.*.*.*.*.*.*.*")
|
||||
require.False(t, ok)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDescendByIndex(t *testing.T) {
|
||||
|
@ -131,6 +152,28 @@ func TestDescendByIndex(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
t.Run("big depth", func(t *testing.T) {
|
||||
js := `[[[[[[[1]]]]]]]`
|
||||
t.Run("single index", func(t *testing.T) {
|
||||
t.Run("max", func(t *testing.T) {
|
||||
p := pathTestCase{"$[0][0][0][0][0][0]", "[[1]]"}
|
||||
p.testUnmarshalGet(t, js)
|
||||
})
|
||||
|
||||
_, ok := unmarshalGet(t, js, "$[0][0][0][0][0][0][0]")
|
||||
require.False(t, ok)
|
||||
})
|
||||
t.Run("slice", func(t *testing.T) {
|
||||
t.Run("max", func(t *testing.T) {
|
||||
p := pathTestCase{"$[0:][0:][0:][0:][0:][0:]", "[[1]]"}
|
||||
p.testUnmarshalGet(t, js)
|
||||
})
|
||||
|
||||
_, ok := unmarshalGet(t, js, "$[0:][0:][0:][0:][0:][0:][0:]")
|
||||
require.False(t, ok)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("$[:][1], skip wrong types", func(t *testing.T) {
|
||||
js := `[[1,2],{"1":"4"},[5,6]]`
|
||||
p := pathTestCase{"$[:][1]", "[2,6]"}
|
||||
|
@ -227,4 +270,9 @@ func TestCSharpCompat(t *testing.T) {
|
|||
tc.testUnmarshalGet(t, js)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("bad cases", func(t *testing.T) {
|
||||
_, ok := unmarshalGet(t, js, `$..book[*].author"`)
|
||||
require.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue