mirror of https://github.com/tidwall/gjson.git
Add @dig modifier for recursive descent searches
This commit adds the "@dig" modifier, which allows for searching for values in deep or arbitrarily nested json documents For example, using the following json: ``` { "something": { "anything": { "abcdefg": { "finally": { "important": { "secret": "password" } } } } } } ``` ``` @dig:secret -> ["password"] ``` See #130
This commit is contained in:
parent
8d2c36ffa4
commit
e8e87f2a00
79
gjson.go
79
gjson.go
|
@ -2754,6 +2754,7 @@ func execModifier(json, path string) (pathOut, res string, ok bool) {
|
||||||
var parsedArgs bool
|
var parsedArgs bool
|
||||||
switch pathOut[0] {
|
switch pathOut[0] {
|
||||||
case '{', '[', '"':
|
case '{', '[', '"':
|
||||||
|
// json arg
|
||||||
res := Parse(pathOut)
|
res := Parse(pathOut)
|
||||||
if res.Exists() {
|
if res.Exists() {
|
||||||
args = squash(pathOut)
|
args = squash(pathOut)
|
||||||
|
@ -2762,14 +2763,20 @@ func execModifier(json, path string) (pathOut, res string, ok bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !parsedArgs {
|
if !parsedArgs {
|
||||||
idx := strings.IndexByte(pathOut, '|')
|
// simple arg
|
||||||
if idx == -1 {
|
i := 0
|
||||||
args = pathOut
|
for ; i < len(pathOut); i++ {
|
||||||
pathOut = ""
|
if pathOut[i] == '|' {
|
||||||
} else {
|
break
|
||||||
args = pathOut[:idx]
|
}
|
||||||
pathOut = pathOut[idx:]
|
switch pathOut[i] {
|
||||||
|
case '{', '[', '"', '(':
|
||||||
|
s := squash(pathOut[i:])
|
||||||
|
i += len(s) - 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
args = pathOut[:i]
|
||||||
|
pathOut = pathOut[i:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pathOut, fn(json, args), true
|
return pathOut, fn(json, args), true
|
||||||
|
@ -2789,19 +2796,24 @@ func unwrap(json string) string {
|
||||||
// DisableModifiers will disable the modifier syntax
|
// DisableModifiers will disable the modifier syntax
|
||||||
var DisableModifiers = false
|
var DisableModifiers = false
|
||||||
|
|
||||||
var modifiers = map[string]func(json, arg string) string{
|
var modifiers map[string]func(json, arg string) string
|
||||||
"pretty": modPretty,
|
|
||||||
"ugly": modUgly,
|
func init() {
|
||||||
"reverse": modReverse,
|
modifiers = map[string]func(json, arg string) string{
|
||||||
"this": modThis,
|
"pretty": modPretty,
|
||||||
"flatten": modFlatten,
|
"ugly": modUgly,
|
||||||
"join": modJoin,
|
"reverse": modReverse,
|
||||||
"valid": modValid,
|
"this": modThis,
|
||||||
"keys": modKeys,
|
"flatten": modFlatten,
|
||||||
"values": modValues,
|
"join": modJoin,
|
||||||
"tostr": modToStr,
|
"valid": modValid,
|
||||||
"fromstr": modFromStr,
|
"keys": modKeys,
|
||||||
"group": modGroup,
|
"values": modValues,
|
||||||
|
"tostr": modToStr,
|
||||||
|
"fromstr": modFromStr,
|
||||||
|
"group": modGroup,
|
||||||
|
"dig": modDig,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddModifier binds a custom modifier command to the GJSON syntax.
|
// AddModifier binds a custom modifier command to the GJSON syntax.
|
||||||
|
@ -3435,3 +3447,30 @@ func escapeComp(comp string) string {
|
||||||
}
|
}
|
||||||
return comp
|
return comp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseRecursiveDescent(all []Result, parent Result, path string) []Result {
|
||||||
|
if res := parent.Get(path); res.Exists() {
|
||||||
|
all = append(all, res)
|
||||||
|
}
|
||||||
|
if parent.IsArray() || parent.IsObject() {
|
||||||
|
parent.ForEach(func(_, val Result) bool {
|
||||||
|
all = parseRecursiveDescent(all, val, path)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return all
|
||||||
|
}
|
||||||
|
|
||||||
|
func modDig(json, arg string) string {
|
||||||
|
all := parseRecursiveDescent(nil, Parse(json), arg)
|
||||||
|
var out []byte
|
||||||
|
out = append(out, '[')
|
||||||
|
for i, res := range all {
|
||||||
|
if i > 0 {
|
||||||
|
out = append(out, ',')
|
||||||
|
}
|
||||||
|
out = append(out, res.Raw...)
|
||||||
|
}
|
||||||
|
out = append(out, ']')
|
||||||
|
return string(out)
|
||||||
|
}
|
||||||
|
|
|
@ -2636,3 +2636,68 @@ func TestIssue301(t *testing.T) {
|
||||||
assert(t, Get(json, `fav\.movie.[1]`).String() == "[]")
|
assert(t, Get(json, `fav\.movie.[1]`).String() == "[]")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestModDig(t *testing.T) {
|
||||||
|
json := `
|
||||||
|
{
|
||||||
|
|
||||||
|
"group": {
|
||||||
|
"issues": [
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"labels": [
|
||||||
|
"milestone_1",
|
||||||
|
"group:foo",
|
||||||
|
"plan:a",
|
||||||
|
"plan:b"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"refid": "123"
|
||||||
|
},{
|
||||||
|
"fields": {
|
||||||
|
"labels": [
|
||||||
|
"milestone_2",
|
||||||
|
"group:foo",
|
||||||
|
"plan:a",
|
||||||
|
"plan"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"refid": "456"
|
||||||
|
},[
|
||||||
|
{"extra_deep":[{
|
||||||
|
"fields": {
|
||||||
|
"labels": [
|
||||||
|
"milestone_3",
|
||||||
|
"group:foo",
|
||||||
|
"plan:a",
|
||||||
|
"plan"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"refid": "789"
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
assert(t, Get(json, "group.@dig:#(refid=123)|0.fields.labels.0").String() == "milestone_1")
|
||||||
|
assert(t, Get(json, "group.@dig:#(refid=456)|0.fields.labels.0").String() == "milestone_2")
|
||||||
|
assert(t, Get(json, "group.@dig:#(refid=789)|0.fields.labels.0").String() == "milestone_3")
|
||||||
|
json = `
|
||||||
|
{ "something": {
|
||||||
|
"anything": {
|
||||||
|
"abcdefg": {
|
||||||
|
"finally": {
|
||||||
|
"important": {
|
||||||
|
"secret": "password",
|
||||||
|
"name": "jake"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "melinda"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
assert(t, Get(json, "@dig:name").String() == `["melinda","jake"]`)
|
||||||
|
assert(t, Get(json, "@dig:secret").String() == `["password"]`)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue