forked from mirror/gjson
subquery syntax
This commit is contained in:
parent
4d7d1a76a8
commit
f40fe4ac37
12
README.md
12
README.md
|
@ -79,6 +79,10 @@ The dot and wildcard characters can be escaped with '\'.
|
||||||
"friends.#.first" >> [ "James", "Roger" ]
|
"friends.#.first" >> [ "James", "Roger" ]
|
||||||
"friends.1.last" >> "Craig"
|
"friends.1.last" >> "Craig"
|
||||||
```
|
```
|
||||||
|
To query an array:
|
||||||
|
```
|
||||||
|
"friends.#[last="Murphy"].first" >> "James"
|
||||||
|
```
|
||||||
|
|
||||||
## Result Type
|
## Result Type
|
||||||
|
|
||||||
|
@ -158,6 +162,14 @@ for _,name := range result.Array() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also query an object inside an array:
|
||||||
|
|
||||||
|
```go
|
||||||
|
name := gjson.Get(json, "programmers.#[lastName="Hunter"].firstName")
|
||||||
|
println(name.String()) // prints "Elliotte"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Simple Parse and Get
|
## Simple Parse and Get
|
||||||
|
|
||||||
There's a `Parse(json)` function that will do a simple parse, and `result.Get(path)` that will search a result.
|
There's a `Parse(json)` function that will do a simple parse, and `result.Get(path)` that will search a result.
|
||||||
|
|
168
gjson.go
168
gjson.go
|
@ -499,6 +499,12 @@ type arrayPathResult struct {
|
||||||
alogok bool
|
alogok bool
|
||||||
arrch bool
|
arrch bool
|
||||||
alogkey string
|
alogkey string
|
||||||
|
query struct {
|
||||||
|
on bool
|
||||||
|
path string
|
||||||
|
op string
|
||||||
|
value string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseArrayPath(path string) (r arrayPathResult) {
|
func parseArrayPath(path string) (r arrayPathResult) {
|
||||||
|
@ -511,10 +517,96 @@ func parseArrayPath(path string) (r arrayPathResult) {
|
||||||
}
|
}
|
||||||
if path[i] == '#' {
|
if path[i] == '#' {
|
||||||
r.arrch = true
|
r.arrch = true
|
||||||
if i == 0 && len(path) > 1 && path[1] == '.' {
|
if i == 0 && len(path) > 1 {
|
||||||
r.alogok = true
|
if path[1] == '.' {
|
||||||
r.alogkey = path[2:]
|
r.alogok = true
|
||||||
r.path = path[:1]
|
r.alogkey = path[2:]
|
||||||
|
r.path = path[:1]
|
||||||
|
} else if path[1] == '[' {
|
||||||
|
r.query.on = true
|
||||||
|
// query
|
||||||
|
i += 2
|
||||||
|
// whitespace
|
||||||
|
for ; i < len(path); i++ {
|
||||||
|
if path[i] > ' ' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s := i
|
||||||
|
for ; i < len(path); i++ {
|
||||||
|
if path[i] <= ' ' || path[i] == '=' ||
|
||||||
|
path[i] == '<' || path[i] == '>' ||
|
||||||
|
path[i] == ']' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.query.path = path[s:i]
|
||||||
|
// whitespace
|
||||||
|
for ; i < len(path); i++ {
|
||||||
|
if path[i] > ' ' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i < len(path) {
|
||||||
|
s = i
|
||||||
|
if path[i] == '<' || path[i] == '>' {
|
||||||
|
if i < len(path)-1 && path[i+1] == '=' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else if path[i] == '=' {
|
||||||
|
if i < len(path)-1 && path[i+1] == '=' {
|
||||||
|
s++
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
r.query.op = path[s:i]
|
||||||
|
// whitespace
|
||||||
|
for ; i < len(path); i++ {
|
||||||
|
if path[i] > ' ' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = i
|
||||||
|
for ; i < len(path); i++ {
|
||||||
|
if path[i] == '"' {
|
||||||
|
i++
|
||||||
|
s2 := i
|
||||||
|
for ; i < len(path); i++ {
|
||||||
|
if path[i] > '\\' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if path[i] == '"' {
|
||||||
|
// look for an escaped slash
|
||||||
|
if path[i-1] == '\\' {
|
||||||
|
n := 0
|
||||||
|
for j := i - 2; j > s2-1; j-- {
|
||||||
|
if path[j] != '\\' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
if n%2 == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if path[i] == ']' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i > len(path) {
|
||||||
|
i = len(path)
|
||||||
|
}
|
||||||
|
v := path[s:i]
|
||||||
|
for len(v) > 0 && v[len(v)-1] <= ' ' {
|
||||||
|
v = v[:len(v)-1]
|
||||||
|
}
|
||||||
|
r.query.value = v
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -773,7 +865,60 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
|
||||||
}
|
}
|
||||||
return i, false
|
return i, false
|
||||||
}
|
}
|
||||||
|
func queryMatches(rp *arrayPathResult, value Result) bool {
|
||||||
|
rpv := rp.query.value
|
||||||
|
if len(rpv) > 2 && rpv[0] == '"' && rpv[len(rpv)-1] == '"' {
|
||||||
|
rpv = rpv[1 : len(rpv)-1]
|
||||||
|
}
|
||||||
|
switch value.Type {
|
||||||
|
case String:
|
||||||
|
switch rp.query.op {
|
||||||
|
case "=":
|
||||||
|
return value.Str == rpv
|
||||||
|
case "<":
|
||||||
|
return value.Str < rpv
|
||||||
|
case "<=":
|
||||||
|
return value.Str <= rpv
|
||||||
|
case ">":
|
||||||
|
return value.Str > rpv
|
||||||
|
case ">=":
|
||||||
|
return value.Str >= rpv
|
||||||
|
}
|
||||||
|
case Number:
|
||||||
|
rpvn, _ := strconv.ParseFloat(rpv, 64)
|
||||||
|
switch rp.query.op {
|
||||||
|
case "=":
|
||||||
|
return value.Num == rpvn
|
||||||
|
case "<":
|
||||||
|
return value.Num < rpvn
|
||||||
|
case "<=":
|
||||||
|
return value.Num <= rpvn
|
||||||
|
case ">":
|
||||||
|
return value.Num > rpvn
|
||||||
|
case ">=":
|
||||||
|
return value.Num >= rpvn
|
||||||
|
}
|
||||||
|
case True:
|
||||||
|
switch rp.query.op {
|
||||||
|
case "=":
|
||||||
|
return rpv == "true"
|
||||||
|
case ">":
|
||||||
|
return rpv == "false"
|
||||||
|
case ">=":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case False:
|
||||||
|
switch rp.query.op {
|
||||||
|
case "=":
|
||||||
|
return rpv == "false"
|
||||||
|
case "<":
|
||||||
|
return rpv == "true"
|
||||||
|
case "<=":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
func parseArray(c *parseContext, i int, path string) (int, bool) {
|
func parseArray(c *parseContext, i int, path string) (int, bool) {
|
||||||
var pmatch, vesc, ok, hit bool
|
var pmatch, vesc, ok, hit bool
|
||||||
var val string
|
var val string
|
||||||
|
@ -832,7 +977,18 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
i, val = parseSquash(c.json, i)
|
i, val = parseSquash(c.json, i)
|
||||||
if hit {
|
if rp.query.on {
|
||||||
|
res := Get(val, rp.query.path)
|
||||||
|
if queryMatches(&rp, res) {
|
||||||
|
if rp.more {
|
||||||
|
c.value = Get(val, rp.path)
|
||||||
|
} else {
|
||||||
|
c.value.Raw = val
|
||||||
|
c.value.Type = JSON
|
||||||
|
}
|
||||||
|
return i, true
|
||||||
|
}
|
||||||
|
} else if hit {
|
||||||
if rp.alogok {
|
if rp.alogok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,10 +126,12 @@ var basicJSON = `{"age":100, "name":{"here":"B\\\"R"},
|
||||||
}`
|
}`
|
||||||
|
|
||||||
func TestBasic(t *testing.T) {
|
func TestBasic(t *testing.T) {
|
||||||
//fmt.Printf("%v\n", Parse(basicJSON).Get("items.3.tags.#").String())
|
|
||||||
//return
|
|
||||||
|
|
||||||
var mtok Result
|
var mtok Result
|
||||||
|
|
||||||
|
mtok = Get(basicJSON, `loggy.programmers.#[firstName == "Brett"].email`)
|
||||||
|
if mtok.String() != "aaaa" {
|
||||||
|
t.Fatalf("expected %v, got %v", "aaaa", mtok.String())
|
||||||
|
}
|
||||||
mtok = Get(basicJSON, "loggy")
|
mtok = Get(basicJSON, "loggy")
|
||||||
if mtok.Type != JSON {
|
if mtok.Type != JSON {
|
||||||
t.Fatalf("expected %v, got %v", JSON, mtok.Type)
|
t.Fatalf("expected %v, got %v", JSON, mtok.Type)
|
||||||
|
|
Loading…
Reference in New Issue