Merge pull request #222 from sspaink/arrayindex

Add support to return multiple indexes when multiple matches are found
This commit is contained in:
Josh Baker 2021-09-01 07:17:47 -07:00 committed by GitHub
commit 52919fa7b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 110 additions and 16 deletions

View File

@ -123,11 +123,12 @@ nil, for JSON null
To directly access the value: To directly access the value:
```go ```go
result.Type // can be String, Number, True, False, Null, or JSON result.Type // can be String, Number, True, False, Null, or JSON
result.Str // holds the string result.Str // holds the string
result.Num // holds the float64 number result.Num // holds the float64 number
result.Raw // holds the raw json result.Raw // holds the raw json
result.Index // index of raw value in original json, zero means index unknown result.Index // index of raw value in original json, zero means index unknown
result.Indexes // Indexes contains the indexes of the elements returned by a query containing the '#' character
``` ```
There are a variety of handy functions that work on a result: There are a variety of handy functions that work on a result:

View File

@ -64,6 +64,8 @@ type Result struct {
Num float64 Num float64
// Index of raw value in original json, zero means index unknown // Index of raw value in original json, zero means index unknown
Index int Index int
// Indexes contains the Indexes of the elements returned by a query containing the '#' character
Indexes []int
} }
// String returns a string representation of the value. // String returns a string representation of the value.
@ -1261,6 +1263,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
var alog []int var alog []int
var partidx int var partidx int
var multires []byte var multires []byte
var queryIndexes []int
rp := parseArrayPath(path) rp := parseArrayPath(path)
if !rp.arrch { if !rp.arrch {
n, ok := parseUint(rp.part) n, ok := parseUint(rp.part)
@ -1281,6 +1284,10 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
multires = append(multires, '[') multires = append(multires, '[')
} }
} }
var tmp parseContext
tmp.value = qval
fillIndex(c.json, &tmp)
parentIndex := tmp.value.Index
var res Result var res Result
if qval.Type == JSON { if qval.Type == JSON {
res = qval.Get(rp.query.path) res = qval.Get(rp.query.path)
@ -1312,6 +1319,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
multires = append(multires, ',') multires = append(multires, ',')
} }
multires = append(multires, raw...) multires = append(multires, raw...)
queryIndexes = append(queryIndexes, res.Index+parentIndex)
} }
} else { } else {
c.value = res c.value = res
@ -1476,6 +1484,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
c.pipe = right c.pipe = right
c.piped = true c.piped = true
} }
var indexes = make([]int, 0, 64)
var jsons = make([]byte, 0, 64) var jsons = make([]byte, 0, 64)
jsons = append(jsons, '[') jsons = append(jsons, '[')
for j, k := 0, 0; j < len(alog); j++ { for j, k := 0, 0; j < len(alog); j++ {
@ -1490,6 +1499,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
} }
if idx < len(c.json) && c.json[idx] != ']' { if idx < len(c.json) && c.json[idx] != ']' {
_, res, ok := parseAny(c.json, idx, true) _, res, ok := parseAny(c.json, idx, true)
parentIndex := res.Index
if ok { if ok {
res := res.Get(rp.alogkey) res := res.Get(rp.alogkey)
if res.Exists() { if res.Exists() {
@ -1501,6 +1511,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
raw = res.String() raw = res.String()
} }
jsons = append(jsons, []byte(raw)...) jsons = append(jsons, []byte(raw)...)
indexes = append(indexes, res.Index+parentIndex)
k++ k++
} }
} }
@ -1509,6 +1520,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
jsons = append(jsons, ']') jsons = append(jsons, ']')
c.value.Type = JSON c.value.Type = JSON
c.value.Raw = string(jsons) c.value.Raw = string(jsons)
c.value.Indexes = indexes
return i + 1, true return i + 1, true
} }
if rp.alogok { if rp.alogok {
@ -1524,8 +1536,9 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
if !c.value.Exists() { if !c.value.Exists() {
if len(multires) > 0 { if len(multires) > 0 {
c.value = Result{ c.value = Result{
Raw: string(append(multires, ']')), Raw: string(append(multires, ']')),
Type: JSON, Type: JSON,
Indexes: queryIndexes,
} }
} else if rp.query.all { } else if rp.query.all {
c.value = Result{ c.value = Result{
@ -1806,6 +1819,7 @@ func Get(json, path string) Result {
if len(path) > 0 && (path[0] == '|' || path[0] == '.') { if len(path) > 0 && (path[0] == '|' || path[0] == '.') {
res := Get(rjson, path[1:]) res := Get(rjson, path[1:])
res.Index = 0 res.Index = 0
res.Indexes = nil
return res return res
} }
return Parse(rjson) return Parse(rjson)
@ -2046,7 +2060,10 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) {
res.Raw = val res.Raw = val
res.Type = JSON res.Type = JSON
} }
return i, res, true var tmp parseContext
tmp.value = res
fillIndex(json, &tmp)
return i, tmp.value, true
} }
if json[i] <= ' ' { if json[i] <= ' ' {
continue continue

View File

@ -859,9 +859,9 @@ func TestIssue20(t *testing.T) {
} }
func TestIssue21(t *testing.T) { func TestIssue21(t *testing.T) {
json := `{ "Level1Field1":3, json := `{ "Level1Field1":3,
"Level1Field4":4, "Level1Field4":4,
"Level1Field2":{ "Level2Field1":[ "value1", "value2" ], "Level1Field2":{ "Level2Field1":[ "value1", "value2" ],
"Level2Field2":{ "Level3Field1":[ { "key1":"value1" } ] } } }` "Level2Field2":{ "Level3Field1":[ { "key1":"value1" } ] } } }`
paths := []string{"Level1Field1", "Level1Field2.Level2Field1", paths := []string{"Level1Field1", "Level1Field2.Level2Field1",
"Level1Field2.Level2Field2.Level3Field1", "Level1Field4"} "Level1Field2.Level2Field2.Level3Field1", "Level1Field4"}
@ -922,7 +922,7 @@ var complicatedJSON = `
"nestedTagged": { "nestedTagged": {
"Green": "Green", "Green": "Green",
"Map": { "Map": {
"this": "that", "this": "that",
"and": "the other thing" "and": "the other thing"
}, },
"Ints": { "Ints": {
@ -1291,10 +1291,10 @@ func TestArrayValues(t *testing.T) {
} }
expect := strings.Join([]string{ expect := strings.Join([]string{
`gjson.Result{Type:3, Raw:"\"PERSON1\"", Str:"PERSON1", Num:0, ` + `gjson.Result{Type:3, Raw:"\"PERSON1\"", Str:"PERSON1", Num:0, ` +
`Index:0}`, `Index:0, Indexes:[]int(nil)}`,
`gjson.Result{Type:3, Raw:"\"PERSON2\"", Str:"PERSON2", Num:0, ` + `gjson.Result{Type:3, Raw:"\"PERSON2\"", Str:"PERSON2", Num:0, ` +
`Index:0}`, `Index:0, Indexes:[]int(nil)}`,
`gjson.Result{Type:2, Raw:"0", Str:"", Num:0, Index:0}`, `gjson.Result{Type:2, Raw:"0", Str:"", Num:0, Index:0, Indexes:[]int(nil)}`,
}, "\n") }, "\n")
if output != expect { if output != expect {
t.Fatalf("expected '%v', got '%v'", expect, output) t.Fatalf("expected '%v', got '%v'", expect, output)
@ -1492,7 +1492,7 @@ func TestDeepSelectors(t *testing.T) {
} }
}, },
{ {
"first": "Roger", "last": "Craig", "first": "Roger", "last": "Craig",
"extra": [40,50,60], "extra": [40,50,60],
"details": { "details": {
"city": "Phoenix", "city": "Phoenix",
@ -2119,4 +2119,80 @@ func TestModifierDoubleQuotes(t *testing.T) {
`{"name":"Product P4","value":"{\"productId\":\"1cc3\",\"vendorId\":\"20de\"}"},`+ `{"name":"Product P4","value":"{\"productId\":\"1cc3\",\"vendorId\":\"20de\"}"},`+
`{"name":"Product P4","value":"{\"productId\":\"1dd3\",\"vendorId\":\"30de\"}"}`+ `{"name":"Product P4","value":"{\"productId\":\"1dd3\",\"vendorId\":\"30de\"}"}`+
`]`) `]`)
}
func TestIndexes(t *testing.T) {
var exampleJSON = `{
"vals": [
[1,66,{test: 3}],
[4,5,[6]]
],
"objectArray":[
{"first": "Dale", "age": 44},
{"first": "Roger", "age": 68},
]
}`
testCases := []struct {
path string
expected []string
}{
{
`vals.#.1`,
[]string{`6`, "5"},
},
{
`vals.#.2`,
[]string{"{", "["},
},
{
`objectArray.#(age>43)#.first`,
[]string{`"`, `"`},
},
{
`objectArray.@reverse.#.first`,
nil,
},
}
for _, tc := range testCases {
r := Get(exampleJSON, tc.path)
assert(t, len(r.Indexes) == len(tc.expected))
for i, a := range r.Indexes {
assert(t, string(exampleJSON[a]) == tc.expected[i])
}
}
}
func TestHashtagIndexesMatchesRaw(t *testing.T) {
var exampleJSON = `{
"objectArray":[
{"first": "Dale", "age": 44},
{"first": "Roger", "age": 68},
]
}`
r := Get(exampleJSON, `objectArray.#(age>43)#.first`)
all := Get(exampleJSON, `@this`)
all.ForEach(func(_, value Result) bool {
if value.IsArray() {
value.ForEach(func(_, v Result) bool {
if v.IsArray() {
v.ForEach(func(_, sv Result) bool {
if sv.IsObject() {
assert(t, string(exampleJSON[r.Indexes[0]:r.Indexes[0]+len(sv.Raw)]) == sv.Raw)
}
if sv.IsArray() {
assert(t, string(exampleJSON[r.Indexes[1]:r.Indexes[1]+len(sv.Raw)]) == sv.Raw)
}
return true
})
}
return true
})
}
return true
})
} }