mirror of https://github.com/tidwall/gjson.git
Allow for modifiers in sub-selectors
This commit is contained in:
parent
c5e72cdf74
commit
1c258afe09
36
gjson.go
36
gjson.go
|
@ -464,9 +464,9 @@ func ParseBytes(json []byte) Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
func squash(json string) string {
|
func squash(json string) string {
|
||||||
// expects that the lead character is a '[' or '{'
|
// expects that the lead character is a '[' or '{' or '('
|
||||||
// squash the value, ignoring all nested arrays and objects.
|
// squash the value, ignoring all nested arrays and objects.
|
||||||
// the first '[' or '{' has already been read
|
// the first '[' or '{' or '(', has already been read
|
||||||
depth := 1
|
depth := 1
|
||||||
for i := 1; i < len(json); i++ {
|
for i := 1; i < len(json); i++ {
|
||||||
if json[i] >= '"' && json[i] <= '}' {
|
if json[i] >= '"' && json[i] <= '}' {
|
||||||
|
@ -495,9 +495,9 @@ func squash(json string) string {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case '{', '[':
|
case '{', '[', '(':
|
||||||
depth++
|
depth++
|
||||||
case '}', ']':
|
case '}', ']', ')':
|
||||||
depth--
|
depth--
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
return json[:i+1]
|
return json[:i+1]
|
||||||
|
@ -1056,9 +1056,9 @@ func parseObjectPath(path string) (r objectPathResult) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSquash(json string, i int) (int, string) {
|
func parseSquash(json string, i int) (int, string) {
|
||||||
// expects that the lead character is a '[' or '{'
|
// expects that the lead character is a '[' or '{' or '('
|
||||||
// squash the value, ignoring all nested arrays and objects.
|
// squash the value, ignoring all nested arrays and objects.
|
||||||
// the first '[' or '{' has already been read
|
// the first '[' or '{' or '(' has already been read
|
||||||
s := i
|
s := i
|
||||||
i++
|
i++
|
||||||
depth := 1
|
depth := 1
|
||||||
|
@ -1089,9 +1089,9 @@ func parseSquash(json string, i int) (int, string) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case '{', '[':
|
case '{', '[', '(':
|
||||||
depth++
|
depth++
|
||||||
case '}', ']':
|
case '}', ']', ')':
|
||||||
depth--
|
depth--
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
i++
|
i++
|
||||||
|
@ -1615,10 +1615,21 @@ func splitPossiblePipe(path string) (left, right string, ok bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(path) > 0 && path[0] == '{' {
|
||||||
|
squashed := squash(path[1:])
|
||||||
|
if len(squashed) < len(path)-1 {
|
||||||
|
squashed = path[:len(squashed)+1]
|
||||||
|
remain := path[len(squashed):]
|
||||||
|
if remain[0] == '|' {
|
||||||
|
return squashed, remain[1:], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// split the left and right side of the path with the pipe character as
|
// split the left and right side of the path with the pipe character as
|
||||||
// the delimiter. This is a little tricky because we'll need to basically
|
// the delimiter. This is a little tricky because we'll need to basically
|
||||||
// parse the entire path.
|
// parse the entire path.
|
||||||
|
|
||||||
for i := 0; i < len(path); i++ {
|
for i := 0; i < len(path); i++ {
|
||||||
if path[i] == '\\' {
|
if path[i] == '\\' {
|
||||||
i++
|
i++
|
||||||
|
@ -1699,6 +1710,7 @@ type subSelector struct {
|
||||||
// first character in path is either '[' or '{', and has already been checked
|
// first character in path is either '[' or '{', and has already been checked
|
||||||
// prior to calling this function.
|
// prior to calling this function.
|
||||||
func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) {
|
func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) {
|
||||||
|
modifer := 0
|
||||||
depth := 1
|
depth := 1
|
||||||
colon := 0
|
colon := 0
|
||||||
start := 1
|
start := 1
|
||||||
|
@ -1719,8 +1731,12 @@ func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) {
|
||||||
switch path[i] {
|
switch path[i] {
|
||||||
case '\\':
|
case '\\':
|
||||||
i++
|
i++
|
||||||
|
case '@':
|
||||||
|
if modifer == 0 && i > 0 && (path[i-1] == '.' || path[i-1] == '|') {
|
||||||
|
modifer = i
|
||||||
|
}
|
||||||
case ':':
|
case ':':
|
||||||
if depth == 1 {
|
if modifer == 0 && colon == 0 && depth == 1 {
|
||||||
colon = i
|
colon = i
|
||||||
}
|
}
|
||||||
case ',':
|
case ',':
|
||||||
|
|
|
@ -1990,3 +1990,29 @@ func TestSingleModifier(t *testing.T) {
|
||||||
assert(t, Get(data, "@key").String() == "value")
|
assert(t, Get(data, "@key").String() == "value")
|
||||||
assert(t, Get(data, "\\@key").String() == "value")
|
assert(t, Get(data, "\\@key").String() == "value")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestModifierInSubSelector(t *testing.T) {
|
||||||
|
AddModifier("case", func(json, arg string) string {
|
||||||
|
if arg == "upper" {
|
||||||
|
return strings.ToUpper(json)
|
||||||
|
}
|
||||||
|
if arg == "lower" {
|
||||||
|
return strings.ToLower(json)
|
||||||
|
}
|
||||||
|
return json
|
||||||
|
})
|
||||||
|
json := `{"friends": [
|
||||||
|
{"age": 44, "first": "Dale", "last": "Murphy"},
|
||||||
|
{"age": 68, "first": "Roger", "last": "Craig"},
|
||||||
|
{"age": 47, "first": "Jane", "last": "Murphy"}
|
||||||
|
]}`
|
||||||
|
|
||||||
|
res := Get(json, `friends.#.{age,first|@case:upper}|@ugly`)
|
||||||
|
exp := `[{"age":44,"@case:upper":"DALE"},{"age":68,"@case:upper":"ROGER"},{"age":47,"@case:upper":"JANE"}]`
|
||||||
|
assert(t, res.Raw == exp)
|
||||||
|
|
||||||
|
res = Get(json, `{friends.#.{age,first:first|@case:upper}|0.first}`)
|
||||||
|
exp = `{"first":"DALE"}`
|
||||||
|
assert(t, res.Raw == exp)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue