mirror of
https://github.com/jeremyd/ergo.git
synced 2026-04-26 07:30:00 -07:00
noirc launch
This commit is contained in:
43
vendor/github.com/tidwall/gjson/README.md
generated
vendored
43
vendor/github.com/tidwall/gjson/README.md
generated
vendored
@@ -1,7 +1,9 @@
|
||||
<p align="center">
|
||||
<img
|
||||
src="logo.png"
|
||||
width="240" height="78" border="0" alt="GJSON">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="/.github/images/logo-dark.png">
|
||||
<source media="(prefers-color-scheme: light)" srcset="/.github/images/logo-light.png">
|
||||
<img src="/.github/images/logo-light.png" width="240" alt="GJSON" >
|
||||
</picture>
|
||||
<br>
|
||||
<a href="https://godoc.org/github.com/tidwall/gjson"><img src="https://img.shields.io/badge/api-reference-blue.svg?style=flat-square" alt="GoDoc"></a>
|
||||
<a href="https://tidwall.com/gjson-play"><img src="https://img.shields.io/badge/%F0%9F%8F%90-playground-9900cc.svg?style=flat-square" alt="GJSON Playground"></a>
|
||||
@@ -176,7 +178,7 @@ The `result.Int()` and `result.Uint()` calls are capable of reading all 64 bits,
|
||||
|
||||
```go
|
||||
result.Int() int64 // -9223372036854775808 to 9223372036854775807
|
||||
result.Uint() int64 // 0 to 18446744073709551615
|
||||
result.Uint() uint64 // 0 to 18446744073709551615
|
||||
```
|
||||
|
||||
## Modifiers and path chaining
|
||||
@@ -211,6 +213,7 @@ There are currently the following built-in modifiers:
|
||||
- `@tostr`: Converts json to a string. Wraps a json string.
|
||||
- `@fromstr`: Converts a string from json. Unwraps a json string.
|
||||
- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db).
|
||||
- `@dig`: Search for a value without providing its entire path. See [e8e87f2](https://github.com/tidwall/gjson/commit/e8e87f2a00dc41f3aba5631094e21f59a8cf8cbf).
|
||||
|
||||
### Modifier arguments
|
||||
|
||||
@@ -426,16 +429,6 @@ if result.Index > 0 {
|
||||
|
||||
This is a best-effort no allocation sub slice of the original json. This method utilizes the `result.Index` field, which is the position of the raw data in the original json. It's possible that the value of `result.Index` equals zero, in which case the `result.Raw` is converted to a `[]byte`.
|
||||
|
||||
## Get multiple values at once
|
||||
|
||||
The `GetMany` function can be used to get multiple values at the same time.
|
||||
|
||||
```go
|
||||
results := gjson.GetMany(json, "name.first", "name.last", "age")
|
||||
```
|
||||
|
||||
The return value is a `[]Result`, which will always contain exactly the same number of items as the input paths.
|
||||
|
||||
## Performance
|
||||
|
||||
Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/),
|
||||
@@ -445,15 +438,15 @@ Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/js
|
||||
and [json-iterator](https://github.com/json-iterator/go)
|
||||
|
||||
```
|
||||
BenchmarkGJSONGet-16 11644512 311 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGJSONUnmarshalMap-16 1122678 3094 ns/op 1920 B/op 26 allocs/op
|
||||
BenchmarkJSONUnmarshalMap-16 516681 6810 ns/op 2944 B/op 69 allocs/op
|
||||
BenchmarkJSONUnmarshalStruct-16 697053 5400 ns/op 928 B/op 13 allocs/op
|
||||
BenchmarkJSONDecoder-16 330450 10217 ns/op 3845 B/op 160 allocs/op
|
||||
BenchmarkFFJSONLexer-16 1424979 2585 ns/op 880 B/op 8 allocs/op
|
||||
BenchmarkEasyJSONLexer-16 3000000 729 ns/op 501 B/op 5 allocs/op
|
||||
BenchmarkJSONParserGet-16 3000000 366 ns/op 21 B/op 0 allocs/op
|
||||
BenchmarkJSONIterator-16 3000000 869 ns/op 693 B/op 14 allocs/op
|
||||
BenchmarkGJSONGet-10 17893731 202.1 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGJSONUnmarshalMap-10 1663548 2157 ns/op 1920 B/op 26 allocs/op
|
||||
BenchmarkJSONUnmarshalMap-10 832236 4279 ns/op 2920 B/op 68 allocs/op
|
||||
BenchmarkJSONUnmarshalStruct-10 1076475 3219 ns/op 920 B/op 12 allocs/op
|
||||
BenchmarkJSONDecoder-10 585729 6126 ns/op 3845 B/op 160 allocs/op
|
||||
BenchmarkFFJSONLexer-10 2508573 1391 ns/op 880 B/op 8 allocs/op
|
||||
BenchmarkEasyJSONLexer-10 3000000 537.9 ns/op 501 B/op 5 allocs/op
|
||||
BenchmarkJSONParserGet-10 13707510 263.9 ns/op 21 B/op 0 allocs/op
|
||||
BenchmarkJSONIterator-10 3000000 561.2 ns/op 693 B/op 14 allocs/op
|
||||
```
|
||||
|
||||
JSON document used:
|
||||
@@ -494,4 +487,6 @@ widget.image.hOffset
|
||||
widget.text.onMouseUp
|
||||
```
|
||||
|
||||
*These benchmarks were run on a MacBook Pro 16" 2.4 GHz Intel Core i9 using Go 1.17 and can be found [here](https://github.com/tidwall/gjson-benchmarks).*
|
||||
**
|
||||
|
||||
*These benchmarks were run on a MacBook Pro M1 Max using Go 1.22 and can be found [here](https://github.com/tidwall/gjson-benchmarks).*
|
||||
|
||||
32
vendor/github.com/tidwall/gjson/SYNTAX.md
generated
vendored
32
vendor/github.com/tidwall/gjson/SYNTAX.md
generated
vendored
@@ -1,6 +1,6 @@
|
||||
# GJSON Path Syntax
|
||||
|
||||
A GJSON Path is a text string syntax that describes a search pattern for quickly retreiving values from a JSON payload.
|
||||
A GJSON Path is a text string syntax that describes a search pattern for quickly retrieving values from a JSON payload.
|
||||
|
||||
This document is designed to explain the structure of a GJSON Path through examples.
|
||||
|
||||
@@ -15,12 +15,12 @@ This document is designed to explain the structure of a GJSON Path through examp
|
||||
- [Multipaths](#multipaths)
|
||||
- [Literals](#literals)
|
||||
|
||||
The definitive implemenation is [github.com/tidwall/gjson](https://github.com/tidwall/gjson).
|
||||
The definitive implementation is [github.com/tidwall/gjson](https://github.com/tidwall/gjson).
|
||||
Use the [GJSON Playground](https://gjson.dev) to experiment with the syntax online.
|
||||
|
||||
## Path structure
|
||||
|
||||
A GJSON Path is intended to be easily expressed as a series of components seperated by a `.` character.
|
||||
A GJSON Path is intended to be easily expressed as a series of components separated by a `.` character.
|
||||
|
||||
Along with `.` character, there are a few more that have special meaning, including `|`, `#`, `@`, `\`, `*`, `!`, and `?`.
|
||||
|
||||
@@ -46,7 +46,7 @@ The following GJSON Paths evaluate to the accompanying values.
|
||||
|
||||
### Basic
|
||||
|
||||
In many cases you'll just want to retreive values by object name or array index.
|
||||
In many cases you'll just want to retrieve values by object name or array index.
|
||||
|
||||
```go
|
||||
name.last "Anderson"
|
||||
@@ -137,12 +137,21 @@ next major release.*
|
||||
|
||||
The `~` (tilde) operator will convert a value to a boolean before comparison.
|
||||
|
||||
Supported tilde comparison type are:
|
||||
|
||||
```
|
||||
~true Converts true-ish values to true
|
||||
~false Converts false-ish and non-existent values to true
|
||||
~null Converts null and non-existent values to true
|
||||
~* Converts any existing value to true
|
||||
```
|
||||
|
||||
For example, using the following JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"vals": [
|
||||
{ "a": 1, "b": true },
|
||||
{ "a": 1, "b": "data" },
|
||||
{ "a": 2, "b": true },
|
||||
{ "a": 3, "b": false },
|
||||
{ "a": 4, "b": "0" },
|
||||
@@ -157,15 +166,23 @@ For example, using the following JSON:
|
||||
}
|
||||
```
|
||||
|
||||
You can now query for all true(ish) or false(ish) values:
|
||||
To query for all true-ish or false-ish values:
|
||||
|
||||
```
|
||||
vals.#(b==~true)#.a >> [1,2,6,7,8]
|
||||
vals.#(b==~true)#.a >> [2,6,7,8]
|
||||
vals.#(b==~false)#.a >> [3,4,5,9,10,11]
|
||||
```
|
||||
|
||||
The last value which was non-existent is treated as `false`
|
||||
|
||||
To query for null and explicit value existence:
|
||||
|
||||
```
|
||||
vals.#(b==~null)#.a >> [10,11]
|
||||
vals.#(b==~*)#.a >> [1,2,3,4,5,6,7,8,9,10]
|
||||
vals.#(b!=~*)#.a >> [11]
|
||||
```
|
||||
|
||||
### Dot vs Pipe
|
||||
|
||||
The `.` is standard separator, but it's also possible to use a `|`.
|
||||
@@ -241,6 +258,7 @@ There are currently the following built-in modifiers:
|
||||
- `@tostr`: Converts json to a string. Wraps a json string.
|
||||
- `@fromstr`: Converts a string from json. Unwraps a json string.
|
||||
- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db).
|
||||
- `@dig`: Search for a value without providing its entire path. See [e8e87f2](https://github.com/tidwall/gjson/commit/e8e87f2a00dc41f3aba5631094e21f59a8cf8cbf).
|
||||
|
||||
#### Modifier arguments
|
||||
|
||||
|
||||
498
vendor/github.com/tidwall/gjson/gjson.go
generated
vendored
498
vendor/github.com/tidwall/gjson/gjson.go
generated
vendored
@@ -645,9 +645,9 @@ func tostr(json string) (raw string, str string) {
|
||||
|
||||
// Exists returns true if value exists.
|
||||
//
|
||||
// if gjson.Get(json, "name.last").Exists(){
|
||||
// println("value exists")
|
||||
// }
|
||||
// if gjson.Get(json, "name.last").Exists(){
|
||||
// println("value exists")
|
||||
// }
|
||||
func (t Result) Exists() bool {
|
||||
return t.Type != Null || len(t.Raw) != 0
|
||||
}
|
||||
@@ -661,7 +661,6 @@ func (t Result) Exists() bool {
|
||||
// nil, for JSON null
|
||||
// map[string]interface{}, for JSON objects
|
||||
// []interface{}, for JSON arrays
|
||||
//
|
||||
func (t Result) Value() interface{} {
|
||||
if t.Type == String {
|
||||
return t.Str
|
||||
@@ -826,19 +825,28 @@ func parseArrayPath(path string) (r arrayPathResult) {
|
||||
}
|
||||
|
||||
// splitQuery takes a query and splits it into three parts:
|
||||
// path, op, middle, and right.
|
||||
//
|
||||
// path, op, middle, and right.
|
||||
//
|
||||
// So for this query:
|
||||
// #(first_name=="Murphy").last
|
||||
//
|
||||
// #(first_name=="Murphy").last
|
||||
//
|
||||
// Becomes
|
||||
// first_name # path
|
||||
// =="Murphy" # middle
|
||||
// .last # right
|
||||
//
|
||||
// first_name # path
|
||||
// =="Murphy" # middle
|
||||
// .last # right
|
||||
//
|
||||
// Or,
|
||||
// #(service_roles.#(=="one")).cap
|
||||
//
|
||||
// #(service_roles.#(=="one")).cap
|
||||
//
|
||||
// Becomes
|
||||
// service_roles.#(=="one") # path
|
||||
// # middle
|
||||
// .cap # right
|
||||
//
|
||||
// service_roles.#(=="one") # path
|
||||
// # middle
|
||||
// .cap # right
|
||||
func parseQuery(query string) (
|
||||
path, op, value, remain string, i int, vesc, ok bool,
|
||||
) {
|
||||
@@ -1009,8 +1017,8 @@ func parseObjectPath(path string) (r objectPathResult) {
|
||||
r.piped = true
|
||||
} else {
|
||||
r.path = path[i+1:]
|
||||
r.more = true
|
||||
}
|
||||
r.more = true
|
||||
return
|
||||
} else if path[i] == '|' {
|
||||
r.part = string(epart)
|
||||
@@ -1032,6 +1040,10 @@ func parseObjectPath(path string) (r objectPathResult) {
|
||||
return
|
||||
}
|
||||
|
||||
var vchars = [256]byte{
|
||||
'"': 2, '{': 3, '(': 3, '[': 3, '}': 1, ')': 1, ']': 1,
|
||||
}
|
||||
|
||||
func parseSquash(json string, i int) (int, string) {
|
||||
// expects that the lead character is a '[' or '{' or '('
|
||||
// squash the value, ignoring all nested arrays and objects.
|
||||
@@ -1039,43 +1051,137 @@ func parseSquash(json string, i int) (int, string) {
|
||||
s := i
|
||||
i++
|
||||
depth := 1
|
||||
for ; i < len(json); i++ {
|
||||
if json[i] >= '"' && json[i] <= '}' {
|
||||
switch json[i] {
|
||||
case '"':
|
||||
var c byte
|
||||
for i < len(json) {
|
||||
for i < len(json)-8 {
|
||||
jslice := json[i : i+8]
|
||||
c = vchars[jslice[0]]
|
||||
if c != 0 {
|
||||
i += 0
|
||||
goto token
|
||||
}
|
||||
c = vchars[jslice[1]]
|
||||
if c != 0 {
|
||||
i += 1
|
||||
goto token
|
||||
}
|
||||
c = vchars[jslice[2]]
|
||||
if c != 0 {
|
||||
i += 2
|
||||
goto token
|
||||
}
|
||||
c = vchars[jslice[3]]
|
||||
if c != 0 {
|
||||
i += 3
|
||||
goto token
|
||||
}
|
||||
c = vchars[jslice[4]]
|
||||
if c != 0 {
|
||||
i += 4
|
||||
goto token
|
||||
}
|
||||
c = vchars[jslice[5]]
|
||||
if c != 0 {
|
||||
i += 5
|
||||
goto token
|
||||
}
|
||||
c = vchars[jslice[6]]
|
||||
if c != 0 {
|
||||
i += 6
|
||||
goto token
|
||||
}
|
||||
c = vchars[jslice[7]]
|
||||
if c != 0 {
|
||||
i += 7
|
||||
goto token
|
||||
}
|
||||
i += 8
|
||||
}
|
||||
c = vchars[json[i]]
|
||||
if c == 0 {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
token:
|
||||
if c == 2 {
|
||||
// '"' string
|
||||
i++
|
||||
s2 := i
|
||||
nextquote:
|
||||
for i < len(json)-8 {
|
||||
jslice := json[i : i+8]
|
||||
if jslice[0] == '"' {
|
||||
i += 0
|
||||
goto strchkesc
|
||||
}
|
||||
if jslice[1] == '"' {
|
||||
i += 1
|
||||
goto strchkesc
|
||||
}
|
||||
if jslice[2] == '"' {
|
||||
i += 2
|
||||
goto strchkesc
|
||||
}
|
||||
if jslice[3] == '"' {
|
||||
i += 3
|
||||
goto strchkesc
|
||||
}
|
||||
if jslice[4] == '"' {
|
||||
i += 4
|
||||
goto strchkesc
|
||||
}
|
||||
if jslice[5] == '"' {
|
||||
i += 5
|
||||
goto strchkesc
|
||||
}
|
||||
if jslice[6] == '"' {
|
||||
i += 6
|
||||
goto strchkesc
|
||||
}
|
||||
if jslice[7] == '"' {
|
||||
i += 7
|
||||
goto strchkesc
|
||||
}
|
||||
i += 8
|
||||
}
|
||||
goto strchkstd
|
||||
strchkesc:
|
||||
if json[i-1] != '\\' {
|
||||
i++
|
||||
s2 := i
|
||||
for ; i < len(json); i++ {
|
||||
if json[i] > '\\' {
|
||||
continue
|
||||
}
|
||||
if json[i] == '"' {
|
||||
// look for an escaped slash
|
||||
if json[i-1] == '\\' {
|
||||
n := 0
|
||||
for j := i - 2; j > s2-1; j-- {
|
||||
if json[j] != '\\' {
|
||||
break
|
||||
}
|
||||
n++
|
||||
}
|
||||
if n%2 == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
case '{', '[', '(':
|
||||
depth++
|
||||
case '}', ']', ')':
|
||||
depth--
|
||||
if depth == 0 {
|
||||
continue
|
||||
}
|
||||
strchkstd:
|
||||
for i < len(json) {
|
||||
if json[i] > '\\' || json[i] != '"' {
|
||||
i++
|
||||
return i, json[s:i]
|
||||
continue
|
||||
}
|
||||
// look for an escaped slash
|
||||
if json[i-1] == '\\' {
|
||||
n := 0
|
||||
for j := i - 2; j > s2-1; j-- {
|
||||
if json[j] != '\\' {
|
||||
break
|
||||
}
|
||||
n++
|
||||
}
|
||||
if n%2 == 0 {
|
||||
i++
|
||||
goto nextquote
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
} else {
|
||||
// '{', '[', '(', '}', ']', ')'
|
||||
// open close tokens
|
||||
depth += int(c) - 2
|
||||
if depth == 0 {
|
||||
i++
|
||||
return i, json[s:i]
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return i, json[s:]
|
||||
}
|
||||
@@ -1244,22 +1350,81 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
|
||||
}
|
||||
|
||||
// matchLimit will limit the complexity of the match operation to avoid ReDos
|
||||
// attacks from arbritary inputs.
|
||||
// attacks from arbitrary inputs.
|
||||
// See the github.com/tidwall/match.MatchLimit function for more information.
|
||||
func matchLimit(str, pattern string) bool {
|
||||
matched, _ := match.MatchLimit(str, pattern, 10000)
|
||||
return matched
|
||||
}
|
||||
|
||||
func falseish(t Result) bool {
|
||||
switch t.Type {
|
||||
case Null:
|
||||
return true
|
||||
case False:
|
||||
return true
|
||||
case String:
|
||||
b, err := strconv.ParseBool(strings.ToLower(t.Str))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return !b
|
||||
case Number:
|
||||
return t.Num == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func trueish(t Result) bool {
|
||||
switch t.Type {
|
||||
case True:
|
||||
return true
|
||||
case String:
|
||||
b, err := strconv.ParseBool(strings.ToLower(t.Str))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return b
|
||||
case Number:
|
||||
return t.Num != 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func nullish(t Result) bool {
|
||||
return t.Type == Null
|
||||
}
|
||||
|
||||
func queryMatches(rp *arrayPathResult, value Result) bool {
|
||||
rpv := rp.query.value
|
||||
if len(rpv) > 0 && rpv[0] == '~' {
|
||||
// convert to bool
|
||||
rpv = rpv[1:]
|
||||
if value.Bool() {
|
||||
value = Result{Type: True}
|
||||
} else {
|
||||
value = Result{Type: False}
|
||||
if len(rpv) > 0 {
|
||||
if rpv[0] == '~' {
|
||||
// convert to bool
|
||||
rpv = rpv[1:]
|
||||
var ish, ok bool
|
||||
switch rpv {
|
||||
case "*":
|
||||
ish, ok = value.Exists(), true
|
||||
case "null":
|
||||
ish, ok = nullish(value), true
|
||||
case "true":
|
||||
ish, ok = trueish(value), true
|
||||
case "false":
|
||||
ish, ok = falseish(value), true
|
||||
}
|
||||
if ok {
|
||||
rpv = "true"
|
||||
if ish {
|
||||
value = Result{Type: True}
|
||||
} else {
|
||||
value = Result{Type: False}
|
||||
}
|
||||
} else {
|
||||
rpv = ""
|
||||
value = Result{}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !value.Exists() {
|
||||
@@ -1850,6 +2015,16 @@ func appendHex16(dst []byte, x uint16) []byte {
|
||||
)
|
||||
}
|
||||
|
||||
// DisableEscapeHTML will disable the automatic escaping of certain
|
||||
// "problamatic" HTML characters when encoding to JSON.
|
||||
// These character include '>', '<' and '&', which get escaped to \u003e,
|
||||
// \u0026, and \u003c respectively.
|
||||
//
|
||||
// This is a global flag and will affect all further gjson operations.
|
||||
// Ideally, if used, it should be set one time before other gjson functions
|
||||
// are called.
|
||||
var DisableEscapeHTML = false
|
||||
|
||||
// AppendJSONString is a convenience function that converts the provided string
|
||||
// to a valid JSON string and appends it to dst.
|
||||
func AppendJSONString(dst []byte, s string) []byte {
|
||||
@@ -1859,6 +2034,10 @@ func AppendJSONString(dst []byte, s string) []byte {
|
||||
if s[i] < ' ' {
|
||||
dst = append(dst, '\\')
|
||||
switch s[i] {
|
||||
case '\b':
|
||||
dst = append(dst, 'b')
|
||||
case '\f':
|
||||
dst = append(dst, 'f')
|
||||
case '\n':
|
||||
dst = append(dst, 'n')
|
||||
case '\r':
|
||||
@@ -1869,7 +2048,8 @@ func AppendJSONString(dst []byte, s string) []byte {
|
||||
dst = append(dst, 'u')
|
||||
dst = appendHex16(dst, uint16(s[i]))
|
||||
}
|
||||
} else if s[i] == '>' || s[i] == '<' || s[i] == '&' {
|
||||
} else if !DisableEscapeHTML &&
|
||||
(s[i] == '>' || s[i] == '<' || s[i] == '&') {
|
||||
dst = append(dst, '\\', 'u')
|
||||
dst = appendHex16(dst, uint16(s[i]))
|
||||
} else if s[i] == '\\' {
|
||||
@@ -1918,23 +2098,23 @@ type parseContext struct {
|
||||
// the '#' character.
|
||||
// The dot and wildcard character can be escaped with '\'.
|
||||
//
|
||||
// {
|
||||
// "name": {"first": "Tom", "last": "Anderson"},
|
||||
// "age":37,
|
||||
// "children": ["Sara","Alex","Jack"],
|
||||
// "friends": [
|
||||
// {"first": "James", "last": "Murphy"},
|
||||
// {"first": "Roger", "last": "Craig"}
|
||||
// ]
|
||||
// }
|
||||
// "name.last" >> "Anderson"
|
||||
// "age" >> 37
|
||||
// "children" >> ["Sara","Alex","Jack"]
|
||||
// "children.#" >> 3
|
||||
// "children.1" >> "Alex"
|
||||
// "child*.2" >> "Jack"
|
||||
// "c?ildren.0" >> "Sara"
|
||||
// "friends.#.first" >> ["James","Roger"]
|
||||
// {
|
||||
// "name": {"first": "Tom", "last": "Anderson"},
|
||||
// "age":37,
|
||||
// "children": ["Sara","Alex","Jack"],
|
||||
// "friends": [
|
||||
// {"first": "James", "last": "Murphy"},
|
||||
// {"first": "Roger", "last": "Craig"}
|
||||
// ]
|
||||
// }
|
||||
// "name.last" >> "Anderson"
|
||||
// "age" >> 37
|
||||
// "children" >> ["Sara","Alex","Jack"]
|
||||
// "children.#" >> 3
|
||||
// "children.1" >> "Alex"
|
||||
// "child*.2" >> "Jack"
|
||||
// "c?ildren.0" >> "Sara"
|
||||
// "friends.#.first" >> ["James","Roger"]
|
||||
//
|
||||
// This function expects that the json is well-formed, and does not validate.
|
||||
// Invalid json will not panic, but it may return back unexpected results.
|
||||
@@ -2123,11 +2303,10 @@ func unescape(json string) string {
|
||||
}
|
||||
|
||||
// Less return true if a token is less than another token.
|
||||
// The caseSensitive paramater is used when the tokens are Strings.
|
||||
// The caseSensitive parameter is used when the tokens are Strings.
|
||||
// The order when comparing two different type is:
|
||||
//
|
||||
// Null < False < Number < String < True < JSON
|
||||
//
|
||||
// Null < False < Number < String < True < JSON
|
||||
func (t Result) Less(token Result, caseSensitive bool) bool {
|
||||
if t.Type < token.Type {
|
||||
return true
|
||||
@@ -2556,11 +2735,10 @@ func validnull(data []byte, i int) (outi int, ok bool) {
|
||||
|
||||
// Valid returns true if the input is valid json.
|
||||
//
|
||||
// if !gjson.Valid(json) {
|
||||
// return errors.New("invalid json")
|
||||
// }
|
||||
// value := gjson.Get(json, "name.last")
|
||||
//
|
||||
// if !gjson.Valid(json) {
|
||||
// return errors.New("invalid json")
|
||||
// }
|
||||
// value := gjson.Get(json, "name.last")
|
||||
func Valid(json string) bool {
|
||||
_, ok := validpayload(stringBytes(json), 0)
|
||||
return ok
|
||||
@@ -2568,13 +2746,12 @@ func Valid(json string) bool {
|
||||
|
||||
// ValidBytes returns true if the input is valid json.
|
||||
//
|
||||
// if !gjson.Valid(json) {
|
||||
// return errors.New("invalid json")
|
||||
// }
|
||||
// value := gjson.Get(json, "name.last")
|
||||
// if !gjson.Valid(json) {
|
||||
// return errors.New("invalid json")
|
||||
// }
|
||||
// value := gjson.Get(json, "name.last")
|
||||
//
|
||||
// If working with bytes, this method preferred over ValidBytes(string(data))
|
||||
//
|
||||
func ValidBytes(json []byte) bool {
|
||||
_, ok := validpayload(json, 0)
|
||||
return ok
|
||||
@@ -2690,6 +2867,7 @@ func execModifier(json, path string) (pathOut, res string, ok bool) {
|
||||
var parsedArgs bool
|
||||
switch pathOut[0] {
|
||||
case '{', '[', '"':
|
||||
// json arg
|
||||
res := Parse(pathOut)
|
||||
if res.Exists() {
|
||||
args = squash(pathOut)
|
||||
@@ -2698,14 +2876,20 @@ func execModifier(json, path string) (pathOut, res string, ok bool) {
|
||||
}
|
||||
}
|
||||
if !parsedArgs {
|
||||
idx := strings.IndexByte(pathOut, '|')
|
||||
if idx == -1 {
|
||||
args = pathOut
|
||||
pathOut = ""
|
||||
} else {
|
||||
args = pathOut[:idx]
|
||||
pathOut = pathOut[idx:]
|
||||
// simple arg
|
||||
i := 0
|
||||
for ; i < len(pathOut); i++ {
|
||||
if pathOut[i] == '|' {
|
||||
break
|
||||
}
|
||||
switch pathOut[i] {
|
||||
case '{', '[', '"', '(':
|
||||
s := squash(pathOut[i:])
|
||||
i += len(s) - 1
|
||||
}
|
||||
}
|
||||
args = pathOut[:i]
|
||||
pathOut = pathOut[i:]
|
||||
}
|
||||
}
|
||||
return pathOut, fn(json, args), true
|
||||
@@ -2725,19 +2909,24 @@ func unwrap(json string) string {
|
||||
// DisableModifiers will disable the modifier syntax
|
||||
var DisableModifiers = false
|
||||
|
||||
var modifiers = map[string]func(json, arg string) string{
|
||||
"pretty": modPretty,
|
||||
"ugly": modUgly,
|
||||
"reverse": modReverse,
|
||||
"this": modThis,
|
||||
"flatten": modFlatten,
|
||||
"join": modJoin,
|
||||
"valid": modValid,
|
||||
"keys": modKeys,
|
||||
"values": modValues,
|
||||
"tostr": modToStr,
|
||||
"fromstr": modFromStr,
|
||||
"group": modGroup,
|
||||
var modifiers map[string]func(json, arg string) string
|
||||
|
||||
func init() {
|
||||
modifiers = map[string]func(json, arg string) string{
|
||||
"pretty": modPretty,
|
||||
"ugly": modUgly,
|
||||
"reverse": modReverse,
|
||||
"this": modThis,
|
||||
"flatten": modFlatten,
|
||||
"join": modJoin,
|
||||
"valid": modValid,
|
||||
"keys": modKeys,
|
||||
"values": modValues,
|
||||
"tostr": modToStr,
|
||||
"fromstr": modFromStr,
|
||||
"group": modGroup,
|
||||
"dig": modDig,
|
||||
}
|
||||
}
|
||||
|
||||
// AddModifier binds a custom modifier command to the GJSON syntax.
|
||||
@@ -2848,9 +3037,13 @@ func modReverse(json, arg string) string {
|
||||
}
|
||||
|
||||
// @flatten an array with child arrays.
|
||||
// [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,[6,7]]
|
||||
//
|
||||
// [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,[6,7]]
|
||||
//
|
||||
// The {"deep":true} arg can be provide for deep flattening.
|
||||
// [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,6,7]
|
||||
//
|
||||
// [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,6,7]
|
||||
//
|
||||
// The original json is returned when the json is not an array.
|
||||
func modFlatten(json, arg string) string {
|
||||
res := Parse(json)
|
||||
@@ -2895,7 +3088,8 @@ func modFlatten(json, arg string) string {
|
||||
}
|
||||
|
||||
// @keys extracts the keys from an object.
|
||||
// {"first":"Tom","last":"Smith"} -> ["first","last"]
|
||||
//
|
||||
// {"first":"Tom","last":"Smith"} -> ["first","last"]
|
||||
func modKeys(json, arg string) string {
|
||||
v := Parse(json)
|
||||
if !v.Exists() {
|
||||
@@ -2922,7 +3116,8 @@ func modKeys(json, arg string) string {
|
||||
}
|
||||
|
||||
// @values extracts the values from an object.
|
||||
// {"first":"Tom","last":"Smith"} -> ["Tom","Smith"]
|
||||
//
|
||||
// {"first":"Tom","last":"Smith"} -> ["Tom","Smith"]
|
||||
func modValues(json, arg string) string {
|
||||
v := Parse(json)
|
||||
if !v.Exists() {
|
||||
@@ -2947,11 +3142,17 @@ func modValues(json, arg string) string {
|
||||
}
|
||||
|
||||
// @join multiple objects into a single object.
|
||||
// [{"first":"Tom"},{"last":"Smith"}] -> {"first","Tom","last":"Smith"}
|
||||
//
|
||||
// [{"first":"Tom"},{"last":"Smith"}] -> {"first","Tom","last":"Smith"}
|
||||
//
|
||||
// The arg can be "true" to specify that duplicate keys should be preserved.
|
||||
// [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":37,"age":41}
|
||||
//
|
||||
// [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":37,"age":41}
|
||||
//
|
||||
// Without preserved keys:
|
||||
// [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":41}
|
||||
//
|
||||
// [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":41}
|
||||
//
|
||||
// The original json is returned when the json is not an object.
|
||||
func modJoin(json, arg string) string {
|
||||
res := Parse(json)
|
||||
@@ -3024,7 +3225,8 @@ func modValid(json, arg string) string {
|
||||
}
|
||||
|
||||
// @fromstr converts a string to json
|
||||
// "{\"id\":1023,\"name\":\"alert\"}" -> {"id":1023,"name":"alert"}
|
||||
//
|
||||
// "{\"id\":1023,\"name\":\"alert\"}" -> {"id":1023,"name":"alert"}
|
||||
func modFromStr(json, arg string) string {
|
||||
if !Valid(json) {
|
||||
return ""
|
||||
@@ -3033,7 +3235,8 @@ func modFromStr(json, arg string) string {
|
||||
}
|
||||
|
||||
// @tostr converts a string to json
|
||||
// {"id":1023,"name":"alert"} -> "{\"id\":1023,\"name\":\"alert\"}"
|
||||
//
|
||||
// {"id":1023,"name":"alert"} -> "{\"id\":1023,\"name\":\"alert\"}"
|
||||
func modToStr(str, arg string) string {
|
||||
return string(AppendJSONString(nil, str))
|
||||
}
|
||||
@@ -3210,11 +3413,11 @@ func revSquash(json string) string {
|
||||
// Paths returns the original GJSON paths for a Result where the Result came
|
||||
// from a simple query path that returns an array, like:
|
||||
//
|
||||
// gjson.Get(json, "friends.#.first")
|
||||
// gjson.Get(json, "friends.#.first")
|
||||
//
|
||||
// The returned value will be in the form of a JSON array:
|
||||
//
|
||||
// ["friends.0.first","friends.1.first","friends.2.first"]
|
||||
// ["friends.0.first","friends.1.first","friends.2.first"]
|
||||
//
|
||||
// The param 'json' must be the original JSON used when calling Get.
|
||||
//
|
||||
@@ -3239,11 +3442,11 @@ func (t Result) Paths(json string) []string {
|
||||
// Path returns the original GJSON path for a Result where the Result came
|
||||
// from a simple path that returns a single value, like:
|
||||
//
|
||||
// gjson.Get(json, "friends.#(last=Murphy)")
|
||||
// gjson.Get(json, "friends.#(last=Murphy)")
|
||||
//
|
||||
// The returned value will be in the form of a JSON string:
|
||||
//
|
||||
// "friends.0"
|
||||
// "friends.0"
|
||||
//
|
||||
// The param 'json' must be the original JSON used when calling Get.
|
||||
//
|
||||
@@ -3259,7 +3462,7 @@ func (t Result) Path(json string) string {
|
||||
goto fail
|
||||
}
|
||||
if !strings.HasPrefix(json[t.Index:], t.Raw) {
|
||||
// Result is not at the JSON index as exepcted.
|
||||
// Result is not at the JSON index as expected.
|
||||
goto fail
|
||||
}
|
||||
for ; i >= 0; i-- {
|
||||
@@ -3320,7 +3523,7 @@ func (t Result) Path(json string) string {
|
||||
if !rcomp.Exists() {
|
||||
goto fail
|
||||
}
|
||||
comp := escapeComp(rcomp.String())
|
||||
comp := Escape(rcomp.String())
|
||||
path = append(path, '.')
|
||||
path = append(path, comp...)
|
||||
}
|
||||
@@ -3335,17 +3538,31 @@ fail:
|
||||
// isSafePathKeyChar returns true if the input character is safe for not
|
||||
// needing escaping.
|
||||
func isSafePathKeyChar(c byte) bool {
|
||||
return c <= ' ' || c > '~' || c == '_' || c == '-' || c == ':' ||
|
||||
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9')
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c <= ' ' || c > '~' || c == '_' ||
|
||||
c == '-' || c == ':'
|
||||
}
|
||||
|
||||
// escapeComp escaped a path compontent, making it safe for generating a
|
||||
// path for later use.
|
||||
func escapeComp(comp string) string {
|
||||
// Escape returns an escaped path component.
|
||||
//
|
||||
// json := `{
|
||||
// "user":{
|
||||
// "first.name": "Janet",
|
||||
// "last.name": "Prichard"
|
||||
// }
|
||||
// }`
|
||||
// user := gjson.Get(json, "user")
|
||||
// println(user.Get(gjson.Escape("first.name"))
|
||||
// println(user.Get(gjson.Escape("last.name"))
|
||||
// // Output:
|
||||
// // Janet
|
||||
// // Prichard
|
||||
func Escape(comp string) string {
|
||||
for i := 0; i < len(comp); i++ {
|
||||
if !isSafePathKeyChar(comp[i]) {
|
||||
ncomp := []byte(comp[:i])
|
||||
ncomp := make([]byte, len(comp)+1)
|
||||
copy(ncomp, comp[:i])
|
||||
ncomp = ncomp[:i]
|
||||
for ; i < len(comp); i++ {
|
||||
if !isSafePathKeyChar(comp[i]) {
|
||||
ncomp = append(ncomp, '\\')
|
||||
@@ -3357,3 +3574,30 @@ func escapeComp(comp string) string {
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
BIN
vendor/github.com/tidwall/gjson/logo.png
generated
vendored
BIN
vendor/github.com/tidwall/gjson/logo.png
generated
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
Reference in New Issue
Block a user