summary refs log tree commit diff
path: root/vendor/github.com/tidwall/gjson
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/tidwall/gjson')
-rw-r--r--vendor/github.com/tidwall/gjson/LICENSE20
-rw-r--r--vendor/github.com/tidwall/gjson/README.md492
-rw-r--r--vendor/github.com/tidwall/gjson/SYNTAX.md360
-rw-r--r--vendor/github.com/tidwall/gjson/gjson.go3603
4 files changed, 4475 insertions, 0 deletions
diff --git a/vendor/github.com/tidwall/gjson/LICENSE b/vendor/github.com/tidwall/gjson/LICENSE
new file mode 100644
index 0000000..58f5819
--- /dev/null
+++ b/vendor/github.com/tidwall/gjson/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Josh Baker
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/tidwall/gjson/README.md b/vendor/github.com/tidwall/gjson/README.md
new file mode 100644
index 0000000..387766d
--- /dev/null
+++ b/vendor/github.com/tidwall/gjson/README.md
@@ -0,0 +1,492 @@
+<p align="center">
+<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>
+<a href="SYNTAX.md"><img src="https://img.shields.io/badge/{}-syntax-33aa33.svg?style=flat-square" alt="GJSON Syntax"></a>
+	
+</p>
+
+<p align="center">get json values quickly</a></p>
+
+GJSON is a Go package that provides a [fast](#performance) and [simple](#get-a-value) way to get values from a json document.
+It has features such as [one line retrieval](#get-a-value), [dot notation paths](#path-syntax), [iteration](#iterate-through-an-object-or-array), and [parsing json lines](#json-lines).
+
+Also check out [SJSON](https://github.com/tidwall/sjson) for modifying json, and the [JJ](https://github.com/tidwall/jj) command line tool.
+
+This README is a quick overview of how to use GJSON, for more information check out [GJSON Syntax](SYNTAX.md).
+
+GJSON is also available for [Python](https://github.com/volans-/gjson-py) and [Rust](https://github.com/tidwall/gjson.rs)
+
+Getting Started
+===============
+
+## Installing
+
+To start using GJSON, install Go and run `go get`:
+
+```sh
+$ go get -u github.com/tidwall/gjson
+```
+
+This will retrieve the library.
+
+## Get a value
+Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". When the value is found it's returned immediately. 
+
+```go
+package main
+
+import "github.com/tidwall/gjson"
+
+const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
+
+func main() {
+	value := gjson.Get(json, "name.last")
+	println(value.String())
+}
+```
+
+This will print:
+
+```
+Prichard
+```
+*There's also the [GetMany](#get-multiple-values-at-once) function to get multiple values at once, and [GetBytes](#working-with-bytes) for working with JSON byte slices.*
+
+## Path Syntax
+
+Below is a quick overview of the path syntax, for more complete information please
+check out [GJSON Syntax](SYNTAX.md).
+
+A path is a series of keys separated by a dot.
+A key may contain special wildcard characters '\*' and '?'.
+To access an array value use the index as the key.
+To get the number of elements in an array or to access a child path, use the '#' character.
+The dot and wildcard characters can be escaped with '\\'.
+
+```json
+{
+  "name": {"first": "Tom", "last": "Anderson"},
+  "age":37,
+  "children": ["Sara","Alex","Jack"],
+  "fav.movie": "Deer Hunter",
+  "friends": [
+    {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
+    {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},
+    {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}
+  ]
+}
+```
+```
+"name.last"          >> "Anderson"
+"age"                >> 37
+"children"           >> ["Sara","Alex","Jack"]
+"children.#"         >> 3
+"children.1"         >> "Alex"
+"child*.2"           >> "Jack"
+"c?ildren.0"         >> "Sara"
+"fav\.movie"         >> "Deer Hunter"
+"friends.#.first"    >> ["Dale","Roger","Jane"]
+"friends.1.last"     >> "Craig"
+```
+
+You can also query an array for the first match by using `#(...)`, or find all 
+matches with `#(...)#`. Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` 
+comparison operators and the simple pattern matching `%` (like) and `!%` 
+(not like) operators.
+
+```
+friends.#(last=="Murphy").first    >> "Dale"
+friends.#(last=="Murphy")#.first   >> ["Dale","Jane"]
+friends.#(age>45)#.last            >> ["Craig","Murphy"]
+friends.#(first%"D*").last         >> "Murphy"
+friends.#(first!%"D*").last        >> "Craig"
+friends.#(nets.#(=="fb"))#.first   >> ["Dale","Roger"]
+```
+
+*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was
+changed in v1.3.0 as to avoid confusion with the new
+[multipath](SYNTAX.md#multipaths) syntax. For backwards compatibility, 
+`#[...]` will continue to work until the next major release.*
+
+## Result Type
+
+GJSON supports the json types `string`, `number`, `bool`, and `null`. 
+Arrays and Objects are returned as their raw json types. 
+
+The `Result` type holds one of these:
+
+```
+bool, for JSON booleans
+float64, for JSON numbers
+string, for JSON string literals
+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.Indexes        // indexes of all the elements that match on a path containing the '#' query character.
+```
+
+There are a variety of handy functions that work on a result:
+
+```go
+result.Exists() bool
+result.Value() interface{}
+result.Int() int64
+result.Uint() uint64
+result.Float() float64
+result.String() string
+result.Bool() bool
+result.Time() time.Time
+result.Array() []gjson.Result
+result.Map() map[string]gjson.Result
+result.Get(path string) Result
+result.ForEach(iterator func(key, value Result) bool)
+result.Less(token Result, caseSensitive bool) bool
+```
+
+The `result.Value()` function returns an `interface{}` which requires type assertion and is one of the following Go types:
+
+```go
+boolean >> bool
+number  >> float64
+string  >> string
+null    >> nil
+array   >> []interface{}
+object  >> map[string]interface{}
+```
+
+The `result.Array()` function returns back an array of values.
+If the result represents a non-existent value, then an empty array will be returned.
+If the result is not a JSON array, the return value will be an array containing one result.
+
+### 64-bit integers
+
+The `result.Int()` and `result.Uint()` calls are capable of reading all 64 bits, allowing for large JSON integers.
+
+```go
+result.Int() int64    // -9223372036854775808 to 9223372036854775807
+result.Uint() uint64   // 0 to 18446744073709551615
+```
+
+## Modifiers and path chaining 
+
+New in version 1.2 is support for modifier functions and path chaining.
+
+A modifier is a path component that performs custom processing on the 
+json.
+
+Multiple paths can be "chained" together using the pipe character. 
+This is useful for getting results from a modified query.
+
+For example, using the built-in `@reverse` modifier on the above json document,
+we'll get `children` array and reverse the order:
+
+```
+"children|@reverse"           >> ["Jack","Alex","Sara"]
+"children|@reverse|0"         >> "Jack"
+```
+
+There are currently the following built-in modifiers:
+
+- `@reverse`: Reverse an array or the members of an object.
+- `@ugly`: Remove all whitespace from a json document.
+- `@pretty`: Make the json document more human readable.
+- `@this`: Returns the current element. It can be used to retrieve the root element.
+- `@valid`: Ensure the json document is valid.
+- `@flatten`: Flattens an array.
+- `@join`: Joins multiple objects into a single object.
+- `@keys`: Returns an array of keys for an object.
+- `@values`: Returns an array of values for an object.
+- `@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
+
+A modifier may accept an optional argument. The argument can be a valid JSON 
+document or just characters.
+
+For example, the `@pretty` modifier takes a json object as its argument. 
+
+```
+@pretty:{"sortKeys":true} 
+```
+
+Which makes the json pretty and orders all of its keys.
+
+```json
+{
+  "age":37,
+  "children": ["Sara","Alex","Jack"],
+  "fav.movie": "Deer Hunter",
+  "friends": [
+    {"age": 44, "first": "Dale", "last": "Murphy"},
+    {"age": 68, "first": "Roger", "last": "Craig"},
+    {"age": 47, "first": "Jane", "last": "Murphy"}
+  ],
+  "name": {"first": "Tom", "last": "Anderson"}
+}
+```
+
+*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`. 
+Please see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.*
+
+### Custom modifiers
+
+You can also add custom modifiers.
+
+For example, here we create a modifier that makes the entire json document upper
+or lower case.
+
+```go
+gjson.AddModifier("case", func(json, arg string) string {
+  if arg == "upper" {
+    return strings.ToUpper(json)
+  }
+  if arg == "lower" {
+    return strings.ToLower(json)
+  }
+  return json
+})
+```
+
+```
+"children|@case:upper"           >> ["SARA","ALEX","JACK"]
+"children|@case:lower|@reverse"  >> ["jack","alex","sara"]
+```
+
+## JSON Lines
+
+There's support for [JSON Lines](http://jsonlines.org/) using the `..` prefix, which treats a multilined document as an array. 
+
+For example:
+
+```
+{"name": "Gilbert", "age": 61}
+{"name": "Alexa", "age": 34}
+{"name": "May", "age": 57}
+{"name": "Deloise", "age": 44}
+```
+
+```
+..#                   >> 4
+..1                   >> {"name": "Alexa", "age": 34}
+..3                   >> {"name": "Deloise", "age": 44}
+..#.name              >> ["Gilbert","Alexa","May","Deloise"]
+..#(name="May").age   >> 57
+```
+
+The `ForEachLines` function will iterate through JSON lines.
+
+```go
+gjson.ForEachLine(json, func(line gjson.Result) bool{
+    println(line.String())
+    return true
+})
+```
+
+## Get nested array values
+
+Suppose you want all the last names from the following json:
+
+```json
+{
+  "programmers": [
+    {
+      "firstName": "Janet", 
+      "lastName": "McLaughlin", 
+    }, {
+      "firstName": "Elliotte", 
+      "lastName": "Hunter", 
+    }, {
+      "firstName": "Jason", 
+      "lastName": "Harold", 
+    }
+  ]
+}
+```
+
+You would use the path "programmers.#.lastName" like such:
+
+```go
+result := gjson.Get(json, "programmers.#.lastName")
+for _, name := range result.Array() {
+	println(name.String())
+}
+```
+
+You can also query an object inside an array:
+
+```go
+name := gjson.Get(json, `programmers.#(lastName="Hunter").firstName`)
+println(name.String())  // prints "Elliotte"
+```
+
+## Iterate through an object or array
+
+The `ForEach` function allows for quickly iterating through an object or array. 
+The key and value are passed to the iterator function for objects.
+Only the value is passed for arrays.
+Returning `false` from an iterator will stop iteration.
+
+```go
+result := gjson.Get(json, "programmers")
+result.ForEach(func(key, value gjson.Result) bool {
+	println(value.String()) 
+	return true // keep iterating
+})
+```
+
+## 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.
+
+For example, all of these will return the same result:
+
+```go
+gjson.Parse(json).Get("name").Get("last")
+gjson.Get(json, "name").Get("last")
+gjson.Get(json, "name.last")
+```
+
+## Check for the existence of a value
+
+Sometimes you just want to know if a value exists. 
+
+```go
+value := gjson.Get(json, "name.last")
+if !value.Exists() {
+	println("no last name")
+} else {
+	println(value.String())
+}
+
+// Or as one step
+if gjson.Get(json, "name.last").Exists() {
+	println("has a last name")
+}
+```
+
+## Validate JSON
+
+The `Get*` and `Parse*` functions expects that the json is well-formed. Bad json will not panic, but it may return back unexpected results.
+
+If you are consuming JSON from an unpredictable source then you may want to validate prior to using GJSON.
+
+```go
+if !gjson.Valid(json) {
+	return errors.New("invalid json")
+}
+value := gjson.Get(json, "name.last")
+```
+
+## Unmarshal to a map
+
+To unmarshal to a `map[string]interface{}`:
+
+```go
+m, ok := gjson.Parse(json).Value().(map[string]interface{})
+if !ok {
+	// not a map
+}
+```
+
+## Working with Bytes
+
+If your JSON is contained in a `[]byte` slice, there's the [GetBytes](https://godoc.org/github.com/tidwall/gjson#GetBytes) function. This is preferred over `Get(string(data), path)`.
+
+```go
+var json []byte = ...
+result := gjson.GetBytes(json, path)
+```
+
+If you are using the `gjson.GetBytes(json, path)` function and you want to avoid converting `result.Raw` to a `[]byte`, then you can use this pattern:
+
+```go
+var json []byte = ...
+result := gjson.GetBytes(json, path)
+var raw []byte
+if result.Index > 0 {
+    raw = json[result.Index:result.Index+len(result.Raw)]
+} else {
+    raw = []byte(result.Raw)
+}
+```
+
+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`.
+
+## Performance
+
+Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/), 
+[ffjson](https://github.com/pquerna/ffjson), 
+[EasyJSON](https://github.com/mailru/easyjson),
+[jsonparser](https://github.com/buger/jsonparser),
+and [json-iterator](https://github.com/json-iterator/go)
+
+```
+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:
+
+```json
+{
+  "widget": {
+    "debug": "on",
+    "window": {
+      "title": "Sample Konfabulator Widget",
+      "name": "main_window",
+      "width": 500,
+      "height": 500
+    },
+    "image": { 
+      "src": "Images/Sun.png",
+      "hOffset": 250,
+      "vOffset": 250,
+      "alignment": "center"
+    },
+    "text": {
+      "data": "Click Here",
+      "size": 36,
+      "style": "bold",
+      "vOffset": 100,
+      "alignment": "center",
+      "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
+    }
+  }
+}    
+```
+
+Each operation was rotated through one of the following search paths:
+
+```
+widget.window.name
+widget.image.hOffset
+widget.text.onMouseUp
+```
+
+**
+
+*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).*
diff --git a/vendor/github.com/tidwall/gjson/SYNTAX.md b/vendor/github.com/tidwall/gjson/SYNTAX.md
new file mode 100644
index 0000000..a3f0fac
--- /dev/null
+++ b/vendor/github.com/tidwall/gjson/SYNTAX.md
@@ -0,0 +1,360 @@
+# GJSON Path Syntax
+
+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.
+
+- [Path structure](#path-structure)
+- [Basic](#basic)
+- [Wildcards](#wildcards)
+- [Escape Character](#escape-character)
+- [Arrays](#arrays)
+- [Queries](#queries)
+- [Dot vs Pipe](#dot-vs-pipe)
+- [Modifiers](#modifiers)
+- [Multipaths](#multipaths)
+- [Literals](#literals)
+
+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 separated by a `.` character. 
+
+Along with `.` character, there are a few more that have special meaning, including `|`, `#`, `@`, `\`, `*`, `!`, and `?`.
+
+## Example
+
+Given this JSON
+
+```json
+{
+  "name": {"first": "Tom", "last": "Anderson"},
+  "age":37,
+  "children": ["Sara","Alex","Jack"],
+  "fav.movie": "Deer Hunter",
+  "friends": [
+    {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
+    {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},
+    {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}
+  ]
+}
+```
+
+The following GJSON Paths evaluate to the accompanying values.
+
+### Basic 
+
+In many cases you'll just want to retrieve values by object name or array index.
+
+```go
+name.last              "Anderson"
+name.first             "Tom"
+age                    37
+children               ["Sara","Alex","Jack"]
+children.0             "Sara"
+children.1             "Alex"
+friends.1              {"first": "Roger", "last": "Craig", "age": 68}
+friends.1.first        "Roger"
+```
+
+### Wildcards
+
+A key may contain the special wildcard characters `*` and `?`. 
+The `*` will match on any zero+ characters, and `?` matches on any one character.
+
+```go
+child*.2               "Jack"
+c?ildren.0             "Sara"
+```
+
+### Escape character
+
+Special purpose characters, such as `.`, `*`, and `?` can be escaped with `\`. 
+
+```go
+fav\.movie             "Deer Hunter"
+```
+
+You'll also need to make sure that the `\` character is correctly escaped when hardcoding a path in your source code.
+
+```go
+// Go
+val := gjson.Get(json, "fav\\.movie")  // must escape the slash
+val := gjson.Get(json, `fav\.movie`)   // no need to escape the slash 
+```
+
+```rust
+// Rust
+let val = gjson::get(json, "fav\\.movie")     // must escape the slash
+let val = gjson::get(json, r#"fav\.movie"#)   // no need to escape the slash 
+```
+
+
+### Arrays
+
+The `#` character allows for digging into JSON Arrays.
+
+To get the length of an array you'll just use the `#` all by itself.
+
+```go
+friends.#              3
+friends.#.age         [44,68,47]
+```
+
+### Queries
+
+You can also query an array for the first match by  using `#(...)`, or find all matches with `#(...)#`. 
+Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` comparison operators, 
+and the simple pattern matching `%` (like) and `!%` (not like) operators.
+
+```go
+friends.#(last=="Murphy").first     "Dale"
+friends.#(last=="Murphy")#.first    ["Dale","Jane"]
+friends.#(age>45)#.last             ["Craig","Murphy"]
+friends.#(first%"D*").last          "Murphy"
+friends.#(first!%"D*").last         "Craig"
+```
+
+To query for a non-object value in an array, you can forgo the string to the right of the operator.
+
+```go
+children.#(!%"*a*")                 "Alex"
+children.#(%"*a*")#                 ["Sara","Jack"]
+```
+
+Nested queries are allowed.
+
+```go
+friends.#(nets.#(=="fb"))#.first  >> ["Dale","Roger"]
+```
+
+*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was
+changed in v1.3.0 as to avoid confusion with the new [multipath](#multipaths) 
+syntax. For backwards compatibility, `#[...]` will continue to work until the
+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": "data" },
+    { "a": 2, "b": true },
+    { "a": 3, "b": false },
+    { "a": 4, "b": "0" },
+    { "a": 5, "b": 0 },
+    { "a": 6, "b": "1" },
+    { "a": 7, "b": 1 },
+    { "a": 8, "b": "true" },
+    { "a": 9, "b": false },
+    { "a": 10, "b": null },
+    { "a": 11 }
+  ]
+}
+```
+
+To query for all true-ish or false-ish values:
+
+```
+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 `|`. 
+In most cases they both end up returning the same results.
+The cases where`|` differs from `.` is when it's used after the `#` for [Arrays](#arrays) and [Queries](#queries). 
+
+Here are some examples
+
+```go
+friends.0.first                     "Dale"
+friends|0.first                     "Dale"
+friends.0|first                     "Dale"
+friends|0|first                     "Dale"
+friends|#                           3
+friends.#                           3
+friends.#(last="Murphy")#           [{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}]
+friends.#(last="Murphy")#.first     ["Dale","Jane"]
+friends.#(last="Murphy")#|first     <non-existent>
+friends.#(last="Murphy")#.0         []
+friends.#(last="Murphy")#|0         {"first": "Dale", "last": "Murphy", "age": 44}
+friends.#(last="Murphy")#.#         []
+friends.#(last="Murphy")#|#         2
+```
+
+Let's break down a few of these.
+
+The path `friends.#(last="Murphy")#` all by itself results in
+
+```json
+[{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}]
+```
+
+The `.first` suffix will process the `first` path on each array element *before* returning the results. Which becomes
+
+```json
+["Dale","Jane"]
+```
+
+But the `|first` suffix actually processes the `first` path *after* the previous result. 
+Since the previous result is an array, not an object, it's not possible to process 
+because `first` does not exist.
+
+Yet, `|0` suffix returns
+
+```json
+{"first": "Dale", "last": "Murphy", "age": 44}
+```
+
+Because `0` is the first index of the previous result.
+
+### Modifiers
+
+A modifier is a path component that performs custom processing on the JSON.
+
+For example, using the built-in `@reverse` modifier on the above JSON payload will reverse the `children` array:
+
+```go
+children.@reverse                   ["Jack","Alex","Sara"]
+children.@reverse.0                 "Jack"
+```
+
+There are currently the following built-in modifiers:
+
+- `@reverse`: Reverse an array or the members of an object.
+- `@ugly`: Remove all whitespace from JSON.
+- `@pretty`: Make the JSON more human readable.
+- `@this`: Returns the current element. It can be used to retrieve the root element.
+- `@valid`: Ensure the json document is valid.
+- `@flatten`: Flattens an array.
+- `@join`: Joins multiple objects into a single object.
+- `@keys`: Returns an array of keys for an object.
+- `@values`: Returns an array of values for an object.
+- `@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
+
+A modifier may accept an optional argument. The argument can be a valid JSON payload or just characters.
+
+For example, the `@pretty` modifier takes a json object as its argument.
+
+```
+@pretty:{"sortKeys":true}
+```
+
+Which makes the json pretty and orders all of its keys.
+
+```json
+{
+  "age":37,
+  "children": ["Sara","Alex","Jack"],
+  "fav.movie": "Deer Hunter",
+  "friends": [
+    {"age": 44, "first": "Dale", "last": "Murphy"},
+    {"age": 68, "first": "Roger", "last": "Craig"},
+    {"age": 47, "first": "Jane", "last": "Murphy"}
+  ],
+  "name": {"first": "Tom", "last": "Anderson"}
+}
+```
+
+*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`. 
+Please see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.*
+
+#### Custom modifiers
+
+You can also add custom modifiers. 
+
+For example, here we create a modifier which makes the entire JSON payload upper or lower case.
+
+```go
+gjson.AddModifier("case", func(json, arg string) string {
+  if arg == "upper" {
+    return strings.ToUpper(json)
+  }
+  if arg == "lower" {
+    return strings.ToLower(json)
+  }
+  return json
+})
+"children.@case:upper"             ["SARA","ALEX","JACK"]
+"children.@case:lower.@reverse"    ["jack","alex","sara"]
+```
+
+*Note: Custom modifiers are not yet available in the Rust version*
+
+### Multipaths
+
+Starting with v1.3.0, GJSON added the ability to join multiple paths together
+to form new documents. Wrapping comma-separated paths between `[...]` or
+`{...}` will result in a new array or object, respectively.
+
+For example, using the given multipath:
+
+```
+{name.first,age,"the_murphys":friends.#(last="Murphy")#.first}
+```
+
+Here we selected the first name, age, and the first name for friends with the 
+last name "Murphy".
+
+You'll notice that an optional key can be provided, in this case 
+"the_murphys", to force assign a key to a value. Otherwise, the name of the 
+actual field will be used, in this case "first". If a name cannot be
+determined, then "_" is used.
+
+This results in
+
+```json
+{"first":"Tom","age":37,"the_murphys":["Dale","Jane"]}
+```
+
+### Literals
+
+Starting with v1.12.0, GJSON added support of json literals, which provides a way for constructing static blocks of json. This is can be particularly useful when constructing a new json document using [multipaths](#multipaths).  
+
+A json literal begins with the '!' declaration character. 
+
+For example, using the given multipath:
+
+```
+{name.first,age,"company":!"Happysoft","employed":!true}
+```
+
+Here we selected the first name and age. Then add two new fields, "company" and "employed".
+
+This results in 
+
+```json
+{"first":"Tom","age":37,"company":"Happysoft","employed":true}
+```
+
+*See issue [#249](https://github.com/tidwall/gjson/issues/249) for additional context on JSON Literals.*
diff --git a/vendor/github.com/tidwall/gjson/gjson.go b/vendor/github.com/tidwall/gjson/gjson.go
new file mode 100644
index 0000000..5aa2a4f
--- /dev/null
+++ b/vendor/github.com/tidwall/gjson/gjson.go
@@ -0,0 +1,3603 @@
+// Package gjson provides searching for json strings.
+package gjson
+
+import (
+	"strconv"
+	"strings"
+	"time"
+	"unicode/utf16"
+	"unicode/utf8"
+	"unsafe"
+
+	"github.com/tidwall/match"
+	"github.com/tidwall/pretty"
+)
+
+// Type is Result type
+type Type int
+
+const (
+	// Null is a null json value
+	Null Type = iota
+	// False is a json false boolean
+	False
+	// Number is json number
+	Number
+	// String is a json string
+	String
+	// True is a json true boolean
+	True
+	// JSON is a raw block of JSON
+	JSON
+)
+
+// String returns a string representation of the type.
+func (t Type) String() string {
+	switch t {
+	default:
+		return ""
+	case Null:
+		return "Null"
+	case False:
+		return "False"
+	case Number:
+		return "Number"
+	case String:
+		return "String"
+	case True:
+		return "True"
+	case JSON:
+		return "JSON"
+	}
+}
+
+// Result represents a json value that is returned from Get().
+type Result struct {
+	// Type is the json type
+	Type Type
+	// Raw is the raw json
+	Raw string
+	// Str is the json string
+	Str string
+	// Num is the json number
+	Num float64
+	// Index of raw value in original json, zero means index unknown
+	Index int
+	// Indexes of all the elements that match on a path containing the '#'
+	// query character.
+	Indexes []int
+}
+
+// String returns a string representation of the value.
+func (t Result) String() string {
+	switch t.Type {
+	default:
+		return ""
+	case False:
+		return "false"
+	case Number:
+		if len(t.Raw) == 0 {
+			// calculated result
+			return strconv.FormatFloat(t.Num, 'f', -1, 64)
+		}
+		var i int
+		if t.Raw[0] == '-' {
+			i++
+		}
+		for ; i < len(t.Raw); i++ {
+			if t.Raw[i] < '0' || t.Raw[i] > '9' {
+				return strconv.FormatFloat(t.Num, 'f', -1, 64)
+			}
+		}
+		return t.Raw
+	case String:
+		return t.Str
+	case JSON:
+		return t.Raw
+	case True:
+		return "true"
+	}
+}
+
+// Bool returns an boolean representation.
+func (t Result) Bool() bool {
+	switch t.Type {
+	default:
+		return false
+	case True:
+		return true
+	case String:
+		b, _ := strconv.ParseBool(strings.ToLower(t.Str))
+		return b
+	case Number:
+		return t.Num != 0
+	}
+}
+
+// Int returns an integer representation.
+func (t Result) Int() int64 {
+	switch t.Type {
+	default:
+		return 0
+	case True:
+		return 1
+	case String:
+		n, _ := parseInt(t.Str)
+		return n
+	case Number:
+		// try to directly convert the float64 to int64
+		i, ok := safeInt(t.Num)
+		if ok {
+			return i
+		}
+		// now try to parse the raw string
+		i, ok = parseInt(t.Raw)
+		if ok {
+			return i
+		}
+		// fallback to a standard conversion
+		return int64(t.Num)
+	}
+}
+
+// Uint returns an unsigned integer representation.
+func (t Result) Uint() uint64 {
+	switch t.Type {
+	default:
+		return 0
+	case True:
+		return 1
+	case String:
+		n, _ := parseUint(t.Str)
+		return n
+	case Number:
+		// try to directly convert the float64 to uint64
+		i, ok := safeInt(t.Num)
+		if ok && i >= 0 {
+			return uint64(i)
+		}
+		// now try to parse the raw string
+		u, ok := parseUint(t.Raw)
+		if ok {
+			return u
+		}
+		// fallback to a standard conversion
+		return uint64(t.Num)
+	}
+}
+
+// Float returns an float64 representation.
+func (t Result) Float() float64 {
+	switch t.Type {
+	default:
+		return 0
+	case True:
+		return 1
+	case String:
+		n, _ := strconv.ParseFloat(t.Str, 64)
+		return n
+	case Number:
+		return t.Num
+	}
+}
+
+// Time returns a time.Time representation.
+func (t Result) Time() time.Time {
+	res, _ := time.Parse(time.RFC3339, t.String())
+	return res
+}
+
+// Array returns back an array of values.
+// If the result represents a null value or is non-existent, then an empty
+// array will be returned.
+// If the result is not a JSON array, the return value will be an
+// array containing one result.
+func (t Result) Array() []Result {
+	if t.Type == Null {
+		return []Result{}
+	}
+	if !t.IsArray() {
+		return []Result{t}
+	}
+	r := t.arrayOrMap('[', false)
+	return r.a
+}
+
+// IsObject returns true if the result value is a JSON object.
+func (t Result) IsObject() bool {
+	return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '{'
+}
+
+// IsArray returns true if the result value is a JSON array.
+func (t Result) IsArray() bool {
+	return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '['
+}
+
+// IsBool returns true if the result value is a JSON boolean.
+func (t Result) IsBool() bool {
+	return t.Type == True || t.Type == False
+}
+
+// ForEach iterates through values.
+// If the result represents a non-existent value, then no values will be
+// iterated. If the result is an Object, the iterator will pass the key and
+// value of each item. If the result is an Array, the iterator will only pass
+// the value of each item. If the result is not a JSON array or object, the
+// iterator will pass back one value equal to the result.
+func (t Result) ForEach(iterator func(key, value Result) bool) {
+	if !t.Exists() {
+		return
+	}
+	if t.Type != JSON {
+		iterator(Result{}, t)
+		return
+	}
+	json := t.Raw
+	var obj bool
+	var i int
+	var key, value Result
+	for ; i < len(json); i++ {
+		if json[i] == '{' {
+			i++
+			key.Type = String
+			obj = true
+			break
+		} else if json[i] == '[' {
+			i++
+			key.Type = Number
+			key.Num = -1
+			break
+		}
+		if json[i] > ' ' {
+			return
+		}
+	}
+	var str string
+	var vesc bool
+	var ok bool
+	var idx int
+	for ; i < len(json); i++ {
+		if obj {
+			if json[i] != '"' {
+				continue
+			}
+			s := i
+			i, str, vesc, ok = parseString(json, i+1)
+			if !ok {
+				return
+			}
+			if vesc {
+				key.Str = unescape(str[1 : len(str)-1])
+			} else {
+				key.Str = str[1 : len(str)-1]
+			}
+			key.Raw = str
+			key.Index = s + t.Index
+		} else {
+			key.Num += 1
+		}
+		for ; i < len(json); i++ {
+			if json[i] <= ' ' || json[i] == ',' || json[i] == ':' {
+				continue
+			}
+			break
+		}
+		s := i
+		i, value, ok = parseAny(json, i, true)
+		if !ok {
+			return
+		}
+		if t.Indexes != nil {
+			if idx < len(t.Indexes) {
+				value.Index = t.Indexes[idx]
+			}
+		} else {
+			value.Index = s + t.Index
+		}
+		if !iterator(key, value) {
+			return
+		}
+		idx++
+	}
+}
+
+// Map returns back a map of values. The result should be a JSON object.
+// If the result is not a JSON object, the return value will be an empty map.
+func (t Result) Map() map[string]Result {
+	if t.Type != JSON {
+		return map[string]Result{}
+	}
+	r := t.arrayOrMap('{', false)
+	return r.o
+}
+
+// Get searches result for the specified path.
+// The result should be a JSON array or object.
+func (t Result) Get(path string) Result {
+	r := Get(t.Raw, path)
+	if r.Indexes != nil {
+		for i := 0; i < len(r.Indexes); i++ {
+			r.Indexes[i] += t.Index
+		}
+	} else {
+		r.Index += t.Index
+	}
+	return r
+}
+
+type arrayOrMapResult struct {
+	a  []Result
+	ai []interface{}
+	o  map[string]Result
+	oi map[string]interface{}
+	vc byte
+}
+
+func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) {
+	var json = t.Raw
+	var i int
+	var value Result
+	var count int
+	var key Result
+	if vc == 0 {
+		for ; i < len(json); i++ {
+			if json[i] == '{' || json[i] == '[' {
+				r.vc = json[i]
+				i++
+				break
+			}
+			if json[i] > ' ' {
+				goto end
+			}
+		}
+	} else {
+		for ; i < len(json); i++ {
+			if json[i] == vc {
+				i++
+				break
+			}
+			if json[i] > ' ' {
+				goto end
+			}
+		}
+		r.vc = vc
+	}
+	if r.vc == '{' {
+		if valueize {
+			r.oi = make(map[string]interface{})
+		} else {
+			r.o = make(map[string]Result)
+		}
+	} else {
+		if valueize {
+			r.ai = make([]interface{}, 0)
+		} else {
+			r.a = make([]Result, 0)
+		}
+	}
+	for ; i < len(json); i++ {
+		if json[i] <= ' ' {
+			continue
+		}
+		// get next value
+		if json[i] == ']' || json[i] == '}' {
+			break
+		}
+		switch json[i] {
+		default:
+			if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' {
+				value.Type = Number
+				value.Raw, value.Num = tonum(json[i:])
+				value.Str = ""
+			} else {
+				continue
+			}
+		case '{', '[':
+			value.Type = JSON
+			value.Raw = squash(json[i:])
+			value.Str, value.Num = "", 0
+		case 'n':
+			value.Type = Null
+			value.Raw = tolit(json[i:])
+			value.Str, value.Num = "", 0
+		case 't':
+			value.Type = True
+			value.Raw = tolit(json[i:])
+			value.Str, value.Num = "", 0
+		case 'f':
+			value.Type = False
+			value.Raw = tolit(json[i:])
+			value.Str, value.Num = "", 0
+		case '"':
+			value.Type = String
+			value.Raw, value.Str = tostr(json[i:])
+			value.Num = 0
+		}
+		value.Index = i + t.Index
+
+		i += len(value.Raw) - 1
+
+		if r.vc == '{' {
+			if count%2 == 0 {
+				key = value
+			} else {
+				if valueize {
+					if _, ok := r.oi[key.Str]; !ok {
+						r.oi[key.Str] = value.Value()
+					}
+				} else {
+					if _, ok := r.o[key.Str]; !ok {
+						r.o[key.Str] = value
+					}
+				}
+			}
+			count++
+		} else {
+			if valueize {
+				r.ai = append(r.ai, value.Value())
+			} else {
+				r.a = append(r.a, value)
+			}
+		}
+	}
+end:
+	if t.Indexes != nil {
+		if len(t.Indexes) != len(r.a) {
+			for i := 0; i < len(r.a); i++ {
+				r.a[i].Index = 0
+			}
+		} else {
+			for i := 0; i < len(r.a); i++ {
+				r.a[i].Index = t.Indexes[i]
+			}
+		}
+	}
+	return
+}
+
+// Parse parses the json and returns a result.
+//
+// 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.
+// If you are consuming JSON from an unpredictable source then you may want to
+// use the Valid function first.
+func Parse(json string) Result {
+	var value Result
+	i := 0
+	for ; i < len(json); i++ {
+		if json[i] == '{' || json[i] == '[' {
+			value.Type = JSON
+			value.Raw = json[i:] // just take the entire raw
+			break
+		}
+		if json[i] <= ' ' {
+			continue
+		}
+		switch json[i] {
+		case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+			'i', 'I', 'N':
+			value.Type = Number
+			value.Raw, value.Num = tonum(json[i:])
+		case 'n':
+			if i+1 < len(json) && json[i+1] != 'u' {
+				// nan
+				value.Type = Number
+				value.Raw, value.Num = tonum(json[i:])
+			} else {
+				// null
+				value.Type = Null
+				value.Raw = tolit(json[i:])
+			}
+		case 't':
+			value.Type = True
+			value.Raw = tolit(json[i:])
+		case 'f':
+			value.Type = False
+			value.Raw = tolit(json[i:])
+		case '"':
+			value.Type = String
+			value.Raw, value.Str = tostr(json[i:])
+		default:
+			return Result{}
+		}
+		break
+	}
+	if value.Exists() {
+		value.Index = i
+	}
+	return value
+}
+
+// ParseBytes parses the json and returns a result.
+// If working with bytes, this method preferred over Parse(string(data))
+func ParseBytes(json []byte) Result {
+	return Parse(string(json))
+}
+
+func squash(json string) string {
+	// expects that the lead character is a '[' or '{' or '(' or '"'
+	// squash the value, ignoring all nested arrays and objects.
+	var i, depth int
+	if json[0] != '"' {
+		i, depth = 1, 1
+	}
+	for ; i < len(json); i++ {
+		if json[i] >= '"' && json[i] <= '}' {
+			switch json[i] {
+			case '"':
+				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
+					}
+				}
+				if depth == 0 {
+					if i >= len(json) {
+						return json
+					}
+					return json[:i+1]
+				}
+			case '{', '[', '(':
+				depth++
+			case '}', ']', ')':
+				depth--
+				if depth == 0 {
+					return json[:i+1]
+				}
+			}
+		}
+	}
+	return json
+}
+
+func tonum(json string) (raw string, num float64) {
+	for i := 1; i < len(json); i++ {
+		// less than dash might have valid characters
+		if json[i] <= '-' {
+			if json[i] <= ' ' || json[i] == ',' {
+				// break on whitespace and comma
+				raw = json[:i]
+				num, _ = strconv.ParseFloat(raw, 64)
+				return
+			}
+			// could be a '+' or '-'. let's assume so.
+		} else if json[i] == ']' || json[i] == '}' {
+			// break on ']' or '}'
+			raw = json[:i]
+			num, _ = strconv.ParseFloat(raw, 64)
+			return
+		}
+	}
+	raw = json
+	num, _ = strconv.ParseFloat(raw, 64)
+	return
+}
+
+func tolit(json string) (raw string) {
+	for i := 1; i < len(json); i++ {
+		if json[i] < 'a' || json[i] > 'z' {
+			return json[:i]
+		}
+	}
+	return json
+}
+
+func tostr(json string) (raw string, str string) {
+	// expects that the lead character is a '"'
+	for i := 1; i < len(json); i++ {
+		if json[i] > '\\' {
+			continue
+		}
+		if json[i] == '"' {
+			return json[:i+1], json[1:i]
+		}
+		if json[i] == '\\' {
+			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 > 0; j-- {
+							if json[j] != '\\' {
+								break
+							}
+							n++
+						}
+						if n%2 == 0 {
+							continue
+						}
+					}
+					return json[:i+1], unescape(json[1:i])
+				}
+			}
+			var ret string
+			if i+1 < len(json) {
+				ret = json[:i+1]
+			} else {
+				ret = json[:i]
+			}
+			return ret, unescape(json[1:i])
+		}
+	}
+	return json, json[1:]
+}
+
+// Exists returns true if 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
+}
+
+// Value returns one of these types:
+//
+//	bool, for JSON booleans
+//	float64, for JSON numbers
+//	Number, for JSON numbers
+//	string, for JSON string literals
+//	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
+	}
+	switch t.Type {
+	default:
+		return nil
+	case False:
+		return false
+	case Number:
+		return t.Num
+	case JSON:
+		r := t.arrayOrMap(0, true)
+		if r.vc == '{' {
+			return r.oi
+		} else if r.vc == '[' {
+			return r.ai
+		}
+		return nil
+	case True:
+		return true
+	}
+}
+
+func parseString(json string, i int) (int, string, bool, bool) {
+	var s = i
+	for ; i < len(json); i++ {
+		if json[i] > '\\' {
+			continue
+		}
+		if json[i] == '"' {
+			return i + 1, json[s-1 : i+1], false, true
+		}
+		if json[i] == '\\' {
+			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 > 0; j-- {
+							if json[j] != '\\' {
+								break
+							}
+							n++
+						}
+						if n%2 == 0 {
+							continue
+						}
+					}
+					return i + 1, json[s-1 : i+1], true, true
+				}
+			}
+			break
+		}
+	}
+	return i, json[s-1:], false, false
+}
+
+func parseNumber(json string, i int) (int, string) {
+	var s = i
+	i++
+	for ; i < len(json); i++ {
+		if json[i] <= ' ' || json[i] == ',' || json[i] == ']' ||
+			json[i] == '}' {
+			return i, json[s:i]
+		}
+	}
+	return i, json[s:]
+}
+
+func parseLiteral(json string, i int) (int, string) {
+	var s = i
+	i++
+	for ; i < len(json); i++ {
+		if json[i] < 'a' || json[i] > 'z' {
+			return i, json[s:i]
+		}
+	}
+	return i, json[s:]
+}
+
+type arrayPathResult struct {
+	part    string
+	path    string
+	pipe    string
+	piped   bool
+	more    bool
+	alogok  bool
+	arrch   bool
+	alogkey string
+	query   struct {
+		on    bool
+		all   bool
+		path  string
+		op    string
+		value string
+	}
+}
+
+func parseArrayPath(path string) (r arrayPathResult) {
+	for i := 0; i < len(path); i++ {
+		if path[i] == '|' {
+			r.part = path[:i]
+			r.pipe = path[i+1:]
+			r.piped = true
+			return
+		}
+		if path[i] == '.' {
+			r.part = path[:i]
+			if !r.arrch && i < len(path)-1 && isDotPiperChar(path[i+1:]) {
+				r.pipe = path[i+1:]
+				r.piped = true
+			} else {
+				r.path = path[i+1:]
+				r.more = true
+			}
+			return
+		}
+		if path[i] == '#' {
+			r.arrch = true
+			if i == 0 && len(path) > 1 {
+				if path[1] == '.' {
+					r.alogok = true
+					r.alogkey = path[2:]
+					r.path = path[:1]
+				} else if path[1] == '[' || path[1] == '(' {
+					// query
+					r.query.on = true
+					qpath, op, value, _, fi, vesc, ok :=
+						parseQuery(path[i:])
+					if !ok {
+						// bad query, end now
+						break
+					}
+					if len(value) >= 2 && value[0] == '"' &&
+						value[len(value)-1] == '"' {
+						value = value[1 : len(value)-1]
+						if vesc {
+							value = unescape(value)
+						}
+					}
+					r.query.path = qpath
+					r.query.op = op
+					r.query.value = value
+
+					i = fi - 1
+					if i+1 < len(path) && path[i+1] == '#' {
+						r.query.all = true
+					}
+				}
+			}
+			continue
+		}
+	}
+	r.part = path
+	r.path = ""
+	return
+}
+
+// splitQuery takes a query and splits it into three parts:
+//
+//	path, op, middle, and right.
+//
+// So for this query:
+//
+//	#(first_name=="Murphy").last
+//
+// Becomes
+//
+//	first_name   # path
+//	=="Murphy"   # middle
+//	.last        # right
+//
+// Or,
+//
+//	#(service_roles.#(=="one")).cap
+//
+// Becomes
+//
+//	service_roles.#(=="one")   # path
+//	                           # middle
+//	.cap                       # right
+func parseQuery(query string) (
+	path, op, value, remain string, i int, vesc, ok bool,
+) {
+	if len(query) < 2 || query[0] != '#' ||
+		(query[1] != '(' && query[1] != '[') {
+		return "", "", "", "", i, false, false
+	}
+	i = 2
+	j := 0 // start of value part
+	depth := 1
+	for ; i < len(query); i++ {
+		if depth == 1 && j == 0 {
+			switch query[i] {
+			case '!', '=', '<', '>', '%':
+				// start of the value part
+				j = i
+				continue
+			}
+		}
+		if query[i] == '\\' {
+			i++
+		} else if query[i] == '[' || query[i] == '(' {
+			depth++
+		} else if query[i] == ']' || query[i] == ')' {
+			depth--
+			if depth == 0 {
+				break
+			}
+		} else if query[i] == '"' {
+			// inside selector string, balance quotes
+			i++
+			for ; i < len(query); i++ {
+				if query[i] == '\\' {
+					vesc = true
+					i++
+				} else if query[i] == '"' {
+					break
+				}
+			}
+		}
+	}
+	if depth > 0 {
+		return "", "", "", "", i, false, false
+	}
+	if j > 0 {
+		path = trim(query[2:j])
+		value = trim(query[j:i])
+		remain = query[i+1:]
+		// parse the compare op from the value
+		var opsz int
+		switch {
+		case len(value) == 1:
+			opsz = 1
+		case value[0] == '!' && value[1] == '=':
+			opsz = 2
+		case value[0] == '!' && value[1] == '%':
+			opsz = 2
+		case value[0] == '<' && value[1] == '=':
+			opsz = 2
+		case value[0] == '>' && value[1] == '=':
+			opsz = 2
+		case value[0] == '=' && value[1] == '=':
+			value = value[1:]
+			opsz = 1
+		case value[0] == '<':
+			opsz = 1
+		case value[0] == '>':
+			opsz = 1
+		case value[0] == '=':
+			opsz = 1
+		case value[0] == '%':
+			opsz = 1
+		}
+		op = value[:opsz]
+		value = trim(value[opsz:])
+	} else {
+		path = trim(query[2:i])
+		remain = query[i+1:]
+	}
+	return path, op, value, remain, i + 1, vesc, true
+}
+
+func trim(s string) string {
+left:
+	if len(s) > 0 && s[0] <= ' ' {
+		s = s[1:]
+		goto left
+	}
+right:
+	if len(s) > 0 && s[len(s)-1] <= ' ' {
+		s = s[:len(s)-1]
+		goto right
+	}
+	return s
+}
+
+// peek at the next byte and see if it's a '@', '[', or '{'.
+func isDotPiperChar(s string) bool {
+	if DisableModifiers {
+		return false
+	}
+	c := s[0]
+	if c == '@' {
+		// check that the next component is *not* a modifier.
+		i := 1
+		for ; i < len(s); i++ {
+			if s[i] == '.' || s[i] == '|' || s[i] == ':' {
+				break
+			}
+		}
+		_, ok := modifiers[s[1:i]]
+		return ok
+	}
+	return c == '[' || c == '{'
+}
+
+type objectPathResult struct {
+	part  string
+	path  string
+	pipe  string
+	piped bool
+	wild  bool
+	more  bool
+}
+
+func parseObjectPath(path string) (r objectPathResult) {
+	for i := 0; i < len(path); i++ {
+		if path[i] == '|' {
+			r.part = path[:i]
+			r.pipe = path[i+1:]
+			r.piped = true
+			return
+		}
+		if path[i] == '.' {
+			r.part = path[:i]
+			if i < len(path)-1 && isDotPiperChar(path[i+1:]) {
+				r.pipe = path[i+1:]
+				r.piped = true
+			} else {
+				r.path = path[i+1:]
+				r.more = true
+			}
+			return
+		}
+		if path[i] == '*' || path[i] == '?' {
+			r.wild = true
+			continue
+		}
+		if path[i] == '\\' {
+			// go into escape mode. this is a slower path that
+			// strips off the escape character from the part.
+			epart := []byte(path[:i])
+			i++
+			if i < len(path) {
+				epart = append(epart, path[i])
+				i++
+				for ; i < len(path); i++ {
+					if path[i] == '\\' {
+						i++
+						if i < len(path) {
+							epart = append(epart, path[i])
+						}
+						continue
+					} else if path[i] == '.' {
+						r.part = string(epart)
+						if i < len(path)-1 && isDotPiperChar(path[i+1:]) {
+							r.pipe = path[i+1:]
+							r.piped = true
+						} else {
+							r.path = path[i+1:]
+							r.more = true
+						}
+						return
+					} else if path[i] == '|' {
+						r.part = string(epart)
+						r.pipe = path[i+1:]
+						r.piped = true
+						return
+					} else if path[i] == '*' || path[i] == '?' {
+						r.wild = true
+					}
+					epart = append(epart, path[i])
+				}
+			}
+			// append the last part
+			r.part = string(epart)
+			return
+		}
+	}
+	r.part = path
+	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.
+	// the first '[' or '{' or '(' has already been read
+	s := i
+	i++
+	depth := 1
+	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++
+				continue
+			}
+		strchkstd:
+			for i < len(json) {
+				if json[i] > '\\' || json[i] != '"' {
+					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:]
+}
+
+func parseObject(c *parseContext, i int, path string) (int, bool) {
+	var pmatch, kesc, vesc, ok, hit bool
+	var key, val string
+	rp := parseObjectPath(path)
+	if !rp.more && rp.piped {
+		c.pipe = rp.pipe
+		c.piped = true
+	}
+	for i < len(c.json) {
+		for ; i < len(c.json); i++ {
+			if c.json[i] == '"' {
+				// parse_key_string
+				// this is slightly different from getting s string value
+				// because we don't need the outer quotes.
+				i++
+				var s = i
+				for ; i < len(c.json); i++ {
+					if c.json[i] > '\\' {
+						continue
+					}
+					if c.json[i] == '"' {
+						i, key, kesc, ok = i+1, c.json[s:i], false, true
+						goto parse_key_string_done
+					}
+					if c.json[i] == '\\' {
+						i++
+						for ; i < len(c.json); i++ {
+							if c.json[i] > '\\' {
+								continue
+							}
+							if c.json[i] == '"' {
+								// look for an escaped slash
+								if c.json[i-1] == '\\' {
+									n := 0
+									for j := i - 2; j > 0; j-- {
+										if c.json[j] != '\\' {
+											break
+										}
+										n++
+									}
+									if n%2 == 0 {
+										continue
+									}
+								}
+								i, key, kesc, ok = i+1, c.json[s:i], true, true
+								goto parse_key_string_done
+							}
+						}
+						break
+					}
+				}
+				key, kesc, ok = c.json[s:], false, false
+			parse_key_string_done:
+				break
+			}
+			if c.json[i] == '}' {
+				return i + 1, false
+			}
+		}
+		if !ok {
+			return i, false
+		}
+		if rp.wild {
+			if kesc {
+				pmatch = matchLimit(unescape(key), rp.part)
+			} else {
+				pmatch = matchLimit(key, rp.part)
+			}
+		} else {
+			if kesc {
+				pmatch = rp.part == unescape(key)
+			} else {
+				pmatch = rp.part == key
+			}
+		}
+		hit = pmatch && !rp.more
+		for ; i < len(c.json); i++ {
+			var num bool
+			switch c.json[i] {
+			default:
+				continue
+			case '"':
+				i++
+				i, val, vesc, ok = parseString(c.json, i)
+				if !ok {
+					return i, false
+				}
+				if hit {
+					if vesc {
+						c.value.Str = unescape(val[1 : len(val)-1])
+					} else {
+						c.value.Str = val[1 : len(val)-1]
+					}
+					c.value.Raw = val
+					c.value.Type = String
+					return i, true
+				}
+			case '{':
+				if pmatch && !hit {
+					i, hit = parseObject(c, i+1, rp.path)
+					if hit {
+						return i, true
+					}
+				} else {
+					i, val = parseSquash(c.json, i)
+					if hit {
+						c.value.Raw = val
+						c.value.Type = JSON
+						return i, true
+					}
+				}
+			case '[':
+				if pmatch && !hit {
+					i, hit = parseArray(c, i+1, rp.path)
+					if hit {
+						return i, true
+					}
+				} else {
+					i, val = parseSquash(c.json, i)
+					if hit {
+						c.value.Raw = val
+						c.value.Type = JSON
+						return i, true
+					}
+				}
+			case 'n':
+				if i+1 < len(c.json) && c.json[i+1] != 'u' {
+					num = true
+					break
+				}
+				fallthrough
+			case 't', 'f':
+				vc := c.json[i]
+				i, val = parseLiteral(c.json, i)
+				if hit {
+					c.value.Raw = val
+					switch vc {
+					case 't':
+						c.value.Type = True
+					case 'f':
+						c.value.Type = False
+					}
+					return i, true
+				}
+			case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+				'i', 'I', 'N':
+				num = true
+			}
+			if num {
+				i, val = parseNumber(c.json, i)
+				if hit {
+					c.value.Raw = val
+					c.value.Type = Number
+					c.value.Num, _ = strconv.ParseFloat(val, 64)
+					return i, true
+				}
+			}
+			break
+		}
+	}
+	return i, false
+}
+
+// matchLimit will limit the complexity of the match operation to avoid ReDos
+// 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 {
+		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() {
+		return false
+	}
+	if rp.query.op == "" {
+		// the query is only looking for existence, such as:
+		//   friends.#(name)
+		// which makes sure that the array "friends" has an element of
+		// "name" that exists
+		return true
+	}
+	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 ">=":
+			return value.Str >= rpv
+		case "%":
+			return matchLimit(value.Str, rpv)
+		case "!%":
+			return !matchLimit(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 ">=":
+			return value.Num >= rpvn
+		}
+	case True:
+		switch rp.query.op {
+		case "=":
+			return rpv == "true"
+		case "!=":
+			return rpv != "true"
+		case ">":
+			return rpv == "false"
+		case ">=":
+			return true
+		}
+	case False:
+		switch rp.query.op {
+		case "=":
+			return rpv == "false"
+		case "!=":
+			return rpv != "false"
+		case "<":
+			return rpv == "true"
+		case "<=":
+			return true
+		}
+	}
+	return false
+}
+func parseArray(c *parseContext, i int, path string) (int, bool) {
+	var pmatch, vesc, ok, hit bool
+	var val string
+	var h int
+	var alog []int
+	var partidx int
+	var multires []byte
+	var queryIndexes []int
+	rp := parseArrayPath(path)
+	if !rp.arrch {
+		n, ok := parseUint(rp.part)
+		if !ok {
+			partidx = -1
+		} else {
+			partidx = int(n)
+		}
+	}
+	if !rp.more && rp.piped {
+		c.pipe = rp.pipe
+		c.piped = true
+	}
+
+	procQuery := func(qval Result) bool {
+		if rp.query.all {
+			if len(multires) == 0 {
+				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)
+		} else {
+			if rp.query.path != "" {
+				return false
+			}
+			res = qval
+		}
+		if queryMatches(&rp, res) {
+			if rp.more {
+				left, right, ok := splitPossiblePipe(rp.path)
+				if ok {
+					rp.path = left
+					c.pipe = right
+					c.piped = true
+				}
+				res = qval.Get(rp.path)
+			} else {
+				res = qval
+			}
+			if rp.query.all {
+				raw := res.Raw
+				if len(raw) == 0 {
+					raw = res.String()
+				}
+				if raw != "" {
+					if len(multires) > 1 {
+						multires = append(multires, ',')
+					}
+					multires = append(multires, raw...)
+					queryIndexes = append(queryIndexes, res.Index+parentIndex)
+				}
+			} else {
+				c.value = res
+				return true
+			}
+		}
+		return false
+	}
+	for i < len(c.json)+1 {
+		if !rp.arrch {
+			pmatch = partidx == h
+			hit = pmatch && !rp.more
+		}
+		h++
+		if rp.alogok {
+			alog = append(alog, i)
+		}
+		for ; ; i++ {
+			var ch byte
+			if i > len(c.json) {
+				break
+			} else if i == len(c.json) {
+				ch = ']'
+			} else {
+				ch = c.json[i]
+			}
+			var num bool
+			switch ch {
+			default:
+				continue
+			case '"':
+				i++
+				i, val, vesc, ok = parseString(c.json, i)
+				if !ok {
+					return i, false
+				}
+				if rp.query.on {
+					var qval Result
+					if vesc {
+						qval.Str = unescape(val[1 : len(val)-1])
+					} else {
+						qval.Str = val[1 : len(val)-1]
+					}
+					qval.Raw = val
+					qval.Type = String
+					if procQuery(qval) {
+						return i, true
+					}
+				} else if hit {
+					if rp.alogok {
+						break
+					}
+					if vesc {
+						c.value.Str = unescape(val[1 : len(val)-1])
+					} else {
+						c.value.Str = val[1 : len(val)-1]
+					}
+					c.value.Raw = val
+					c.value.Type = String
+					return i, true
+				}
+			case '{':
+				if pmatch && !hit {
+					i, hit = parseObject(c, i+1, rp.path)
+					if hit {
+						if rp.alogok {
+							break
+						}
+						return i, true
+					}
+				} else {
+					i, val = parseSquash(c.json, i)
+					if rp.query.on {
+						if procQuery(Result{Raw: val, Type: JSON}) {
+							return i, true
+						}
+					} else if hit {
+						if rp.alogok {
+							break
+						}
+						c.value.Raw = val
+						c.value.Type = JSON
+						return i, true
+					}
+				}
+			case '[':
+				if pmatch && !hit {
+					i, hit = parseArray(c, i+1, rp.path)
+					if hit {
+						if rp.alogok {
+							break
+						}
+						return i, true
+					}
+				} else {
+					i, val = parseSquash(c.json, i)
+					if rp.query.on {
+						if procQuery(Result{Raw: val, Type: JSON}) {
+							return i, true
+						}
+					} else if hit {
+						if rp.alogok {
+							break
+						}
+						c.value.Raw = val
+						c.value.Type = JSON
+						return i, true
+					}
+				}
+			case 'n':
+				if i+1 < len(c.json) && c.json[i+1] != 'u' {
+					num = true
+					break
+				}
+				fallthrough
+			case 't', 'f':
+				vc := c.json[i]
+				i, val = parseLiteral(c.json, i)
+				if rp.query.on {
+					var qval Result
+					qval.Raw = val
+					switch vc {
+					case 't':
+						qval.Type = True
+					case 'f':
+						qval.Type = False
+					}
+					if procQuery(qval) {
+						return i, true
+					}
+				} else if hit {
+					if rp.alogok {
+						break
+					}
+					c.value.Raw = val
+					switch vc {
+					case 't':
+						c.value.Type = True
+					case 'f':
+						c.value.Type = False
+					}
+					return i, true
+				}
+			case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+				'i', 'I', 'N':
+				num = true
+			case ']':
+				if rp.arrch && rp.part == "#" {
+					if rp.alogok {
+						left, right, ok := splitPossiblePipe(rp.alogkey)
+						if ok {
+							rp.alogkey = left
+							c.pipe = right
+							c.piped = true
+						}
+						var indexes = make([]int, 0, 64)
+						var jsons = make([]byte, 0, 64)
+						jsons = append(jsons, '[')
+						for j, k := 0, 0; j < len(alog); j++ {
+							idx := alog[j]
+							for idx < len(c.json) {
+								switch c.json[idx] {
+								case ' ', '\t', '\r', '\n':
+									idx++
+									continue
+								}
+								break
+							}
+							if idx < len(c.json) && c.json[idx] != ']' {
+								_, res, ok := parseAny(c.json, idx, true)
+								if ok {
+									res := res.Get(rp.alogkey)
+									if res.Exists() {
+										if k > 0 {
+											jsons = append(jsons, ',')
+										}
+										raw := res.Raw
+										if len(raw) == 0 {
+											raw = res.String()
+										}
+										jsons = append(jsons, []byte(raw)...)
+										indexes = append(indexes, res.Index)
+										k++
+									}
+								}
+							}
+						}
+						jsons = append(jsons, ']')
+						c.value.Type = JSON
+						c.value.Raw = string(jsons)
+						c.value.Indexes = indexes
+						return i + 1, true
+					}
+					if rp.alogok {
+						break
+					}
+
+					c.value.Type = Number
+					c.value.Num = float64(h - 1)
+					c.value.Raw = strconv.Itoa(h - 1)
+					c.calcd = true
+					return i + 1, true
+				}
+				if !c.value.Exists() {
+					if len(multires) > 0 {
+						c.value = Result{
+							Raw:     string(append(multires, ']')),
+							Type:    JSON,
+							Indexes: queryIndexes,
+						}
+					} else if rp.query.all {
+						c.value = Result{
+							Raw:  "[]",
+							Type: JSON,
+						}
+					}
+				}
+				return i + 1, false
+			}
+			if num {
+				i, val = parseNumber(c.json, i)
+				if rp.query.on {
+					var qval Result
+					qval.Raw = val
+					qval.Type = Number
+					qval.Num, _ = strconv.ParseFloat(val, 64)
+					if procQuery(qval) {
+						return i, true
+					}
+				} else if hit {
+					if rp.alogok {
+						break
+					}
+					c.value.Raw = val
+					c.value.Type = Number
+					c.value.Num, _ = strconv.ParseFloat(val, 64)
+					return i, true
+				}
+			}
+			break
+		}
+	}
+	return i, false
+}
+
+func splitPossiblePipe(path string) (left, right string, ok bool) {
+	// take a quick peek for the pipe character. If found we'll split the piped
+	// part of the path into the c.pipe field and shorten the rp.
+	var possible bool
+	for i := 0; i < len(path); i++ {
+		if path[i] == '|' {
+			possible = true
+			break
+		}
+	}
+	if !possible {
+		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
+	// the delimiter. This is a little tricky because we'll need to basically
+	// parse the entire path.
+	for i := 0; i < len(path); i++ {
+		if path[i] == '\\' {
+			i++
+		} else if path[i] == '.' {
+			if i == len(path)-1 {
+				return
+			}
+			if path[i+1] == '#' {
+				i += 2
+				if i == len(path) {
+					return
+				}
+				if path[i] == '[' || path[i] == '(' {
+					var start, end byte
+					if path[i] == '[' {
+						start, end = '[', ']'
+					} else {
+						start, end = '(', ')'
+					}
+					// inside selector, balance brackets
+					i++
+					depth := 1
+					for ; i < len(path); i++ {
+						if path[i] == '\\' {
+							i++
+						} else if path[i] == start {
+							depth++
+						} else if path[i] == end {
+							depth--
+							if depth == 0 {
+								break
+							}
+						} else if path[i] == '"' {
+							// inside selector string, balance quotes
+							i++
+							for ; i < len(path); i++ {
+								if path[i] == '\\' {
+									i++
+								} else if path[i] == '"' {
+									break
+								}
+							}
+						}
+					}
+				}
+			}
+		} else if path[i] == '|' {
+			return path[:i], path[i+1:], true
+		}
+	}
+	return
+}
+
+// ForEachLine iterates through lines of JSON as specified by the JSON Lines
+// format (http://jsonlines.org/).
+// Each line is returned as a GJSON Result.
+func ForEachLine(json string, iterator func(line Result) bool) {
+	var res Result
+	var i int
+	for {
+		i, res, _ = parseAny(json, i, true)
+		if !res.Exists() {
+			break
+		}
+		if !iterator(res) {
+			return
+		}
+	}
+}
+
+type subSelector struct {
+	name string
+	path string
+}
+
+// parseSubSelectors returns the subselectors belonging to a '[path1,path2]' or
+// '{"field1":path1,"field2":path2}' type subSelection. It's expected that the
+// first character in path is either '[' or '{', and has already been checked
+// prior to calling this function.
+func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) {
+	modifier := 0
+	depth := 1
+	colon := 0
+	start := 1
+	i := 1
+	pushSel := func() {
+		var sel subSelector
+		if colon == 0 {
+			sel.path = path[start:i]
+		} else {
+			sel.name = path[start:colon]
+			sel.path = path[colon+1 : i]
+		}
+		sels = append(sels, sel)
+		colon = 0
+		modifier = 0
+		start = i + 1
+	}
+	for ; i < len(path); i++ {
+		switch path[i] {
+		case '\\':
+			i++
+		case '@':
+			if modifier == 0 && i > 0 && (path[i-1] == '.' || path[i-1] == '|') {
+				modifier = i
+			}
+		case ':':
+			if modifier == 0 && colon == 0 && depth == 1 {
+				colon = i
+			}
+		case ',':
+			if depth == 1 {
+				pushSel()
+			}
+		case '"':
+			i++
+		loop:
+			for ; i < len(path); i++ {
+				switch path[i] {
+				case '\\':
+					i++
+				case '"':
+					break loop
+				}
+			}
+		case '[', '(', '{':
+			depth++
+		case ']', ')', '}':
+			depth--
+			if depth == 0 {
+				pushSel()
+				path = path[i+1:]
+				return sels, path, true
+			}
+		}
+	}
+	return
+}
+
+// nameOfLast returns the name of the last component
+func nameOfLast(path string) string {
+	for i := len(path) - 1; i >= 0; i-- {
+		if path[i] == '|' || path[i] == '.' {
+			if i > 0 {
+				if path[i-1] == '\\' {
+					continue
+				}
+			}
+			return path[i+1:]
+		}
+	}
+	return path
+}
+
+func isSimpleName(component string) bool {
+	for i := 0; i < len(component); i++ {
+		if component[i] < ' ' {
+			return false
+		}
+		switch component[i] {
+		case '[', ']', '{', '}', '(', ')', '#', '|', '!':
+			return false
+		}
+	}
+	return true
+}
+
+var hexchars = [...]byte{
+	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+	'a', 'b', 'c', 'd', 'e', 'f',
+}
+
+func appendHex16(dst []byte, x uint16) []byte {
+	return append(dst,
+		hexchars[x>>12&0xF], hexchars[x>>8&0xF],
+		hexchars[x>>4&0xF], hexchars[x>>0&0xF],
+	)
+}
+
+// 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 {
+	dst = append(dst, make([]byte, len(s)+2)...)
+	dst = append(dst[:len(dst)-len(s)-2], '"')
+	for i := 0; i < len(s); i++ {
+		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':
+				dst = append(dst, 'r')
+			case '\t':
+				dst = append(dst, 't')
+			default:
+				dst = append(dst, 'u')
+				dst = appendHex16(dst, uint16(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] == '\\' {
+			dst = append(dst, '\\', '\\')
+		} else if s[i] == '"' {
+			dst = append(dst, '\\', '"')
+		} else if s[i] > 127 {
+			// read utf8 character
+			r, n := utf8.DecodeRuneInString(s[i:])
+			if n == 0 {
+				break
+			}
+			if r == utf8.RuneError && n == 1 {
+				dst = append(dst, `\ufffd`...)
+			} else if r == '\u2028' || r == '\u2029' {
+				dst = append(dst, `\u202`...)
+				dst = append(dst, hexchars[r&0xF])
+			} else {
+				dst = append(dst, s[i:i+n]...)
+			}
+			i = i + n - 1
+		} else {
+			dst = append(dst, s[i])
+		}
+	}
+	return append(dst, '"')
+}
+
+type parseContext struct {
+	json  string
+	value Result
+	pipe  string
+	piped bool
+	calcd bool
+	lines bool
+}
+
+// Get searches json for the specified path.
+// A path is in dot syntax, such as "name.last" or "age".
+// When the value is found it's returned immediately.
+//
+// A path is a series of keys separated by a dot.
+// A key may contain special wildcard characters '*' and '?'.
+// To access an array value use the index as the key.
+// To get the number of elements in an array or to access a child path, use
+// 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"]
+//
+// 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.
+// If you are consuming JSON from an unpredictable source then you may want to
+// use the Valid function first.
+func Get(json, path string) Result {
+	if len(path) > 1 {
+		if (path[0] == '@' && !DisableModifiers) || path[0] == '!' {
+			// possible modifier
+			var ok bool
+			var npath string
+			var rjson string
+			if path[0] == '@' && !DisableModifiers {
+				npath, rjson, ok = execModifier(json, path)
+			} else if path[0] == '!' {
+				npath, rjson, ok = execStatic(json, path)
+			}
+			if ok {
+				path = npath
+				if len(path) > 0 && (path[0] == '|' || path[0] == '.') {
+					res := Get(rjson, path[1:])
+					res.Index = 0
+					res.Indexes = nil
+					return res
+				}
+				return Parse(rjson)
+			}
+		}
+		if path[0] == '[' || path[0] == '{' {
+			// using a subselector path
+			kind := path[0]
+			var ok bool
+			var subs []subSelector
+			subs, path, ok = parseSubSelectors(path)
+			if ok {
+				if len(path) == 0 || (path[0] == '|' || path[0] == '.') {
+					var b []byte
+					b = append(b, kind)
+					var i int
+					for _, sub := range subs {
+						res := Get(json, sub.path)
+						if res.Exists() {
+							if i > 0 {
+								b = append(b, ',')
+							}
+							if kind == '{' {
+								if len(sub.name) > 0 {
+									if sub.name[0] == '"' && Valid(sub.name) {
+										b = append(b, sub.name...)
+									} else {
+										b = AppendJSONString(b, sub.name)
+									}
+								} else {
+									last := nameOfLast(sub.path)
+									if isSimpleName(last) {
+										b = AppendJSONString(b, last)
+									} else {
+										b = AppendJSONString(b, "_")
+									}
+								}
+								b = append(b, ':')
+							}
+							var raw string
+							if len(res.Raw) == 0 {
+								raw = res.String()
+								if len(raw) == 0 {
+									raw = "null"
+								}
+							} else {
+								raw = res.Raw
+							}
+							b = append(b, raw...)
+							i++
+						}
+					}
+					b = append(b, kind+2)
+					var res Result
+					res.Raw = string(b)
+					res.Type = JSON
+					if len(path) > 0 {
+						res = res.Get(path[1:])
+					}
+					res.Index = 0
+					return res
+				}
+			}
+		}
+	}
+	var i int
+	var c = &parseContext{json: json}
+	if len(path) >= 2 && path[0] == '.' && path[1] == '.' {
+		c.lines = true
+		parseArray(c, 0, path[2:])
+	} else {
+		for ; i < len(c.json); i++ {
+			if c.json[i] == '{' {
+				i++
+				parseObject(c, i, path)
+				break
+			}
+			if c.json[i] == '[' {
+				i++
+				parseArray(c, i, path)
+				break
+			}
+		}
+	}
+	if c.piped {
+		res := c.value.Get(c.pipe)
+		res.Index = 0
+		return res
+	}
+	fillIndex(json, c)
+	return c.value
+}
+
+// GetBytes searches json for the specified path.
+// If working with bytes, this method preferred over Get(string(data), path)
+func GetBytes(json []byte, path string) Result {
+	return getBytes(json, path)
+}
+
+// runeit returns the rune from the the \uXXXX
+func runeit(json string) rune {
+	n, _ := strconv.ParseUint(json[:4], 16, 64)
+	return rune(n)
+}
+
+// unescape unescapes a string
+func unescape(json string) string {
+	var str = make([]byte, 0, len(json))
+	for i := 0; i < len(json); i++ {
+		switch {
+		default:
+			str = append(str, json[i])
+		case json[i] < ' ':
+			return string(str)
+		case json[i] == '\\':
+			i++
+			if i >= len(json) {
+				return string(str)
+			}
+			switch json[i] {
+			default:
+				return string(str)
+			case '\\':
+				str = append(str, '\\')
+			case '/':
+				str = append(str, '/')
+			case 'b':
+				str = append(str, '\b')
+			case 'f':
+				str = append(str, '\f')
+			case 'n':
+				str = append(str, '\n')
+			case 'r':
+				str = append(str, '\r')
+			case 't':
+				str = append(str, '\t')
+			case '"':
+				str = append(str, '"')
+			case 'u':
+				if i+5 > len(json) {
+					return string(str)
+				}
+				r := runeit(json[i+1:])
+				i += 5
+				if utf16.IsSurrogate(r) {
+					// need another code
+					if len(json[i:]) >= 6 && json[i] == '\\' &&
+						json[i+1] == 'u' {
+						// we expect it to be correct so just consume it
+						r = utf16.DecodeRune(r, runeit(json[i+2:]))
+						i += 6
+					}
+				}
+				// provide enough space to encode the largest utf8 possible
+				str = append(str, 0, 0, 0, 0, 0, 0, 0, 0)
+				n := utf8.EncodeRune(str[len(str)-8:], r)
+				str = str[:len(str)-8+n]
+				i-- // backtrack index by one
+			}
+		}
+	}
+	return string(str)
+}
+
+// Less return true if a token is less than another token.
+// The caseSensitive parameter is used when the tokens are Strings.
+// The order when comparing two different type is:
+//
+//	Null < False < Number < String < True < JSON
+func (t Result) Less(token Result, caseSensitive bool) bool {
+	if t.Type < token.Type {
+		return true
+	}
+	if t.Type > token.Type {
+		return false
+	}
+	if t.Type == String {
+		if caseSensitive {
+			return t.Str < token.Str
+		}
+		return stringLessInsensitive(t.Str, token.Str)
+	}
+	if t.Type == Number {
+		return t.Num < token.Num
+	}
+	return t.Raw < token.Raw
+}
+
+func stringLessInsensitive(a, b string) bool {
+	for i := 0; i < len(a) && i < len(b); i++ {
+		if a[i] >= 'A' && a[i] <= 'Z' {
+			if b[i] >= 'A' && b[i] <= 'Z' {
+				// both are uppercase, do nothing
+				if a[i] < b[i] {
+					return true
+				} else if a[i] > b[i] {
+					return false
+				}
+			} else {
+				// a is uppercase, convert a to lowercase
+				if a[i]+32 < b[i] {
+					return true
+				} else if a[i]+32 > b[i] {
+					return false
+				}
+			}
+		} else if b[i] >= 'A' && b[i] <= 'Z' {
+			// b is uppercase, convert b to lowercase
+			if a[i] < b[i]+32 {
+				return true
+			} else if a[i] > b[i]+32 {
+				return false
+			}
+		} else {
+			// neither are uppercase
+			if a[i] < b[i] {
+				return true
+			} else if a[i] > b[i] {
+				return false
+			}
+		}
+	}
+	return len(a) < len(b)
+}
+
+// parseAny parses the next value from a json string.
+// A Result is returned when the hit param is set.
+// The return values are (i int, res Result, ok bool)
+func parseAny(json string, i int, hit bool) (int, Result, bool) {
+	var res Result
+	var val string
+	for ; i < len(json); i++ {
+		if json[i] == '{' || json[i] == '[' {
+			i, val = parseSquash(json, i)
+			if hit {
+				res.Raw = val
+				res.Type = JSON
+			}
+			var tmp parseContext
+			tmp.value = res
+			fillIndex(json, &tmp)
+			return i, tmp.value, true
+		}
+		if json[i] <= ' ' {
+			continue
+		}
+		var num bool
+		switch json[i] {
+		case '"':
+			i++
+			var vesc bool
+			var ok bool
+			i, val, vesc, ok = parseString(json, i)
+			if !ok {
+				return i, res, false
+			}
+			if hit {
+				res.Type = String
+				res.Raw = val
+				if vesc {
+					res.Str = unescape(val[1 : len(val)-1])
+				} else {
+					res.Str = val[1 : len(val)-1]
+				}
+			}
+			return i, res, true
+		case 'n':
+			if i+1 < len(json) && json[i+1] != 'u' {
+				num = true
+				break
+			}
+			fallthrough
+		case 't', 'f':
+			vc := json[i]
+			i, val = parseLiteral(json, i)
+			if hit {
+				res.Raw = val
+				switch vc {
+				case 't':
+					res.Type = True
+				case 'f':
+					res.Type = False
+				}
+				return i, res, true
+			}
+		case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+			'i', 'I', 'N':
+			num = true
+		}
+		if num {
+			i, val = parseNumber(json, i)
+			if hit {
+				res.Raw = val
+				res.Type = Number
+				res.Num, _ = strconv.ParseFloat(val, 64)
+			}
+			return i, res, true
+		}
+
+	}
+	return i, res, false
+}
+
+// GetMany searches json for the multiple paths.
+// The return value is a Result array where the number of items
+// will be equal to the number of input paths.
+func GetMany(json string, path ...string) []Result {
+	res := make([]Result, len(path))
+	for i, path := range path {
+		res[i] = Get(json, path)
+	}
+	return res
+}
+
+// GetManyBytes searches json for the multiple paths.
+// The return value is a Result array where the number of items
+// will be equal to the number of input paths.
+func GetManyBytes(json []byte, path ...string) []Result {
+	res := make([]Result, len(path))
+	for i, path := range path {
+		res[i] = GetBytes(json, path)
+	}
+	return res
+}
+
+func validpayload(data []byte, i int) (outi int, ok bool) {
+	for ; i < len(data); i++ {
+		switch data[i] {
+		default:
+			i, ok = validany(data, i)
+			if !ok {
+				return i, false
+			}
+			for ; i < len(data); i++ {
+				switch data[i] {
+				default:
+					return i, false
+				case ' ', '\t', '\n', '\r':
+					continue
+				}
+			}
+			return i, true
+		case ' ', '\t', '\n', '\r':
+			continue
+		}
+	}
+	return i, false
+}
+func validany(data []byte, i int) (outi int, ok bool) {
+	for ; i < len(data); i++ {
+		switch data[i] {
+		default:
+			return i, false
+		case ' ', '\t', '\n', '\r':
+			continue
+		case '{':
+			return validobject(data, i+1)
+		case '[':
+			return validarray(data, i+1)
+		case '"':
+			return validstring(data, i+1)
+		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+			return validnumber(data, i+1)
+		case 't':
+			return validtrue(data, i+1)
+		case 'f':
+			return validfalse(data, i+1)
+		case 'n':
+			return validnull(data, i+1)
+		}
+	}
+	return i, false
+}
+func validobject(data []byte, i int) (outi int, ok bool) {
+	for ; i < len(data); i++ {
+		switch data[i] {
+		default:
+			return i, false
+		case ' ', '\t', '\n', '\r':
+			continue
+		case '}':
+			return i + 1, true
+		case '"':
+		key:
+			if i, ok = validstring(data, i+1); !ok {
+				return i, false
+			}
+			if i, ok = validcolon(data, i); !ok {
+				return i, false
+			}
+			if i, ok = validany(data, i); !ok {
+				return i, false
+			}
+			if i, ok = validcomma(data, i, '}'); !ok {
+				return i, false
+			}
+			if data[i] == '}' {
+				return i + 1, true
+			}
+			i++
+			for ; i < len(data); i++ {
+				switch data[i] {
+				default:
+					return i, false
+				case ' ', '\t', '\n', '\r':
+					continue
+				case '"':
+					goto key
+				}
+			}
+			return i, false
+		}
+	}
+	return i, false
+}
+func validcolon(data []byte, i int) (outi int, ok bool) {
+	for ; i < len(data); i++ {
+		switch data[i] {
+		default:
+			return i, false
+		case ' ', '\t', '\n', '\r':
+			continue
+		case ':':
+			return i + 1, true
+		}
+	}
+	return i, false
+}
+func validcomma(data []byte, i int, end byte) (outi int, ok bool) {
+	for ; i < len(data); i++ {
+		switch data[i] {
+		default:
+			return i, false
+		case ' ', '\t', '\n', '\r':
+			continue
+		case ',':
+			return i, true
+		case end:
+			return i, true
+		}
+	}
+	return i, false
+}
+func validarray(data []byte, i int) (outi int, ok bool) {
+	for ; i < len(data); i++ {
+		switch data[i] {
+		default:
+			for ; i < len(data); i++ {
+				if i, ok = validany(data, i); !ok {
+					return i, false
+				}
+				if i, ok = validcomma(data, i, ']'); !ok {
+					return i, false
+				}
+				if data[i] == ']' {
+					return i + 1, true
+				}
+			}
+		case ' ', '\t', '\n', '\r':
+			continue
+		case ']':
+			return i + 1, true
+		}
+	}
+	return i, false
+}
+func validstring(data []byte, i int) (outi int, ok bool) {
+	for ; i < len(data); i++ {
+		if data[i] < ' ' {
+			return i, false
+		} else if data[i] == '\\' {
+			i++
+			if i == len(data) {
+				return i, false
+			}
+			switch data[i] {
+			default:
+				return i, false
+			case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
+			case 'u':
+				for j := 0; j < 4; j++ {
+					i++
+					if i >= len(data) {
+						return i, false
+					}
+					if !((data[i] >= '0' && data[i] <= '9') ||
+						(data[i] >= 'a' && data[i] <= 'f') ||
+						(data[i] >= 'A' && data[i] <= 'F')) {
+						return i, false
+					}
+				}
+			}
+		} else if data[i] == '"' {
+			return i + 1, true
+		}
+	}
+	return i, false
+}
+func validnumber(data []byte, i int) (outi int, ok bool) {
+	i--
+	// sign
+	if data[i] == '-' {
+		i++
+		if i == len(data) {
+			return i, false
+		}
+		if data[i] < '0' || data[i] > '9' {
+			return i, false
+		}
+	}
+	// int
+	if i == len(data) {
+		return i, false
+	}
+	if data[i] == '0' {
+		i++
+	} else {
+		for ; i < len(data); i++ {
+			if data[i] >= '0' && data[i] <= '9' {
+				continue
+			}
+			break
+		}
+	}
+	// frac
+	if i == len(data) {
+		return i, true
+	}
+	if data[i] == '.' {
+		i++
+		if i == len(data) {
+			return i, false
+		}
+		if data[i] < '0' || data[i] > '9' {
+			return i, false
+		}
+		i++
+		for ; i < len(data); i++ {
+			if data[i] >= '0' && data[i] <= '9' {
+				continue
+			}
+			break
+		}
+	}
+	// exp
+	if i == len(data) {
+		return i, true
+	}
+	if data[i] == 'e' || data[i] == 'E' {
+		i++
+		if i == len(data) {
+			return i, false
+		}
+		if data[i] == '+' || data[i] == '-' {
+			i++
+		}
+		if i == len(data) {
+			return i, false
+		}
+		if data[i] < '0' || data[i] > '9' {
+			return i, false
+		}
+		i++
+		for ; i < len(data); i++ {
+			if data[i] >= '0' && data[i] <= '9' {
+				continue
+			}
+			break
+		}
+	}
+	return i, true
+}
+
+func validtrue(data []byte, i int) (outi int, ok bool) {
+	if i+3 <= len(data) && data[i] == 'r' && data[i+1] == 'u' &&
+		data[i+2] == 'e' {
+		return i + 3, true
+	}
+	return i, false
+}
+func validfalse(data []byte, i int) (outi int, ok bool) {
+	if i+4 <= len(data) && data[i] == 'a' && data[i+1] == 'l' &&
+		data[i+2] == 's' && data[i+3] == 'e' {
+		return i + 4, true
+	}
+	return i, false
+}
+func validnull(data []byte, i int) (outi int, ok bool) {
+	if i+3 <= len(data) && data[i] == 'u' && data[i+1] == 'l' &&
+		data[i+2] == 'l' {
+		return i + 3, true
+	}
+	return i, false
+}
+
+// Valid returns true if the input is valid json.
+//
+//	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
+}
+
+// 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 working with bytes, this method preferred over ValidBytes(string(data))
+func ValidBytes(json []byte) bool {
+	_, ok := validpayload(json, 0)
+	return ok
+}
+
+func parseUint(s string) (n uint64, ok bool) {
+	var i int
+	if i == len(s) {
+		return 0, false
+	}
+	for ; i < len(s); i++ {
+		if s[i] >= '0' && s[i] <= '9' {
+			n = n*10 + uint64(s[i]-'0')
+		} else {
+			return 0, false
+		}
+	}
+	return n, true
+}
+
+func parseInt(s string) (n int64, ok bool) {
+	var i int
+	var sign bool
+	if len(s) > 0 && s[0] == '-' {
+		sign = true
+		i++
+	}
+	if i == len(s) {
+		return 0, false
+	}
+	for ; i < len(s); i++ {
+		if s[i] >= '0' && s[i] <= '9' {
+			n = n*10 + int64(s[i]-'0')
+		} else {
+			return 0, false
+		}
+	}
+	if sign {
+		return n * -1, true
+	}
+	return n, true
+}
+
+// safeInt validates a given JSON number
+// ensures it lies within the minimum and maximum representable JSON numbers
+func safeInt(f float64) (n int64, ok bool) {
+	// https://tc39.es/ecma262/#sec-number.min_safe_integer
+	// https://tc39.es/ecma262/#sec-number.max_safe_integer
+	if f < -9007199254740991 || f > 9007199254740991 {
+		return 0, false
+	}
+	return int64(f), true
+}
+
+// execStatic parses the path to find a static value.
+// The input expects that the path already starts with a '!'
+func execStatic(json, path string) (pathOut, res string, ok bool) {
+	name := path[1:]
+	if len(name) > 0 {
+		switch name[0] {
+		case '{', '[', '"', '+', '-', '0', '1', '2', '3', '4', '5', '6', '7',
+			'8', '9':
+			_, res = parseSquash(name, 0)
+			pathOut = name[len(res):]
+			return pathOut, res, true
+		}
+	}
+	for i := 1; i < len(path); i++ {
+		if path[i] == '|' {
+			pathOut = path[i:]
+			name = path[1:i]
+			break
+		}
+		if path[i] == '.' {
+			pathOut = path[i:]
+			name = path[1:i]
+			break
+		}
+	}
+	switch strings.ToLower(name) {
+	case "true", "false", "null", "nan", "inf":
+		return pathOut, name, true
+	}
+	return pathOut, res, false
+}
+
+// execModifier parses the path to find a matching modifier function.
+// The input expects that the path already starts with a '@'
+func execModifier(json, path string) (pathOut, res string, ok bool) {
+	name := path[1:]
+	var hasArgs bool
+	for i := 1; i < len(path); i++ {
+		if path[i] == ':' {
+			pathOut = path[i+1:]
+			name = path[1:i]
+			hasArgs = len(pathOut) > 0
+			break
+		}
+		if path[i] == '|' {
+			pathOut = path[i:]
+			name = path[1:i]
+			break
+		}
+		if path[i] == '.' {
+			pathOut = path[i:]
+			name = path[1:i]
+			break
+		}
+	}
+	if fn, ok := modifiers[name]; ok {
+		var args string
+		if hasArgs {
+			var parsedArgs bool
+			switch pathOut[0] {
+			case '{', '[', '"':
+				// json arg
+				res := Parse(pathOut)
+				if res.Exists() {
+					args = squash(pathOut)
+					pathOut = pathOut[len(args):]
+					parsedArgs = true
+				}
+			}
+			if !parsedArgs {
+				// 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
+	}
+	return pathOut, res, false
+}
+
+// unwrap removes the '[]' or '{}' characters around json
+func unwrap(json string) string {
+	json = trim(json)
+	if len(json) >= 2 && (json[0] == '[' || json[0] == '{') {
+		json = json[1 : len(json)-1]
+	}
+	return json
+}
+
+// DisableModifiers will disable the modifier syntax
+var DisableModifiers = false
+
+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.
+// This operation is not thread safe and should be executed prior to
+// using all other gjson function.
+func AddModifier(name string, fn func(json, arg string) string) {
+	modifiers[name] = fn
+}
+
+// ModifierExists returns true when the specified modifier exists.
+func ModifierExists(name string, fn func(json, arg string) string) bool {
+	_, ok := modifiers[name]
+	return ok
+}
+
+// cleanWS remove any non-whitespace from string
+func cleanWS(s string) string {
+	for i := 0; i < len(s); i++ {
+		switch s[i] {
+		case ' ', '\t', '\n', '\r':
+			continue
+		default:
+			var s2 []byte
+			for i := 0; i < len(s); i++ {
+				switch s[i] {
+				case ' ', '\t', '\n', '\r':
+					s2 = append(s2, s[i])
+				}
+			}
+			return string(s2)
+		}
+	}
+	return s
+}
+
+// @pretty modifier makes the json look nice.
+func modPretty(json, arg string) string {
+	if len(arg) > 0 {
+		opts := *pretty.DefaultOptions
+		Parse(arg).ForEach(func(key, value Result) bool {
+			switch key.String() {
+			case "sortKeys":
+				opts.SortKeys = value.Bool()
+			case "indent":
+				opts.Indent = cleanWS(value.String())
+			case "prefix":
+				opts.Prefix = cleanWS(value.String())
+			case "width":
+				opts.Width = int(value.Int())
+			}
+			return true
+		})
+		return bytesString(pretty.PrettyOptions(stringBytes(json), &opts))
+	}
+	return bytesString(pretty.Pretty(stringBytes(json)))
+}
+
+// @this returns the current element. Can be used to retrieve the root element.
+func modThis(json, arg string) string {
+	return json
+}
+
+// @ugly modifier removes all whitespace.
+func modUgly(json, arg string) string {
+	return bytesString(pretty.Ugly(stringBytes(json)))
+}
+
+// @reverse reverses array elements or root object members.
+func modReverse(json, arg string) string {
+	res := Parse(json)
+	if res.IsArray() {
+		var values []Result
+		res.ForEach(func(_, value Result) bool {
+			values = append(values, value)
+			return true
+		})
+		out := make([]byte, 0, len(json))
+		out = append(out, '[')
+		for i, j := len(values)-1, 0; i >= 0; i, j = i-1, j+1 {
+			if j > 0 {
+				out = append(out, ',')
+			}
+			out = append(out, values[i].Raw...)
+		}
+		out = append(out, ']')
+		return bytesString(out)
+	}
+	if res.IsObject() {
+		var keyValues []Result
+		res.ForEach(func(key, value Result) bool {
+			keyValues = append(keyValues, key, value)
+			return true
+		})
+		out := make([]byte, 0, len(json))
+		out = append(out, '{')
+		for i, j := len(keyValues)-2, 0; i >= 0; i, j = i-2, j+1 {
+			if j > 0 {
+				out = append(out, ',')
+			}
+			out = append(out, keyValues[i+0].Raw...)
+			out = append(out, ':')
+			out = append(out, keyValues[i+1].Raw...)
+		}
+		out = append(out, '}')
+		return bytesString(out)
+	}
+	return json
+}
+
+// @flatten an array with child arrays.
+//
+//	[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]
+//
+// The original json is returned when the json is not an array.
+func modFlatten(json, arg string) string {
+	res := Parse(json)
+	if !res.IsArray() {
+		return json
+	}
+	var deep bool
+	if arg != "" {
+		Parse(arg).ForEach(func(key, value Result) bool {
+			if key.String() == "deep" {
+				deep = value.Bool()
+			}
+			return true
+		})
+	}
+	var out []byte
+	out = append(out, '[')
+	var idx int
+	res.ForEach(func(_, value Result) bool {
+		var raw string
+		if value.IsArray() {
+			if deep {
+				raw = unwrap(modFlatten(value.Raw, arg))
+			} else {
+				raw = unwrap(value.Raw)
+			}
+		} else {
+			raw = value.Raw
+		}
+		raw = strings.TrimSpace(raw)
+		if len(raw) > 0 {
+			if idx > 0 {
+				out = append(out, ',')
+			}
+			out = append(out, raw...)
+			idx++
+		}
+		return true
+	})
+	out = append(out, ']')
+	return bytesString(out)
+}
+
+// @keys extracts the keys from an object.
+//
+//	{"first":"Tom","last":"Smith"} -> ["first","last"]
+func modKeys(json, arg string) string {
+	v := Parse(json)
+	if !v.Exists() {
+		return "[]"
+	}
+	obj := v.IsObject()
+	var out strings.Builder
+	out.WriteByte('[')
+	var i int
+	v.ForEach(func(key, _ Result) bool {
+		if i > 0 {
+			out.WriteByte(',')
+		}
+		if obj {
+			out.WriteString(key.Raw)
+		} else {
+			out.WriteString("null")
+		}
+		i++
+		return true
+	})
+	out.WriteByte(']')
+	return out.String()
+}
+
+// @values extracts the values from an object.
+//
+//	{"first":"Tom","last":"Smith"} -> ["Tom","Smith"]
+func modValues(json, arg string) string {
+	v := Parse(json)
+	if !v.Exists() {
+		return "[]"
+	}
+	if v.IsArray() {
+		return json
+	}
+	var out strings.Builder
+	out.WriteByte('[')
+	var i int
+	v.ForEach(func(_, value Result) bool {
+		if i > 0 {
+			out.WriteByte(',')
+		}
+		out.WriteString(value.Raw)
+		i++
+		return true
+	})
+	out.WriteByte(']')
+	return out.String()
+}
+
+// @join multiple objects into a single object.
+//
+//	[{"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}
+//
+// Without preserved keys:
+//
+//	[{"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)
+	if !res.IsArray() {
+		return json
+	}
+	var preserve bool
+	if arg != "" {
+		Parse(arg).ForEach(func(key, value Result) bool {
+			if key.String() == "preserve" {
+				preserve = value.Bool()
+			}
+			return true
+		})
+	}
+	var out []byte
+	out = append(out, '{')
+	if preserve {
+		// Preserve duplicate keys.
+		var idx int
+		res.ForEach(func(_, value Result) bool {
+			if !value.IsObject() {
+				return true
+			}
+			if idx > 0 {
+				out = append(out, ',')
+			}
+			out = append(out, unwrap(value.Raw)...)
+			idx++
+			return true
+		})
+	} else {
+		// Deduplicate keys and generate an object with stable ordering.
+		var keys []Result
+		kvals := make(map[string]Result)
+		res.ForEach(func(_, value Result) bool {
+			if !value.IsObject() {
+				return true
+			}
+			value.ForEach(func(key, value Result) bool {
+				k := key.String()
+				if _, ok := kvals[k]; !ok {
+					keys = append(keys, key)
+				}
+				kvals[k] = value
+				return true
+			})
+			return true
+		})
+		for i := 0; i < len(keys); i++ {
+			if i > 0 {
+				out = append(out, ',')
+			}
+			out = append(out, keys[i].Raw...)
+			out = append(out, ':')
+			out = append(out, kvals[keys[i].String()].Raw...)
+		}
+	}
+	out = append(out, '}')
+	return bytesString(out)
+}
+
+// @valid ensures that the json is valid before moving on. An empty string is
+// returned when the json is not valid, otherwise it returns the original json.
+func modValid(json, arg string) string {
+	if !Valid(json) {
+		return ""
+	}
+	return json
+}
+
+// @fromstr converts a string to json
+//
+//	"{\"id\":1023,\"name\":\"alert\"}" -> {"id":1023,"name":"alert"}
+func modFromStr(json, arg string) string {
+	if !Valid(json) {
+		return ""
+	}
+	return Parse(json).String()
+}
+
+// @tostr converts a string to json
+//
+//	{"id":1023,"name":"alert"} -> "{\"id\":1023,\"name\":\"alert\"}"
+func modToStr(str, arg string) string {
+	return string(AppendJSONString(nil, str))
+}
+
+func modGroup(json, arg string) string {
+	res := Parse(json)
+	if !res.IsObject() {
+		return ""
+	}
+	var all [][]byte
+	res.ForEach(func(key, value Result) bool {
+		if !value.IsArray() {
+			return true
+		}
+		var idx int
+		value.ForEach(func(_, value Result) bool {
+			if idx == len(all) {
+				all = append(all, []byte{})
+			}
+			all[idx] = append(all[idx], ("," + key.Raw + ":" + value.Raw)...)
+			idx++
+			return true
+		})
+		return true
+	})
+	var data []byte
+	data = append(data, '[')
+	for i, item := range all {
+		if i > 0 {
+			data = append(data, ',')
+		}
+		data = append(data, '{')
+		data = append(data, item[1:]...)
+		data = append(data, '}')
+	}
+	data = append(data, ']')
+	return string(data)
+}
+
+// stringHeader instead of reflect.StringHeader
+type stringHeader struct {
+	data unsafe.Pointer
+	len  int
+}
+
+// sliceHeader instead of reflect.SliceHeader
+type sliceHeader struct {
+	data unsafe.Pointer
+	len  int
+	cap  int
+}
+
+// getBytes casts the input json bytes to a string and safely returns the
+// results as uniquely allocated data. This operation is intended to minimize
+// copies and allocations for the large json string->[]byte.
+func getBytes(json []byte, path string) Result {
+	var result Result
+	if json != nil {
+		// unsafe cast to string
+		result = Get(*(*string)(unsafe.Pointer(&json)), path)
+		// safely get the string headers
+		rawhi := *(*stringHeader)(unsafe.Pointer(&result.Raw))
+		strhi := *(*stringHeader)(unsafe.Pointer(&result.Str))
+		// create byte slice headers
+		rawh := sliceHeader{data: rawhi.data, len: rawhi.len, cap: rawhi.len}
+		strh := sliceHeader{data: strhi.data, len: strhi.len, cap: rawhi.len}
+		if strh.data == nil {
+			// str is nil
+			if rawh.data == nil {
+				// raw is nil
+				result.Raw = ""
+			} else {
+				// raw has data, safely copy the slice header to a string
+				result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
+			}
+			result.Str = ""
+		} else if rawh.data == nil {
+			// raw is nil
+			result.Raw = ""
+			// str has data, safely copy the slice header to a string
+			result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
+		} else if uintptr(strh.data) >= uintptr(rawh.data) &&
+			uintptr(strh.data)+uintptr(strh.len) <=
+				uintptr(rawh.data)+uintptr(rawh.len) {
+			// Str is a substring of Raw.
+			start := uintptr(strh.data) - uintptr(rawh.data)
+			// safely copy the raw slice header
+			result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
+			// substring the raw
+			result.Str = result.Raw[start : start+uintptr(strh.len)]
+		} else {
+			// safely copy both the raw and str slice headers to strings
+			result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
+			result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
+		}
+	}
+	return result
+}
+
+// fillIndex finds the position of Raw data and assigns it to the Index field
+// of the resulting value. If the position cannot be found then Index zero is
+// used instead.
+func fillIndex(json string, c *parseContext) {
+	if len(c.value.Raw) > 0 && !c.calcd {
+		jhdr := *(*stringHeader)(unsafe.Pointer(&json))
+		rhdr := *(*stringHeader)(unsafe.Pointer(&(c.value.Raw)))
+		c.value.Index = int(uintptr(rhdr.data) - uintptr(jhdr.data))
+		if c.value.Index < 0 || c.value.Index >= len(json) {
+			c.value.Index = 0
+		}
+	}
+}
+
+func stringBytes(s string) []byte {
+	return *(*[]byte)(unsafe.Pointer(&sliceHeader{
+		data: (*stringHeader)(unsafe.Pointer(&s)).data,
+		len:  len(s),
+		cap:  len(s),
+	}))
+}
+
+func bytesString(b []byte) string {
+	return *(*string)(unsafe.Pointer(&b))
+}
+
+func revSquash(json string) string {
+	// reverse squash
+	// expects that the tail character is a ']' or '}' or ')' or '"'
+	// squash the value, ignoring all nested arrays and objects.
+	i := len(json) - 1
+	var depth int
+	if json[i] != '"' {
+		depth++
+	}
+	if json[i] == '}' || json[i] == ']' || json[i] == ')' {
+		i--
+	}
+	for ; i >= 0; i-- {
+		switch json[i] {
+		case '"':
+			i--
+			for ; i >= 0; i-- {
+				if json[i] == '"' {
+					esc := 0
+					for i > 0 && json[i-1] == '\\' {
+						i--
+						esc++
+					}
+					if esc%2 == 1 {
+						continue
+					}
+					i += esc
+					break
+				}
+			}
+			if depth == 0 {
+				if i < 0 {
+					i = 0
+				}
+				return json[i:]
+			}
+		case '}', ']', ')':
+			depth++
+		case '{', '[', '(':
+			depth--
+			if depth == 0 {
+				return json[i:]
+			}
+		}
+	}
+	return json
+}
+
+// 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")
+//
+// The returned value will be in the form of a JSON array:
+//
+//	["friends.0.first","friends.1.first","friends.2.first"]
+//
+// The param 'json' must be the original JSON used when calling Get.
+//
+// Returns an empty string if the paths cannot be determined, which can happen
+// when the Result came from a path that contained a multipath, modifier,
+// or a nested query.
+func (t Result) Paths(json string) []string {
+	if t.Indexes == nil {
+		return nil
+	}
+	paths := make([]string, 0, len(t.Indexes))
+	t.ForEach(func(_, value Result) bool {
+		paths = append(paths, value.Path(json))
+		return true
+	})
+	if len(paths) != len(t.Indexes) {
+		return nil
+	}
+	return paths
+}
+
+// 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)")
+//
+// The returned value will be in the form of a JSON string:
+//
+//	"friends.0"
+//
+// The param 'json' must be the original JSON used when calling Get.
+//
+// Returns an empty string if the paths cannot be determined, which can happen
+// when the Result came from a path that contained a multipath, modifier,
+// or a nested query.
+func (t Result) Path(json string) string {
+	var path []byte
+	var comps []string // raw components
+	i := t.Index - 1
+	if t.Index+len(t.Raw) > len(json) {
+		// JSON cannot safely contain Result.
+		goto fail
+	}
+	if !strings.HasPrefix(json[t.Index:], t.Raw) {
+		// Result is not at the JSON index as expected.
+		goto fail
+	}
+	for ; i >= 0; i-- {
+		if json[i] <= ' ' {
+			continue
+		}
+		if json[i] == ':' {
+			// inside of object, get the key
+			for ; i >= 0; i-- {
+				if json[i] != '"' {
+					continue
+				}
+				break
+			}
+			raw := revSquash(json[:i+1])
+			i = i - len(raw)
+			comps = append(comps, raw)
+			// key gotten, now squash the rest
+			raw = revSquash(json[:i+1])
+			i = i - len(raw)
+			i++ // increment the index for next loop step
+		} else if json[i] == '{' {
+			// Encountered an open object. The original result was probably an
+			// object key.
+			goto fail
+		} else if json[i] == ',' || json[i] == '[' {
+			// inside of an array, count the position
+			var arrIdx int
+			if json[i] == ',' {
+				arrIdx++
+				i--
+			}
+			for ; i >= 0; i-- {
+				if json[i] == ':' {
+					// Encountered an unexpected colon. The original result was
+					// probably an object key.
+					goto fail
+				} else if json[i] == ',' {
+					arrIdx++
+				} else if json[i] == '[' {
+					comps = append(comps, strconv.Itoa(arrIdx))
+					break
+				} else if json[i] == ']' || json[i] == '}' || json[i] == '"' {
+					raw := revSquash(json[:i+1])
+					i = i - len(raw) + 1
+				}
+			}
+		}
+	}
+	if len(comps) == 0 {
+		if DisableModifiers {
+			goto fail
+		}
+		return "@this"
+	}
+	for i := len(comps) - 1; i >= 0; i-- {
+		rcomp := Parse(comps[i])
+		if !rcomp.Exists() {
+			goto fail
+		}
+		comp := Escape(rcomp.String())
+		path = append(path, '.')
+		path = append(path, comp...)
+	}
+	if len(path) > 0 {
+		path = path[1:]
+	}
+	return string(path)
+fail:
+	return ""
+}
+
+// isSafePathKeyChar returns true if the input character is safe for not
+// needing escaping.
+func isSafePathKeyChar(c byte) bool {
+	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+		(c >= '0' && c <= '9') || c <= ' ' || c > '~' || c == '_' ||
+		c == '-' || c == ':'
+}
+
+// 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 := make([]byte, len(comp)+1)
+			copy(ncomp, comp[:i])
+			ncomp = ncomp[:i]
+			for ; i < len(comp); i++ {
+				if !isSafePathKeyChar(comp[i]) {
+					ncomp = append(ncomp, '\\')
+				}
+				ncomp = append(ncomp, comp[i])
+			}
+			return string(ncomp)
+		}
+	}
+	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)
+}