diff --git a/README.md b/README.md index 059570f..5a1a374 100644 --- a/README.md +++ b/README.md @@ -123,11 +123,12 @@ nil, for JSON null To directly access the value: ```go -result.Type // can be String, Number, True, False, Null, or JSON -result.Str // holds the string -result.Num // holds the float64 number -result.Raw // holds the raw json -result.Index // index of raw value in original json, zero means index unknown +result.Type // can be String, Number, True, False, Null, or JSON +result.Str // holds the string +result.Num // holds the float64 number +result.Raw // holds the raw json +result.Index // index of raw value in original json, zero means index unknown +result.HashtagIndexes // hashtagIndexes 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: diff --git a/gjson.go b/gjson.go index b00fb59..ac121c4 100644 --- a/gjson.go +++ b/gjson.go @@ -64,8 +64,8 @@ type Result struct { Num float64 // Index of raw value in original json, zero means index unknown Index int - // ArrayIndex is the Index of each returned element in the original json - ArrayIndex []int + // HashtagIndexes contains the Indexes of the elements returned by a query containing the '#' character + HashtagIndexes []int } // String returns a string representation of the value. @@ -1263,6 +1263,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { var alog []int var partidx int var multires []byte + var hashtagQueryIndex []int rp := parseArrayPath(path) if !rp.arrch { n, ok := parseUint(rp.part) @@ -1283,6 +1284,10 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { multires = append(multires, '[') } } + var tmp parseContext + tmp.value = qval + fillIndex(c.json, &tmp) + parentIndex := tmp.value.Index var res Result if qval.Type == JSON { res = qval.Get(rp.query.path) @@ -1314,6 +1319,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { multires = append(multires, ',') } multires = append(multires, raw...) + hashtagQueryIndex = append(hashtagQueryIndex, res.Index+parentIndex) } } else { c.value = res @@ -1478,7 +1484,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { c.pipe = right c.piped = true } - var arrayIndex = make([]int, 0, 64) + var hashtagIndexes = make([]int, 0, 64) var jsons = make([]byte, 0, 64) jsons = append(jsons, '[') for j, k := 0, 0; j < len(alog); j++ { @@ -1505,7 +1511,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { raw = res.String() } jsons = append(jsons, []byte(raw)...) - arrayIndex = append(arrayIndex, res.Index+parentIndex) + hashtagIndexes = append(hashtagIndexes, res.Index+parentIndex) k++ } } @@ -1514,7 +1520,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { jsons = append(jsons, ']') c.value.Type = JSON c.value.Raw = string(jsons) - c.value.ArrayIndex = arrayIndex + c.value.HashtagIndexes = hashtagIndexes return i + 1, true } if rp.alogok { @@ -1530,8 +1536,9 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { if !c.value.Exists() { if len(multires) > 0 { c.value = Result{ - Raw: string(append(multires, ']')), - Type: JSON, + Raw: string(append(multires, ']')), + Type: JSON, + HashtagIndexes: hashtagQueryIndex, } } else if rp.query.all { c.value = Result{ @@ -2052,10 +2059,10 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) { res.Raw = val res.Type = JSON } - var c parseContext - c.value = res - fillIndex(json, &c) - return i, c.value, true + var tmp parseContext + tmp.value = res + fillIndex(json, &tmp) + return i, tmp.value, true } if json[i] <= ' ' { continue diff --git a/gjson_test.go b/gjson_test.go index 39a08b3..e326fd7 100644 --- a/gjson_test.go +++ b/gjson_test.go @@ -858,9 +858,9 @@ func TestIssue20(t *testing.T) { } func TestIssue21(t *testing.T) { - json := `{ "Level1Field1":3, - "Level1Field4":4, - "Level1Field2":{ "Level2Field1":[ "value1", "value2" ], + json := `{ "Level1Field1":3, + "Level1Field4":4, + "Level1Field2":{ "Level2Field1":[ "value1", "value2" ], "Level2Field2":{ "Level3Field1":[ { "key1":"value1" } ] } } }` paths := []string{"Level1Field1", "Level1Field2.Level2Field1", "Level1Field2.Level2Field2.Level3Field1", "Level1Field4"} @@ -1491,7 +1491,7 @@ func TestDeepSelectors(t *testing.T) { } }, { - "first": "Roger", "last": "Craig", + "first": "Roger", "last": "Craig", "extra": [40,50,60], "details": { "city": "Phoenix", @@ -2087,48 +2087,73 @@ func TestBoolConvertQuery(t *testing.T) { assert(t, falses == "[3,4,5,9,10,11]") } -func TestArrayIndex(t *testing.T) { - json := `{ +func TestHashtagIndexes(t *testing.T) { + var exampleJSON = `{ "vals": [ - [ - 2, - 2, - { - "wut", - "yup" - } - ], - [ - 4, - 5, - 6 - ] + [1,66,{test: 3}], + [4,5,[6]] + ], + "objectArray":[ + {"first": "Dale", "age": 44}, + {"first": "Roger", "age": 68}, ] }` - r := Get(json, `vals.#.2`) - fmt.Println(r.ArrayIndex) - fmt.Println(string(json[37])) - all := Get(json, `@this`) + testCases := []struct { + path string + expected []string + }{ + { + `vals.#.1`, + []string{`6`, "5"}, + }, + { + `vals.#.2`, + []string{"{", "["}, + }, + { + `objectArray.#(age>43)#.first`, + []string{`"`, `"`}, + }, + } + + for _, tc := range testCases { + r := Get(exampleJSON, tc.path) + + assert(t, len(r.HashtagIndexes) == len(tc.expected)) + + for i, a := range r.HashtagIndexes { + 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 { - println(value.Raw, "index", value.Index) - println(string(json[value.Index : value.Index+len(value.Raw)])) if value.IsArray() { value.ForEach(func(_, v Result) bool { - println(v.Raw, "index", v.Index) - parentIndex := value.Index + v.Index - println(string(json[parentIndex : parentIndex+len(v.Raw)])) - if v.IsArray() { v.ForEach(func(_, sv Result) bool { - println(sv.Raw, "index", sv.Index+parentIndex) - println(string(json[sv.Index+parentIndex : sv.Index+parentIndex+len(sv.Raw)])) + if sv.IsObject() { + assert(t, string(exampleJSON[r.HashtagIndexes[0]:r.HashtagIndexes[0]+len(sv.Raw)]) == sv.Raw) + } + if sv.IsArray() { + assert(t, string(exampleJSON[r.HashtagIndexes[1]:r.HashtagIndexes[1]+len(sv.Raw)]) == sv.Raw) + } return true }) } return true }) } - return true // keep iterating + return true }) }