Skip to content

Commit 52919fa

Browse files
authored
Merge pull request #222 from sspaink/arrayindex
Add support to return multiple indexes when multiple matches are found
2 parents a5e52e5 + 7405f21 commit 52919fa

3 files changed

Lines changed: 110 additions & 16 deletions

File tree

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,12 @@ nil, for JSON null
123123
To directly access the value:
124124

125125
```go
126-
result.Type // can be String, Number, True, False, Null, or JSON
127-
result.Str // holds the string
128-
result.Num // holds the float64 number
129-
result.Raw // holds the raw json
130-
result.Index // index of raw value in original json, zero means index unknown
126+
result.Type // can be String, Number, True, False, Null, or JSON
127+
result.Str // holds the string
128+
result.Num // holds the float64 number
129+
result.Raw // holds the raw json
130+
result.Index // index of raw value in original json, zero means index unknown
131+
result.Indexes // Indexes contains the indexes of the elements returned by a query containing the '#' character
131132
```
132133

133134
There are a variety of handy functions that work on a result:

gjson.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ type Result struct {
6464
Num float64
6565
// Index of raw value in original json, zero means index unknown
6666
Index int
67+
// Indexes contains the Indexes of the elements returned by a query containing the '#' character
68+
Indexes []int
6769
}
6870

6971
// String returns a string representation of the value.
@@ -1261,6 +1263,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
12611263
var alog []int
12621264
var partidx int
12631265
var multires []byte
1266+
var queryIndexes []int
12641267
rp := parseArrayPath(path)
12651268
if !rp.arrch {
12661269
n, ok := parseUint(rp.part)
@@ -1281,6 +1284,10 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
12811284
multires = append(multires, '[')
12821285
}
12831286
}
1287+
var tmp parseContext
1288+
tmp.value = qval
1289+
fillIndex(c.json, &tmp)
1290+
parentIndex := tmp.value.Index
12841291
var res Result
12851292
if qval.Type == JSON {
12861293
res = qval.Get(rp.query.path)
@@ -1312,6 +1319,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
13121319
multires = append(multires, ',')
13131320
}
13141321
multires = append(multires, raw...)
1322+
queryIndexes = append(queryIndexes, res.Index+parentIndex)
13151323
}
13161324
} else {
13171325
c.value = res
@@ -1476,6 +1484,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
14761484
c.pipe = right
14771485
c.piped = true
14781486
}
1487+
var indexes = make([]int, 0, 64)
14791488
var jsons = make([]byte, 0, 64)
14801489
jsons = append(jsons, '[')
14811490
for j, k := 0, 0; j < len(alog); j++ {
@@ -1490,6 +1499,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
14901499
}
14911500
if idx < len(c.json) && c.json[idx] != ']' {
14921501
_, res, ok := parseAny(c.json, idx, true)
1502+
parentIndex := res.Index
14931503
if ok {
14941504
res := res.Get(rp.alogkey)
14951505
if res.Exists() {
@@ -1501,6 +1511,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
15011511
raw = res.String()
15021512
}
15031513
jsons = append(jsons, []byte(raw)...)
1514+
indexes = append(indexes, res.Index+parentIndex)
15041515
k++
15051516
}
15061517
}
@@ -1509,6 +1520,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
15091520
jsons = append(jsons, ']')
15101521
c.value.Type = JSON
15111522
c.value.Raw = string(jsons)
1523+
c.value.Indexes = indexes
15121524
return i + 1, true
15131525
}
15141526
if rp.alogok {
@@ -1524,8 +1536,9 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
15241536
if !c.value.Exists() {
15251537
if len(multires) > 0 {
15261538
c.value = Result{
1527-
Raw: string(append(multires, ']')),
1528-
Type: JSON,
1539+
Raw: string(append(multires, ']')),
1540+
Type: JSON,
1541+
Indexes: queryIndexes,
15291542
}
15301543
} else if rp.query.all {
15311544
c.value = Result{
@@ -1806,6 +1819,7 @@ func Get(json, path string) Result {
18061819
if len(path) > 0 && (path[0] == '|' || path[0] == '.') {
18071820
res := Get(rjson, path[1:])
18081821
res.Index = 0
1822+
res.Indexes = nil
18091823
return res
18101824
}
18111825
return Parse(rjson)
@@ -2046,7 +2060,10 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) {
20462060
res.Raw = val
20472061
res.Type = JSON
20482062
}
2049-
return i, res, true
2063+
var tmp parseContext
2064+
tmp.value = res
2065+
fillIndex(json, &tmp)
2066+
return i, tmp.value, true
20502067
}
20512068
if json[i] <= ' ' {
20522069
continue

gjson_test.go

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -859,9 +859,9 @@ func TestIssue20(t *testing.T) {
859859
}
860860

861861
func TestIssue21(t *testing.T) {
862-
json := `{ "Level1Field1":3,
863-
"Level1Field4":4,
864-
"Level1Field2":{ "Level2Field1":[ "value1", "value2" ],
862+
json := `{ "Level1Field1":3,
863+
"Level1Field4":4,
864+
"Level1Field2":{ "Level2Field1":[ "value1", "value2" ],
865865
"Level2Field2":{ "Level3Field1":[ { "key1":"value1" } ] } } }`
866866
paths := []string{"Level1Field1", "Level1Field2.Level2Field1",
867867
"Level1Field2.Level2Field2.Level3Field1", "Level1Field4"}
@@ -922,7 +922,7 @@ var complicatedJSON = `
922922
"nestedTagged": {
923923
"Green": "Green",
924924
"Map": {
925-
"this": "that",
925+
"this": "that",
926926
"and": "the other thing"
927927
},
928928
"Ints": {
@@ -1291,10 +1291,10 @@ func TestArrayValues(t *testing.T) {
12911291
}
12921292
expect := strings.Join([]string{
12931293
`gjson.Result{Type:3, Raw:"\"PERSON1\"", Str:"PERSON1", Num:0, ` +
1294-
`Index:0}`,
1294+
`Index:0, Indexes:[]int(nil)}`,
12951295
`gjson.Result{Type:3, Raw:"\"PERSON2\"", Str:"PERSON2", Num:0, ` +
1296-
`Index:0}`,
1297-
`gjson.Result{Type:2, Raw:"0", Str:"", Num:0, Index:0}`,
1296+
`Index:0, Indexes:[]int(nil)}`,
1297+
`gjson.Result{Type:2, Raw:"0", Str:"", Num:0, Index:0, Indexes:[]int(nil)}`,
12981298
}, "\n")
12991299
if output != expect {
13001300
t.Fatalf("expected '%v', got '%v'", expect, output)
@@ -1492,7 +1492,7 @@ func TestDeepSelectors(t *testing.T) {
14921492
}
14931493
},
14941494
{
1495-
"first": "Roger", "last": "Craig",
1495+
"first": "Roger", "last": "Craig",
14961496
"extra": [40,50,60],
14971497
"details": {
14981498
"city": "Phoenix",
@@ -2119,4 +2119,80 @@ func TestModifierDoubleQuotes(t *testing.T) {
21192119
`{"name":"Product P4","value":"{\"productId\":\"1cc3\",\"vendorId\":\"20de\"}"},`+
21202120
`{"name":"Product P4","value":"{\"productId\":\"1dd3\",\"vendorId\":\"30de\"}"}`+
21212121
`]`)
2122+
2123+
}
2124+
2125+
func TestIndexes(t *testing.T) {
2126+
var exampleJSON = `{
2127+
"vals": [
2128+
[1,66,{test: 3}],
2129+
[4,5,[6]]
2130+
],
2131+
"objectArray":[
2132+
{"first": "Dale", "age": 44},
2133+
{"first": "Roger", "age": 68},
2134+
]
2135+
}`
2136+
2137+
testCases := []struct {
2138+
path string
2139+
expected []string
2140+
}{
2141+
{
2142+
`vals.#.1`,
2143+
[]string{`6`, "5"},
2144+
},
2145+
{
2146+
`vals.#.2`,
2147+
[]string{"{", "["},
2148+
},
2149+
{
2150+
`objectArray.#(age>43)#.first`,
2151+
[]string{`"`, `"`},
2152+
},
2153+
{
2154+
`objectArray.@reverse.#.first`,
2155+
nil,
2156+
},
2157+
}
2158+
2159+
for _, tc := range testCases {
2160+
r := Get(exampleJSON, tc.path)
2161+
2162+
assert(t, len(r.Indexes) == len(tc.expected))
2163+
2164+
for i, a := range r.Indexes {
2165+
assert(t, string(exampleJSON[a]) == tc.expected[i])
2166+
}
2167+
}
2168+
}
2169+
2170+
func TestHashtagIndexesMatchesRaw(t *testing.T) {
2171+
var exampleJSON = `{
2172+
"objectArray":[
2173+
{"first": "Dale", "age": 44},
2174+
{"first": "Roger", "age": 68},
2175+
]
2176+
}`
2177+
r := Get(exampleJSON, `objectArray.#(age>43)#.first`)
2178+
all := Get(exampleJSON, `@this`)
2179+
all.ForEach(func(_, value Result) bool {
2180+
if value.IsArray() {
2181+
value.ForEach(func(_, v Result) bool {
2182+
if v.IsArray() {
2183+
v.ForEach(func(_, sv Result) bool {
2184+
if sv.IsObject() {
2185+
assert(t, string(exampleJSON[r.Indexes[0]:r.Indexes[0]+len(sv.Raw)]) == sv.Raw)
2186+
}
2187+
if sv.IsArray() {
2188+
assert(t, string(exampleJSON[r.Indexes[1]:r.Indexes[1]+len(sv.Raw)]) == sv.Raw)
2189+
}
2190+
return true
2191+
})
2192+
}
2193+
return true
2194+
})
2195+
}
2196+
return true
2197+
})
21222198
}

0 commit comments

Comments
 (0)