[
  {
    "path": ".github/workflows/go.yml",
    "content": "name: Go\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    steps:\n\n    - name: Set up Go 1.x\n      uses: actions/setup-go@v2\n      with:\n        go-version: ^1.13\n\n    - name: Check out code into the Go module directory\n      uses: actions/checkout@v2\n\n    - name: Get dependencies\n      run: |\n        go get -v -t -d ./...\n        if [ -f Gopkg.toml ]; then\n            curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh\n            dep ensure\n        fi\n\n    - name: Build\n      run: go build -v .\n\n    - name: Test\n      run: go test -v .\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Josh Baker\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"/.github/images/logo-dark.png\">\n  <source media=\"(prefers-color-scheme: light)\" srcset=\"/.github/images/logo-light.png\">\n  <img src=\"/.github/images/logo-light.png\" width=\"240\" alt=\"GJSON\" >\n</picture>\n<br>\n<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>\n<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>\n<a href=\"SYNTAX.md\"><img src=\"https://img.shields.io/badge/{}-syntax-33aa33.svg?style=flat-square\" alt=\"GJSON Syntax\"></a>\n\t\n</p>\n\n<p align=\"center\">get json values quickly</a></p>\n\nGJSON is a Go package that provides a [fast](#performance) and [simple](#get-a-value) way to get values from a json document.\nIt 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).\n\nAlso check out [SJSON](https://github.com/tidwall/sjson) for modifying json, and the [JJ](https://github.com/tidwall/jj) command line tool.\n\nThis README is a quick overview of how to use GJSON, for more information check out [GJSON Syntax](SYNTAX.md).\n\nGJSON is also available for [Python](https://github.com/volans-/gjson-py) and [Rust](https://github.com/tidwall/gjson.rs)\n\nGetting Started\n===============\n\n## Installing\n\nTo start using GJSON, install Go and run `go get`:\n\n```sh\n$ go get -u github.com/tidwall/gjson\n```\n\nThis will retrieve the library.\n\n## Get a value\nGet 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. \n\n```go\npackage main\n\nimport \"github.com/tidwall/gjson\"\n\nconst json = `{\"name\":{\"first\":\"Janet\",\"last\":\"Prichard\"},\"age\":47}`\n\nfunc main() {\n\tvalue := gjson.Get(json, \"name.last\")\n\tprintln(value.String())\n}\n```\n\nThis will print:\n\n```\nPrichard\n```\n*There's also [GetBytes](#working-with-bytes) for working with JSON byte slices.*\n\n## Path Syntax\n\nBelow is a quick overview of the path syntax, for more complete information please\ncheck out [GJSON Syntax](SYNTAX.md).\n\nA path is a series of keys separated by a dot.\nA key may contain special wildcard characters '\\*' and '?'.\nTo access an array value use the index as the key.\nTo get the number of elements in an array or to access a child path, use the '#' character.\nThe dot and wildcard characters can be escaped with '\\\\'.\n\n```json\n{\n  \"name\": {\"first\": \"Tom\", \"last\": \"Anderson\"},\n  \"age\":37,\n  \"children\": [\"Sara\",\"Alex\",\"Jack\"],\n  \"fav.movie\": \"Deer Hunter\",\n  \"friends\": [\n    {\"first\": \"Dale\", \"last\": \"Murphy\", \"age\": 44, \"nets\": [\"ig\", \"fb\", \"tw\"]},\n    {\"first\": \"Roger\", \"last\": \"Craig\", \"age\": 68, \"nets\": [\"fb\", \"tw\"]},\n    {\"first\": \"Jane\", \"last\": \"Murphy\", \"age\": 47, \"nets\": [\"ig\", \"tw\"]}\n  ]\n}\n```\n```\n\"name.last\"          >> \"Anderson\"\n\"age\"                >> 37\n\"children\"           >> [\"Sara\",\"Alex\",\"Jack\"]\n\"children.#\"         >> 3\n\"children.1\"         >> \"Alex\"\n\"child*.2\"           >> \"Jack\"\n\"c?ildren.0\"         >> \"Sara\"\n\"fav\\.movie\"         >> \"Deer Hunter\"\n\"friends.#.first\"    >> [\"Dale\",\"Roger\",\"Jane\"]\n\"friends.1.last\"     >> \"Craig\"\n```\n\nYou can also query an array for the first match by using `#(...)`, or find all \nmatches with `#(...)#`. Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` \ncomparison operators and the simple pattern matching `%` (like) and `!%` \n(not like) operators.\n\n```\nfriends.#(last==\"Murphy\").first    >> \"Dale\"\nfriends.#(last==\"Murphy\")#.first   >> [\"Dale\",\"Jane\"]\nfriends.#(age>45)#.last            >> [\"Craig\",\"Murphy\"]\nfriends.#(first%\"D*\").last         >> \"Murphy\"\nfriends.#(first!%\"D*\").last        >> \"Craig\"\nfriends.#(nets.#(==\"fb\"))#.first   >> [\"Dale\",\"Roger\"]\n```\n\n*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was\nchanged in v1.3.0 as to avoid confusion with the new\n[multipath](SYNTAX.md#multipaths) syntax. For backwards compatibility, \n`#[...]` will continue to work until the next major release.*\n\n## Result Type\n\nGJSON supports the json types `string`, `number`, `bool`, and `null`. \nArrays and Objects are returned as their raw json types. \n\nThe `Result` type holds one of these:\n\n```\nbool, for JSON booleans\nfloat64, for JSON numbers\nstring, for JSON string literals\nnil, for JSON null\n```\n\nTo directly access the value:\n\n```go\nresult.Type           // can be String, Number, True, False, Null, or JSON\nresult.Str            // holds the string\nresult.Num            // holds the float64 number\nresult.Raw            // holds the raw json\nresult.Index          // index of raw value in original json, zero means index unknown\nresult.Indexes        // indexes of all the elements that match on a path containing the '#' query character.\n```\n\nThere are a variety of handy functions that work on a result:\n\n```go\nresult.Exists() bool\nresult.Value() interface{}\nresult.Int() int64\nresult.Uint() uint64\nresult.Float() float64\nresult.String() string\nresult.Bool() bool\nresult.Time() time.Time\nresult.Array() []gjson.Result\nresult.Map() map[string]gjson.Result\nresult.Get(path string) Result\nresult.ForEach(iterator func(key, value Result) bool)\nresult.Less(token Result, caseSensitive bool) bool\n```\n\nThe `result.Value()` function returns an `interface{}` which requires type assertion and is one of the following Go types:\n\n```go\nboolean >> bool\nnumber  >> float64\nstring  >> string\nnull    >> nil\narray   >> []interface{}\nobject  >> map[string]interface{}\n```\n\nThe `result.Array()` function returns back an array of values.\nIf the result represents a non-existent value, then an empty array will be returned.\nIf the result is not a JSON array, the return value will be an array containing one result.\n\n### 64-bit integers\n\nThe `result.Int()` and `result.Uint()` calls are capable of reading all 64 bits, allowing for large JSON integers.\n\n```go\nresult.Int() int64    // -9223372036854775808 to 9223372036854775807\nresult.Uint() uint64   // 0 to 18446744073709551615\n```\n\n## Modifiers and path chaining \n\nNew in version 1.2 is support for modifier functions and path chaining.\n\nA modifier is a path component that performs custom processing on the \njson.\n\nMultiple paths can be \"chained\" together using the pipe character. \nThis is useful for getting results from a modified query.\n\nFor example, using the built-in `@reverse` modifier on the above json document,\nwe'll get `children` array and reverse the order:\n\n```\n\"children|@reverse\"           >> [\"Jack\",\"Alex\",\"Sara\"]\n\"children|@reverse|0\"         >> \"Jack\"\n```\n\nThere are currently the following built-in modifiers:\n\n- `@reverse`: Reverse an array or the members of an object.\n- `@ugly`: Remove all whitespace from a json document.\n- `@pretty`: Make the json document more human readable.\n- `@this`: Returns the current element. It can be used to retrieve the root element.\n- `@valid`: Ensure the json document is valid.\n- `@flatten`: Flattens an array.\n- `@join`: Joins multiple objects into a single object.\n- `@keys`: Returns an array of keys for an object.\n- `@values`: Returns an array of values for an object.\n- `@tostr`: Converts json to a string. Wraps a json string.\n- `@fromstr`: Converts a string from json. Unwraps a json string.\n- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db).\n- `@dig`: Search for a value without providing its entire path. See [e8e87f2](https://github.com/tidwall/gjson/commit/e8e87f2a00dc41f3aba5631094e21f59a8cf8cbf).\n\n### Modifier arguments\n\nA modifier may accept an optional argument. The argument can be a valid JSON \ndocument or just characters.\n\nFor example, the `@pretty` modifier takes a json object as its argument. \n\n```\n@pretty:{\"sortKeys\":true} \n```\n\nWhich makes the json pretty and orders all of its keys.\n\n```json\n{\n  \"age\":37,\n  \"children\": [\"Sara\",\"Alex\",\"Jack\"],\n  \"fav.movie\": \"Deer Hunter\",\n  \"friends\": [\n    {\"age\": 44, \"first\": \"Dale\", \"last\": \"Murphy\"},\n    {\"age\": 68, \"first\": \"Roger\", \"last\": \"Craig\"},\n    {\"age\": 47, \"first\": \"Jane\", \"last\": \"Murphy\"}\n  ],\n  \"name\": {\"first\": \"Tom\", \"last\": \"Anderson\"}\n}\n```\n\n*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`. \nPlease see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.*\n\n### Custom modifiers\n\nYou can also add custom modifiers.\n\nFor example, here we create a modifier that makes the entire json document upper\nor lower case.\n\n```go\ngjson.AddModifier(\"case\", func(json, arg string) string {\n  if arg == \"upper\" {\n    return strings.ToUpper(json)\n  }\n  if arg == \"lower\" {\n    return strings.ToLower(json)\n  }\n  return json\n})\n```\n\n```\n\"children|@case:upper\"           >> [\"SARA\",\"ALEX\",\"JACK\"]\n\"children|@case:lower|@reverse\"  >> [\"jack\",\"alex\",\"sara\"]\n```\n\n## JSON Lines\n\nThere's support for [JSON Lines](http://jsonlines.org/) using the `..` prefix, which treats a multilined document as an array. \n\nFor example:\n\n```\n{\"name\": \"Gilbert\", \"age\": 61}\n{\"name\": \"Alexa\", \"age\": 34}\n{\"name\": \"May\", \"age\": 57}\n{\"name\": \"Deloise\", \"age\": 44}\n```\n\n```\n..#                   >> 4\n..1                   >> {\"name\": \"Alexa\", \"age\": 34}\n..3                   >> {\"name\": \"Deloise\", \"age\": 44}\n..#.name              >> [\"Gilbert\",\"Alexa\",\"May\",\"Deloise\"]\n..#(name=\"May\").age   >> 57\n```\n\nThe `ForEachLines` function will iterate through JSON lines.\n\n```go\ngjson.ForEachLine(json, func(line gjson.Result) bool{\n    println(line.String())\n    return true\n})\n```\n\n## Get nested array values\n\nSuppose you want all the last names from the following json:\n\n```json\n{\n  \"programmers\": [\n    {\n      \"firstName\": \"Janet\", \n      \"lastName\": \"McLaughlin\", \n    }, {\n      \"firstName\": \"Elliotte\", \n      \"lastName\": \"Hunter\", \n    }, {\n      \"firstName\": \"Jason\", \n      \"lastName\": \"Harold\", \n    }\n  ]\n}\n```\n\nYou would use the path \"programmers.#.lastName\" like such:\n\n```go\nresult := gjson.Get(json, \"programmers.#.lastName\")\nfor _, name := range result.Array() {\n\tprintln(name.String())\n}\n```\n\nYou can also query an object inside an array:\n\n```go\nname := gjson.Get(json, `programmers.#(lastName=\"Hunter\").firstName`)\nprintln(name.String())  // prints \"Elliotte\"\n```\n\n## Iterate through an object or array\n\nThe `ForEach` function allows for quickly iterating through an object or array. \nThe key and value are passed to the iterator function for objects.\nOnly the value is passed for arrays.\nReturning `false` from an iterator will stop iteration.\n\n```go\nresult := gjson.Get(json, \"programmers\")\nresult.ForEach(func(key, value gjson.Result) bool {\n\tprintln(value.String()) \n\treturn true // keep iterating\n})\n```\n\n## Simple Parse and Get\n\nThere's a `Parse(json)` function that will do a simple parse, and `result.Get(path)` that will search a result.\n\nFor example, all of these will return the same result:\n\n```go\ngjson.Parse(json).Get(\"name\").Get(\"last\")\ngjson.Get(json, \"name\").Get(\"last\")\ngjson.Get(json, \"name.last\")\n```\n\n## Check for the existence of a value\n\nSometimes you just want to know if a value exists. \n\n```go\nvalue := gjson.Get(json, \"name.last\")\nif !value.Exists() {\n\tprintln(\"no last name\")\n} else {\n\tprintln(value.String())\n}\n\n// Or as one step\nif gjson.Get(json, \"name.last\").Exists() {\n\tprintln(\"has a last name\")\n}\n```\n\n## Validate JSON\n\nThe `Get*` and `Parse*` functions expects that the json is well-formed. Bad json will not panic, but it may return back unexpected results.\n\nIf you are consuming JSON from an unpredictable source then you may want to validate prior to using GJSON.\n\n```go\nif !gjson.Valid(json) {\n\treturn errors.New(\"invalid json\")\n}\nvalue := gjson.Get(json, \"name.last\")\n```\n\n## Unmarshal to a map\n\nTo unmarshal to a `map[string]interface{}`:\n\n```go\nm, ok := gjson.Parse(json).Value().(map[string]interface{})\nif !ok {\n\t// not a map\n}\n```\n\n## Working with Bytes\n\nIf 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)`.\n\n```go\nvar json []byte = ...\nresult := gjson.GetBytes(json, path)\n```\n\nIf 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:\n\n```go\nvar json []byte = ...\nresult := gjson.GetBytes(json, path)\nvar raw []byte\nif result.Index > 0 {\n    raw = json[result.Index:result.Index+len(result.Raw)]\n} else {\n    raw = []byte(result.Raw)\n}\n```\n\nThis 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`.\n\n## Performance\n\nBenchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/), \n[ffjson](https://github.com/pquerna/ffjson), \n[EasyJSON](https://github.com/mailru/easyjson),\n[jsonparser](https://github.com/buger/jsonparser),\nand [json-iterator](https://github.com/json-iterator/go)\n\n```\nBenchmarkGJSONGet-10             17893731    202.1 ns/op      0 B/op     0 allocs/op\nBenchmarkGJSONUnmarshalMap-10     1663548   2157 ns/op     1920 B/op    26 allocs/op\nBenchmarkJSONUnmarshalMap-10       832236   4279 ns/op     2920 B/op    68 allocs/op\nBenchmarkJSONUnmarshalStruct-10   1076475   3219 ns/op      920 B/op    12 allocs/op\nBenchmarkJSONDecoder-10            585729   6126 ns/op     3845 B/op   160 allocs/op\nBenchmarkFFJSONLexer-10           2508573   1391 ns/op      880 B/op     8 allocs/op\nBenchmarkEasyJSONLexer-10         3000000    537.9 ns/op    501 B/op     5 allocs/op\nBenchmarkJSONParserGet-10        13707510    263.9 ns/op     21 B/op     0 allocs/op\nBenchmarkJSONIterator-10          3000000    561.2 ns/op    693 B/op    14 allocs/op\n```\n\nJSON document used:\n\n```json\n{\n  \"widget\": {\n    \"debug\": \"on\",\n    \"window\": {\n      \"title\": \"Sample Konfabulator Widget\",\n      \"name\": \"main_window\",\n      \"width\": 500,\n      \"height\": 500\n    },\n    \"image\": { \n      \"src\": \"Images/Sun.png\",\n      \"hOffset\": 250,\n      \"vOffset\": 250,\n      \"alignment\": \"center\"\n    },\n    \"text\": {\n      \"data\": \"Click Here\",\n      \"size\": 36,\n      \"style\": \"bold\",\n      \"vOffset\": 100,\n      \"alignment\": \"center\",\n      \"onMouseUp\": \"sun1.opacity = (sun1.opacity / 100) * 90;\"\n    }\n  }\n}    \n```\n\nEach operation was rotated through one of the following search paths:\n\n```\nwidget.window.name\nwidget.image.hOffset\nwidget.text.onMouseUp\n```\n\n**\n\n*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).*\n"
  },
  {
    "path": "SYNTAX.md",
    "content": "# GJSON Path Syntax\n\nA GJSON Path is a text string syntax that describes a search pattern for quickly retrieving values from a JSON payload.\n\nThis document is designed to explain the structure of a GJSON Path through examples.\n\n- [Path structure](#path-structure)\n- [Basic](#basic)\n- [Wildcards](#wildcards)\n- [Escape Character](#escape-character)\n- [Arrays](#arrays)\n- [Queries](#queries)\n- [Dot vs Pipe](#dot-vs-pipe)\n- [Modifiers](#modifiers)\n- [Multipaths](#multipaths)\n- [Literals](#literals)\n\nThe definitive implementation is [github.com/tidwall/gjson](https://github.com/tidwall/gjson).  \nUse the [GJSON Playground](https://gjson.dev) to experiment with the syntax online.\n\n## Path structure\n\nA GJSON Path is intended to be easily expressed as a series of components separated by a `.` character. \n\nAlong with `.` character, there are a few more that have special meaning, including `|`, `#`, `@`, `\\`, `*`, `!`, and `?`.\n\n## Example\n\nGiven this JSON\n\n```json\n{\n  \"name\": {\"first\": \"Tom\", \"last\": \"Anderson\"},\n  \"age\":37,\n  \"children\": [\"Sara\",\"Alex\",\"Jack\"],\n  \"fav.movie\": \"Deer Hunter\",\n  \"friends\": [\n    {\"first\": \"Dale\", \"last\": \"Murphy\", \"age\": 44, \"nets\": [\"ig\", \"fb\", \"tw\"]},\n    {\"first\": \"Roger\", \"last\": \"Craig\", \"age\": 68, \"nets\": [\"fb\", \"tw\"]},\n    {\"first\": \"Jane\", \"last\": \"Murphy\", \"age\": 47, \"nets\": [\"ig\", \"tw\"]}\n  ]\n}\n```\n\nThe following GJSON Paths evaluate to the accompanying values.\n\n### Basic \n\nIn many cases you'll just want to retrieve values by object name or array index.\n\n```go\nname.last              \"Anderson\"\nname.first             \"Tom\"\nage                    37\nchildren               [\"Sara\",\"Alex\",\"Jack\"]\nchildren.0             \"Sara\"\nchildren.1             \"Alex\"\nfriends.1              {\"first\": \"Roger\", \"last\": \"Craig\", \"age\": 68}\nfriends.1.first        \"Roger\"\n```\n\n### Wildcards\n\nA key may contain the special wildcard characters `*` and `?`. \nThe `*` will match on any zero+ characters, and `?` matches on any one character.\n\n```go\nchild*.2               \"Jack\"\nc?ildren.0             \"Sara\"\n```\n\n### Escape character\n\nSpecial purpose characters, such as `.`, `*`, and `?` can be escaped with `\\`. \n\n```go\nfav\\.movie             \"Deer Hunter\"\n```\n\nYou'll also need to make sure that the `\\` character is correctly escaped when hardcoding a path in your source code.\n\n```go\n// Go\nval := gjson.Get(json, \"fav\\\\.movie\")  // must escape the slash\nval := gjson.Get(json, `fav\\.movie`)   // no need to escape the slash \n```\n\n```rust\n// Rust\nlet val = gjson::get(json, \"fav\\\\.movie\")     // must escape the slash\nlet val = gjson::get(json, r#\"fav\\.movie\"#)   // no need to escape the slash \n```\n\n\n### Arrays\n\nThe `#` character allows for digging into JSON Arrays.\n\nTo get the length of an array you'll just use the `#` all by itself.\n\n```go\nfriends.#              3\nfriends.#.age         [44,68,47]\n```\n\n### Queries\n\nYou can also query an array for the first match by  using `#(...)`, or find all matches with `#(...)#`. \nQueries support the `==`, `!=`, `<`, `<=`, `>`, `>=` comparison operators, \nand the simple pattern matching `%` (like) and `!%` (not like) operators.\n\n```go\nfriends.#(last==\"Murphy\").first     \"Dale\"\nfriends.#(last==\"Murphy\")#.first    [\"Dale\",\"Jane\"]\nfriends.#(age>45)#.last             [\"Craig\",\"Murphy\"]\nfriends.#(first%\"D*\").last          \"Murphy\"\nfriends.#(first!%\"D*\").last         \"Craig\"\n```\n\nTo query for a non-object value in an array, you can forgo the string to the right of the operator.\n\n```go\nchildren.#(!%\"*a*\")                 \"Alex\"\nchildren.#(%\"*a*\")#                 [\"Sara\",\"Jack\"]\n```\n\nNested queries are allowed.\n\n```go\nfriends.#(nets.#(==\"fb\"))#.first  >> [\"Dale\",\"Roger\"]\n```\n\n*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was\nchanged in v1.3.0 as to avoid confusion with the new [multipath](#multipaths) \nsyntax. For backwards compatibility, `#[...]` will continue to work until the\nnext major release.*\n\nThe `~` (tilde) operator will convert a value to a boolean before comparison.\n\nSupported tilde comparison type are:\n\n```\n~true      Converts true-ish values to true\n~false     Converts false-ish and non-existent values to true\n~null      Converts null and non-existent values to true\n~*         Converts any existing value to true\n```\n\nFor example, using the following JSON:\n\n```json\n{\n  \"vals\": [\n    { \"a\": 1, \"b\": \"data\" },\n    { \"a\": 2, \"b\": true },\n    { \"a\": 3, \"b\": false },\n    { \"a\": 4, \"b\": \"0\" },\n    { \"a\": 5, \"b\": 0 },\n    { \"a\": 6, \"b\": \"1\" },\n    { \"a\": 7, \"b\": 1 },\n    { \"a\": 8, \"b\": \"true\" },\n    { \"a\": 9, \"b\": false },\n    { \"a\": 10, \"b\": null },\n    { \"a\": 11 }\n  ]\n}\n```\n\nTo query for all true-ish or false-ish values:\n\n```\nvals.#(b==~true)#.a    >> [2,6,7,8]\nvals.#(b==~false)#.a   >> [3,4,5,9,10,11]\n```\n\nThe last value which was non-existent is treated as `false`\n\nTo query for null and explicit value existence:\n\n```\nvals.#(b==~null)#.a    >> [10,11]\nvals.#(b==~*)#.a       >> [1,2,3,4,5,6,7,8,9,10]\nvals.#(b!=~*)#.a       >> [11]\n```\n\n### Dot vs Pipe\n\nThe `.` is standard separator, but it's also possible to use a `|`. \nIn most cases they both end up returning the same results.\nThe cases where`|` differs from `.` is when it's used after the `#` for [Arrays](#arrays) and [Queries](#queries). \n\nHere are some examples\n\n```go\nfriends.0.first                     \"Dale\"\nfriends|0.first                     \"Dale\"\nfriends.0|first                     \"Dale\"\nfriends|0|first                     \"Dale\"\nfriends|#                           3\nfriends.#                           3\nfriends.#(last=\"Murphy\")#           [{\"first\": \"Dale\", \"last\": \"Murphy\", \"age\": 44},{\"first\": \"Jane\", \"last\": \"Murphy\", \"age\": 47}]\nfriends.#(last=\"Murphy\")#.first     [\"Dale\",\"Jane\"]\nfriends.#(last=\"Murphy\")#|first     <non-existent>\nfriends.#(last=\"Murphy\")#.0         []\nfriends.#(last=\"Murphy\")#|0         {\"first\": \"Dale\", \"last\": \"Murphy\", \"age\": 44}\nfriends.#(last=\"Murphy\")#.#         []\nfriends.#(last=\"Murphy\")#|#         2\n```\n\nLet's break down a few of these.\n\nThe path `friends.#(last=\"Murphy\")#` all by itself results in\n\n```json\n[{\"first\": \"Dale\", \"last\": \"Murphy\", \"age\": 44},{\"first\": \"Jane\", \"last\": \"Murphy\", \"age\": 47}]\n```\n\nThe `.first` suffix will process the `first` path on each array element *before* returning the results. Which becomes\n\n```json\n[\"Dale\",\"Jane\"]\n```\n\nBut the `|first` suffix actually processes the `first` path *after* the previous result. \nSince the previous result is an array, not an object, it's not possible to process \nbecause `first` does not exist.\n\nYet, `|0` suffix returns\n\n```json\n{\"first\": \"Dale\", \"last\": \"Murphy\", \"age\": 44}\n```\n\nBecause `0` is the first index of the previous result.\n\n### Modifiers\n\nA modifier is a path component that performs custom processing on the JSON.\n\nFor example, using the built-in `@reverse` modifier on the above JSON payload will reverse the `children` array:\n\n```go\nchildren.@reverse                   [\"Jack\",\"Alex\",\"Sara\"]\nchildren.@reverse.0                 \"Jack\"\n```\n\nThere are currently the following built-in modifiers:\n\n- `@reverse`: Reverse an array or the members of an object.\n- `@ugly`: Remove all whitespace from JSON.\n- `@pretty`: Make the JSON more human readable.\n- `@this`: Returns the current element. It can be used to retrieve the root element.\n- `@valid`: Ensure the json document is valid.\n- `@flatten`: Flattens an array.\n- `@join`: Joins multiple objects into a single object.\n- `@keys`: Returns an array of keys for an object.\n- `@values`: Returns an array of values for an object.\n- `@tostr`: Converts json to a string. Wraps a json string.\n- `@fromstr`: Converts a string from json. Unwraps a json string.\n- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db).\n- `@dig`: Search for a value without providing its entire path. See [e8e87f2](https://github.com/tidwall/gjson/commit/e8e87f2a00dc41f3aba5631094e21f59a8cf8cbf).\n\n#### Modifier arguments\n\nA modifier may accept an optional argument. The argument can be a valid JSON payload or just characters.\n\nFor example, the `@pretty` modifier takes a json object as its argument.\n\n```\n@pretty:{\"sortKeys\":true}\n```\n\nWhich makes the json pretty and orders all of its keys.\n\n```json\n{\n  \"age\":37,\n  \"children\": [\"Sara\",\"Alex\",\"Jack\"],\n  \"fav.movie\": \"Deer Hunter\",\n  \"friends\": [\n    {\"age\": 44, \"first\": \"Dale\", \"last\": \"Murphy\"},\n    {\"age\": 68, \"first\": \"Roger\", \"last\": \"Craig\"},\n    {\"age\": 47, \"first\": \"Jane\", \"last\": \"Murphy\"}\n  ],\n  \"name\": {\"first\": \"Tom\", \"last\": \"Anderson\"}\n}\n```\n\n*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`. \nPlease see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.*\n\n#### Custom modifiers\n\nYou can also add custom modifiers. \n\nFor example, here we create a modifier which makes the entire JSON payload upper or lower case.\n\n```go\ngjson.AddModifier(\"case\", func(json, arg string) string {\n  if arg == \"upper\" {\n    return strings.ToUpper(json)\n  }\n  if arg == \"lower\" {\n    return strings.ToLower(json)\n  }\n  return json\n})\n\"children.@case:upper\"             [\"SARA\",\"ALEX\",\"JACK\"]\n\"children.@case:lower.@reverse\"    [\"jack\",\"alex\",\"sara\"]\n```\n\n*Note: Custom modifiers are not yet available in the Rust version*\n\n### Multipaths\n\nStarting with v1.3.0, GJSON added the ability to join multiple paths together\nto form new documents. Wrapping comma-separated paths between `[...]` or\n`{...}` will result in a new array or object, respectively.\n\nFor example, using the given multipath:\n\n```\n{name.first,age,\"the_murphys\":friends.#(last=\"Murphy\")#.first}\n```\n\nHere we selected the first name, age, and the first name for friends with the \nlast name \"Murphy\".\n\nYou'll notice that an optional key can be provided, in this case \n\"the_murphys\", to force assign a key to a value. Otherwise, the name of the \nactual field will be used, in this case \"first\". If a name cannot be\ndetermined, then \"_\" is used.\n\nThis results in\n\n```json\n{\"first\":\"Tom\",\"age\":37,\"the_murphys\":[\"Dale\",\"Jane\"]}\n```\n\n### Literals\n\nStarting 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).  \n\nA json literal begins with the '!' declaration character. \n\nFor example, using the given multipath:\n\n```\n{name.first,age,\"company\":!\"Happysoft\",\"employed\":!true}\n```\n\nHere we selected the first name and age. Then add two new fields, \"company\" and \"employed\".\n\nThis results in \n\n```json\n{\"first\":\"Tom\",\"age\":37,\"company\":\"Happysoft\",\"employed\":true}\n```\n\n*See issue [#249](https://github.com/tidwall/gjson/issues/249) for additional context on JSON Literals.*\n"
  },
  {
    "path": "gjson.go",
    "content": "// Copyright 2024 Joshua J Baker. All rights reserved.\n// Use of this source code is governed by an MIT-style\n// license that can be found in the LICENSE file.\n//\n// https://github.com/tidwall/gjson\n\n// Package gjson provides searching for json strings.\npackage gjson\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf16\"\n\t\"unicode/utf8\"\n\t\"unsafe\"\n\n\t\"github.com/tidwall/match\"\n\t\"github.com/tidwall/pretty\"\n)\n\n// Type is Result type\ntype Type int\n\nconst (\n\t// Null is a null json value\n\tNull Type = iota\n\t// False is a json false boolean\n\tFalse\n\t// Number is json number\n\tNumber\n\t// String is a json string\n\tString\n\t// True is a json true boolean\n\tTrue\n\t// JSON is a raw block of JSON\n\tJSON\n)\n\n// String returns a string representation of the type.\nfunc (t Type) String() string {\n\tswitch t {\n\tdefault:\n\t\treturn \"\"\n\tcase Null:\n\t\treturn \"Null\"\n\tcase False:\n\t\treturn \"False\"\n\tcase Number:\n\t\treturn \"Number\"\n\tcase String:\n\t\treturn \"String\"\n\tcase True:\n\t\treturn \"True\"\n\tcase JSON:\n\t\treturn \"JSON\"\n\t}\n}\n\n// Result represents a json value that is returned from Get().\ntype Result struct {\n\t// Type is the json type\n\tType Type\n\t// Raw is the raw json\n\tRaw string\n\t// Str is the json string\n\tStr string\n\t// Num is the json number\n\tNum float64\n\t// Index of raw value in original json, zero means index unknown\n\tIndex int\n\t// Indexes of all the elements that match on a path containing the '#'\n\t// query character.\n\tIndexes []int\n}\n\n// String returns a string representation of the value.\nfunc (t Result) String() string {\n\tswitch t.Type {\n\tdefault:\n\t\treturn \"\"\n\tcase False:\n\t\treturn \"false\"\n\tcase Number:\n\t\tif len(t.Raw) == 0 {\n\t\t\t// calculated result\n\t\t\treturn strconv.FormatFloat(t.Num, 'f', -1, 64)\n\t\t}\n\t\tvar i int\n\t\tif t.Raw[0] == '-' {\n\t\t\ti++\n\t\t}\n\t\tfor ; i < len(t.Raw); i++ {\n\t\t\tif t.Raw[i] < '0' || t.Raw[i] > '9' {\n\t\t\t\treturn strconv.FormatFloat(t.Num, 'f', -1, 64)\n\t\t\t}\n\t\t}\n\t\treturn t.Raw\n\tcase String:\n\t\treturn t.Str\n\tcase JSON:\n\t\treturn t.Raw\n\tcase True:\n\t\treturn \"true\"\n\t}\n}\n\n// Bool returns an boolean representation.\nfunc (t Result) Bool() bool {\n\tswitch t.Type {\n\tdefault:\n\t\treturn false\n\tcase True:\n\t\treturn true\n\tcase String:\n\t\tb, _ := strconv.ParseBool(strings.ToLower(t.Str))\n\t\treturn b\n\tcase Number:\n\t\treturn t.Num != 0\n\t}\n}\n\n// Int returns an integer representation.\nfunc (t Result) Int() int64 {\n\tswitch t.Type {\n\tdefault:\n\t\treturn 0\n\tcase True:\n\t\treturn 1\n\tcase String:\n\t\tn, _ := parseInt(t.Str)\n\t\treturn n\n\tcase Number:\n\t\t// try to directly convert the float64 to int64\n\t\ti, ok := safeInt(t.Num)\n\t\tif ok {\n\t\t\treturn i\n\t\t}\n\t\t// now try to parse the raw string\n\t\ti, ok = parseInt(t.Raw)\n\t\tif ok {\n\t\t\treturn i\n\t\t}\n\t\t// fallback to a standard conversion\n\t\treturn int64(t.Num)\n\t}\n}\n\n// Uint returns an unsigned integer representation.\nfunc (t Result) Uint() uint64 {\n\tswitch t.Type {\n\tdefault:\n\t\treturn 0\n\tcase True:\n\t\treturn 1\n\tcase String:\n\t\tn, _ := parseUint(t.Str)\n\t\treturn n\n\tcase Number:\n\t\t// try to directly convert the float64 to uint64\n\t\ti, ok := safeInt(t.Num)\n\t\tif ok && i >= 0 {\n\t\t\treturn uint64(i)\n\t\t}\n\t\t// now try to parse the raw string\n\t\tu, ok := parseUint(t.Raw)\n\t\tif ok {\n\t\t\treturn u\n\t\t}\n\t\t// fallback to a standard conversion\n\t\treturn uint64(t.Num)\n\t}\n}\n\n// Float returns an float64 representation.\nfunc (t Result) Float() float64 {\n\tswitch t.Type {\n\tdefault:\n\t\treturn 0\n\tcase True:\n\t\treturn 1\n\tcase String:\n\t\tn, _ := strconv.ParseFloat(t.Str, 64)\n\t\treturn n\n\tcase Number:\n\t\treturn t.Num\n\t}\n}\n\n// Time returns a time.Time representation.\nfunc (t Result) Time() time.Time {\n\tres, _ := time.Parse(time.RFC3339, t.String())\n\treturn res\n}\n\n// Array returns back an array of values.\n// If the result represents a null value or is non-existent, then an empty\n// array will be returned.\n// If the result is not a JSON array, the return value will be an\n// array containing one result.\nfunc (t Result) Array() []Result {\n\tif t.Type == Null {\n\t\treturn []Result{}\n\t}\n\tif !t.IsArray() {\n\t\treturn []Result{t}\n\t}\n\tr := t.arrayOrMap('[', false)\n\treturn r.a\n}\n\n// IsObject returns true if the result value is a JSON object.\nfunc (t Result) IsObject() bool {\n\treturn t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '{'\n}\n\n// IsArray returns true if the result value is a JSON array.\nfunc (t Result) IsArray() bool {\n\treturn t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '['\n}\n\n// IsBool returns true if the result value is a JSON boolean.\nfunc (t Result) IsBool() bool {\n\treturn t.Type == True || t.Type == False\n}\n\n// ForEach iterates through values.\n// If the result represents a non-existent value, then no values will be\n// iterated. If the result is an Object, the iterator will pass the key and\n// value of each item. If the result is an Array, the iterator will only pass\n// the value of each item. If the result is not a JSON array or object, the\n// iterator will pass back one value equal to the result.\nfunc (t Result) ForEach(iterator func(key, value Result) bool) {\n\tif !t.Exists() {\n\t\treturn\n\t}\n\tif t.Type != JSON {\n\t\titerator(Result{}, t)\n\t\treturn\n\t}\n\tjson := t.Raw\n\tvar obj bool\n\tvar i int\n\tvar key, value Result\n\tfor ; i < len(json); i++ {\n\t\tif json[i] == '{' {\n\t\t\ti++\n\t\t\tkey.Type = String\n\t\t\tobj = true\n\t\t\tbreak\n\t\t} else if json[i] == '[' {\n\t\t\ti++\n\t\t\tkey.Type = Number\n\t\t\tkey.Num = -1\n\t\t\tbreak\n\t\t}\n\t\tif json[i] > ' ' {\n\t\t\treturn\n\t\t}\n\t}\n\tvar str string\n\tvar vesc bool\n\tvar ok bool\n\tvar idx int\n\tfor ; i < len(json); i++ {\n\t\tif obj {\n\t\t\tif json[i] != '\"' {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ts := i\n\t\t\ti, str, vesc, ok = parseString(json, i+1)\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif vesc {\n\t\t\t\tkey.Str = unescape(str[1 : len(str)-1])\n\t\t\t} else {\n\t\t\t\tkey.Str = str[1 : len(str)-1]\n\t\t\t}\n\t\t\tkey.Raw = str\n\t\t\tkey.Index = s + t.Index\n\t\t} else {\n\t\t\tkey.Num += 1\n\t\t}\n\t\tfor ; i < len(json); i++ {\n\t\t\tif json[i] <= ' ' || json[i] == ',' || json[i] == ':' {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\ts := i\n\t\ti, value, ok = parseAny(json, i, true)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tif t.Indexes != nil {\n\t\t\tif idx < len(t.Indexes) {\n\t\t\t\tvalue.Index = t.Indexes[idx]\n\t\t\t}\n\t\t} else {\n\t\t\tvalue.Index = s + t.Index\n\t\t}\n\t\tif !iterator(key, value) {\n\t\t\treturn\n\t\t}\n\t\tidx++\n\t}\n}\n\n// Map returns back a map of values. The result should be a JSON object.\n// If the result is not a JSON object, the return value will be an empty map.\nfunc (t Result) Map() map[string]Result {\n\tif t.Type != JSON {\n\t\treturn map[string]Result{}\n\t}\n\tr := t.arrayOrMap('{', false)\n\treturn r.o\n}\n\n// Get searches result for the specified path.\n// The result should be a JSON array or object.\nfunc (t Result) Get(path string) Result {\n\tr := Get(t.Raw, path)\n\tif r.Indexes != nil {\n\t\tfor i := 0; i < len(r.Indexes); i++ {\n\t\t\tr.Indexes[i] += t.Index\n\t\t}\n\t} else {\n\t\tr.Index += t.Index\n\t}\n\treturn r\n}\n\ntype arrayOrMapResult struct {\n\ta  []Result\n\tai []interface{}\n\to  map[string]Result\n\toi map[string]interface{}\n\tvc byte\n}\n\nfunc (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) {\n\tvar json = t.Raw\n\tvar i int\n\tvar value Result\n\tvar count int\n\tvar key Result\n\tif vc == 0 {\n\t\tfor ; i < len(json); i++ {\n\t\t\tif json[i] == '{' || json[i] == '[' {\n\t\t\t\tr.vc = json[i]\n\t\t\t\ti++\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif json[i] > ' ' {\n\t\t\t\tgoto end\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor ; i < len(json); i++ {\n\t\t\tif json[i] == vc {\n\t\t\t\ti++\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif json[i] > ' ' {\n\t\t\t\tgoto end\n\t\t\t}\n\t\t}\n\t\tr.vc = vc\n\t}\n\tif r.vc == '{' {\n\t\tif valueize {\n\t\t\tr.oi = make(map[string]interface{})\n\t\t} else {\n\t\t\tr.o = make(map[string]Result)\n\t\t}\n\t} else {\n\t\tif valueize {\n\t\t\tr.ai = make([]interface{}, 0)\n\t\t} else {\n\t\t\tr.a = make([]Result, 0)\n\t\t}\n\t}\n\tfor ; i < len(json); i++ {\n\t\tif json[i] <= ' ' {\n\t\t\tcontinue\n\t\t}\n\t\t// get next value\n\t\tif json[i] == ']' || json[i] == '}' {\n\t\t\tbreak\n\t\t}\n\t\tswitch json[i] {\n\t\tdefault:\n\t\t\tif (json[i] >= '0' && json[i] <= '9') || json[i] == '-' {\n\t\t\t\tvalue.Type = Number\n\t\t\t\tvalue.Raw, value.Num = tonum(json[i:])\n\t\t\t\tvalue.Str = \"\"\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\t\tcase '{', '[':\n\t\t\tvalue.Type = JSON\n\t\t\tvalue.Raw = squash(json[i:])\n\t\t\tvalue.Str, value.Num = \"\", 0\n\t\tcase 'n':\n\t\t\tvalue.Type = Null\n\t\t\tvalue.Raw = tolit(json[i:])\n\t\t\tvalue.Str, value.Num = \"\", 0\n\t\tcase 't':\n\t\t\tvalue.Type = True\n\t\t\tvalue.Raw = tolit(json[i:])\n\t\t\tvalue.Str, value.Num = \"\", 0\n\t\tcase 'f':\n\t\t\tvalue.Type = False\n\t\t\tvalue.Raw = tolit(json[i:])\n\t\t\tvalue.Str, value.Num = \"\", 0\n\t\tcase '\"':\n\t\t\tvalue.Type = String\n\t\t\tvalue.Raw, value.Str = tostr(json[i:])\n\t\t\tvalue.Num = 0\n\t\t}\n\t\tvalue.Index = i + t.Index\n\n\t\ti += len(value.Raw) - 1\n\n\t\tif r.vc == '{' {\n\t\t\tif count%2 == 0 {\n\t\t\t\tkey = value\n\t\t\t} else {\n\t\t\t\tif valueize {\n\t\t\t\t\tif _, ok := r.oi[key.Str]; !ok {\n\t\t\t\t\t\tr.oi[key.Str] = value.Value()\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif _, ok := r.o[key.Str]; !ok {\n\t\t\t\t\t\tr.o[key.Str] = value\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcount++\n\t\t} else {\n\t\t\tif valueize {\n\t\t\t\tr.ai = append(r.ai, value.Value())\n\t\t\t} else {\n\t\t\t\tr.a = append(r.a, value)\n\t\t\t}\n\t\t}\n\t}\nend:\n\tif t.Indexes != nil {\n\t\tif len(t.Indexes) != len(r.a) {\n\t\t\tfor i := 0; i < len(r.a); i++ {\n\t\t\t\tr.a[i].Index = 0\n\t\t\t}\n\t\t} else {\n\t\t\tfor i := 0; i < len(r.a); i++ {\n\t\t\t\tr.a[i].Index = t.Indexes[i]\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// Parse parses the json and returns a result.\n//\n// This function expects that the json is well-formed, and does not validate.\n// Invalid json will not panic, but it may return back unexpected results.\n// If you are consuming JSON from an unpredictable source then you may want to\n// use the Valid function first.\nfunc Parse(json string) Result {\n\tvar value Result\n\ti := 0\n\tfor ; i < len(json); i++ {\n\t\tif json[i] == '{' || json[i] == '[' {\n\t\t\tvalue.Type = JSON\n\t\t\tvalue.Raw = json[i:] // just take the entire raw\n\t\t\tbreak\n\t\t}\n\t\tif json[i] <= ' ' {\n\t\t\tcontinue\n\t\t}\n\t\tswitch json[i] {\n\t\tcase '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n\t\t\t'i', 'I', 'N':\n\t\t\tvalue.Type = Number\n\t\t\tvalue.Raw, value.Num = tonum(json[i:])\n\t\tcase 'n':\n\t\t\tif i+1 < len(json) && json[i+1] != 'u' {\n\t\t\t\t// nan\n\t\t\t\tvalue.Type = Number\n\t\t\t\tvalue.Raw, value.Num = tonum(json[i:])\n\t\t\t} else {\n\t\t\t\t// null\n\t\t\t\tvalue.Type = Null\n\t\t\t\tvalue.Raw = tolit(json[i:])\n\t\t\t}\n\t\tcase 't':\n\t\t\tvalue.Type = True\n\t\t\tvalue.Raw = tolit(json[i:])\n\t\tcase 'f':\n\t\t\tvalue.Type = False\n\t\t\tvalue.Raw = tolit(json[i:])\n\t\tcase '\"':\n\t\t\tvalue.Type = String\n\t\t\tvalue.Raw, value.Str = tostr(json[i:])\n\t\tdefault:\n\t\t\treturn Result{}\n\t\t}\n\t\tbreak\n\t}\n\tif value.Exists() {\n\t\tvalue.Index = i\n\t}\n\treturn value\n}\n\n// ParseBytes parses the json and returns a result.\n// If working with bytes, this method preferred over Parse(string(data))\nfunc ParseBytes(json []byte) Result {\n\treturn Parse(string(json))\n}\n\nfunc squash(json string) string {\n\t// expects that the lead character is a '[' or '{' or '(' or '\"'\n\t// squash the value, ignoring all nested arrays and objects.\n\tvar i, depth int\n\tif json[0] != '\"' {\n\t\ti, depth = 1, 1\n\t}\n\tfor ; i < len(json); i++ {\n\t\tif json[i] >= '\"' && json[i] <= '}' {\n\t\t\tswitch json[i] {\n\t\t\tcase '\"':\n\t\t\t\ti++\n\t\t\t\ts2 := i\n\t\t\t\tfor ; i < len(json); i++ {\n\t\t\t\t\tif json[i] > '\\\\' {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif json[i] == '\"' {\n\t\t\t\t\t\t// look for an escaped slash\n\t\t\t\t\t\tif json[i-1] == '\\\\' {\n\t\t\t\t\t\t\tn := 0\n\t\t\t\t\t\t\tfor j := i - 2; j > s2-1; j-- {\n\t\t\t\t\t\t\t\tif json[j] != '\\\\' {\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tn++\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif n%2 == 0 {\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif depth == 0 {\n\t\t\t\t\tif i >= len(json) {\n\t\t\t\t\t\treturn json\n\t\t\t\t\t}\n\t\t\t\t\treturn json[:i+1]\n\t\t\t\t}\n\t\t\tcase '{', '[', '(':\n\t\t\t\tdepth++\n\t\t\tcase '}', ']', ')':\n\t\t\t\tdepth--\n\t\t\t\tif depth == 0 {\n\t\t\t\t\treturn json[:i+1]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn json\n}\n\nfunc tonum(json string) (raw string, num float64) {\n\tfor i := 1; i < len(json); i++ {\n\t\t// less than dash might have valid characters\n\t\tif json[i] <= '-' {\n\t\t\tif json[i] <= ' ' || json[i] == ',' {\n\t\t\t\t// break on whitespace and comma\n\t\t\t\traw = json[:i]\n\t\t\t\tnum, _ = strconv.ParseFloat(raw, 64)\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// could be a '+' or '-'. let's assume so.\n\t\t} else if json[i] == ']' || json[i] == '}' {\n\t\t\t// break on ']' or '}'\n\t\t\traw = json[:i]\n\t\t\tnum, _ = strconv.ParseFloat(raw, 64)\n\t\t\treturn\n\t\t}\n\t}\n\traw = json\n\tnum, _ = strconv.ParseFloat(raw, 64)\n\treturn\n}\n\nfunc tolit(json string) (raw string) {\n\tfor i := 1; i < len(json); i++ {\n\t\tif json[i] < 'a' || json[i] > 'z' {\n\t\t\treturn json[:i]\n\t\t}\n\t}\n\treturn json\n}\n\nfunc tostr(json string) (raw string, str string) {\n\t// expects that the lead character is a '\"'\n\tfor i := 1; i < len(json); i++ {\n\t\tif json[i] > '\\\\' {\n\t\t\tcontinue\n\t\t}\n\t\tif json[i] == '\"' {\n\t\t\treturn json[:i+1], json[1:i]\n\t\t}\n\t\tif json[i] == '\\\\' {\n\t\t\ti++\n\t\t\tfor ; i < len(json); i++ {\n\t\t\t\tif json[i] > '\\\\' {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif json[i] == '\"' {\n\t\t\t\t\t// look for an escaped slash\n\t\t\t\t\tif json[i-1] == '\\\\' {\n\t\t\t\t\t\tn := 0\n\t\t\t\t\t\tfor j := i - 2; j > 0; j-- {\n\t\t\t\t\t\t\tif json[j] != '\\\\' {\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tn++\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif n%2 == 0 {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn json[:i+1], unescape(json[1:i])\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar ret string\n\t\t\tif i+1 < len(json) {\n\t\t\t\tret = json[:i+1]\n\t\t\t} else {\n\t\t\t\tret = json[:i]\n\t\t\t}\n\t\t\treturn ret, unescape(json[1:i])\n\t\t}\n\t}\n\treturn json, json[1:]\n}\n\n// Exists returns true if value exists.\n//\n//\t if gjson.Get(json, \"name.last\").Exists(){\n//\t\t\tprintln(\"value exists\")\n//\t }\nfunc (t Result) Exists() bool {\n\treturn t.Type != Null || len(t.Raw) != 0\n}\n\n// Value returns one of these types:\n//\n//\tbool, for JSON booleans\n//\tfloat64, for JSON numbers\n//\tNumber, for JSON numbers\n//\tstring, for JSON string literals\n//\tnil, for JSON null\n//\tmap[string]interface{}, for JSON objects\n//\t[]interface{}, for JSON arrays\nfunc (t Result) Value() interface{} {\n\tif t.Type == String {\n\t\treturn t.Str\n\t}\n\tswitch t.Type {\n\tdefault:\n\t\treturn nil\n\tcase False:\n\t\treturn false\n\tcase Number:\n\t\treturn t.Num\n\tcase JSON:\n\t\tr := t.arrayOrMap(0, true)\n\t\tif r.vc == '{' {\n\t\t\treturn r.oi\n\t\t} else if r.vc == '[' {\n\t\t\treturn r.ai\n\t\t}\n\t\treturn nil\n\tcase True:\n\t\treturn true\n\t}\n}\n\nfunc parseString(json string, i int) (int, string, bool, bool) {\n\tvar s = i\n\tfor ; i < len(json); i++ {\n\t\tif json[i] > '\\\\' {\n\t\t\tcontinue\n\t\t}\n\t\tif json[i] == '\"' {\n\t\t\treturn i + 1, json[s-1 : i+1], false, true\n\t\t}\n\t\tif json[i] == '\\\\' {\n\t\t\ti++\n\t\t\tfor ; i < len(json); i++ {\n\t\t\t\tif json[i] > '\\\\' {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif json[i] == '\"' {\n\t\t\t\t\t// look for an escaped slash\n\t\t\t\t\tif json[i-1] == '\\\\' {\n\t\t\t\t\t\tn := 0\n\t\t\t\t\t\tfor j := i - 2; j > 0; j-- {\n\t\t\t\t\t\t\tif json[j] != '\\\\' {\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tn++\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif n%2 == 0 {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn i + 1, json[s-1 : i+1], true, true\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\treturn i, json[s-1:], false, false\n}\n\nfunc parseNumber(json string, i int) (int, string) {\n\tvar s = i\n\ti++\n\tfor ; i < len(json); i++ {\n\t\tif json[i] <= ' ' || json[i] == ',' || json[i] == ']' ||\n\t\t\tjson[i] == '}' {\n\t\t\treturn i, json[s:i]\n\t\t}\n\t}\n\treturn i, json[s:]\n}\n\nfunc parseLiteral(json string, i int) (int, string) {\n\tvar s = i\n\ti++\n\tfor ; i < len(json); i++ {\n\t\tif json[i] < 'a' || json[i] > 'z' {\n\t\t\treturn i, json[s:i]\n\t\t}\n\t}\n\treturn i, json[s:]\n}\n\ntype arrayPathResult struct {\n\tpart    string\n\tpath    string\n\tpipe    string\n\tpiped   bool\n\tmore    bool\n\talogok  bool\n\tarrch   bool\n\talogkey string\n\tquery   struct {\n\t\ton    bool\n\t\tall   bool\n\t\tpath  string\n\t\top    string\n\t\tvalue string\n\t}\n}\n\nfunc parseArrayPath(path string) (r arrayPathResult) {\n\tfor i := 0; i < len(path); i++ {\n\t\tif path[i] == '|' {\n\t\t\tr.part = path[:i]\n\t\t\tr.pipe = path[i+1:]\n\t\t\tr.piped = true\n\t\t\treturn\n\t\t}\n\t\tif path[i] == '.' {\n\t\t\tr.part = path[:i]\n\t\t\tif !r.arrch && i < len(path)-1 && isDotPiperChar(path[i+1:]) {\n\t\t\t\tr.pipe = path[i+1:]\n\t\t\t\tr.piped = true\n\t\t\t} else {\n\t\t\t\tr.path = path[i+1:]\n\t\t\t\tr.more = true\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif path[i] == '#' {\n\t\t\tr.arrch = true\n\t\t\tif i == 0 && len(path) > 1 {\n\t\t\t\tif path[1] == '.' {\n\t\t\t\t\tr.alogok = true\n\t\t\t\t\tr.alogkey = path[2:]\n\t\t\t\t\tr.path = path[:1]\n\t\t\t\t} else if path[1] == '[' || path[1] == '(' {\n\t\t\t\t\t// query\n\t\t\t\t\tr.query.on = true\n\t\t\t\t\tqpath, op, value, _, fi, vesc, ok :=\n\t\t\t\t\t\tparseQuery(path[i:])\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\t// bad query, end now\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tif len(value) >= 2 && value[0] == '\"' &&\n\t\t\t\t\t\tvalue[len(value)-1] == '\"' {\n\t\t\t\t\t\tvalue = value[1 : len(value)-1]\n\t\t\t\t\t\tif vesc {\n\t\t\t\t\t\t\tvalue = unescape(value)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tr.query.path = qpath\n\t\t\t\t\tr.query.op = op\n\t\t\t\t\tr.query.value = value\n\n\t\t\t\t\ti = fi - 1\n\t\t\t\t\tif i+1 < len(path) && path[i+1] == '#' {\n\t\t\t\t\t\tr.query.all = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t}\n\tr.part = path\n\tr.path = \"\"\n\treturn\n}\n\n// splitQuery takes a query and splits it into three parts:\n//\n//\tpath, op, middle, and right.\n//\n// So for this query:\n//\n//\t#(first_name==\"Murphy\").last\n//\n// Becomes\n//\n//\tfirst_name   # path\n//\t==\"Murphy\"   # middle\n//\t.last        # right\n//\n// Or,\n//\n//\t#(service_roles.#(==\"one\")).cap\n//\n// Becomes\n//\n//\tservice_roles.#(==\"one\")   # path\n//\t                           # middle\n//\t.cap                       # right\nfunc parseQuery(query string) (\n\tpath, op, value, remain string, i int, vesc, ok bool,\n) {\n\tif len(query) < 2 || query[0] != '#' ||\n\t\t(query[1] != '(' && query[1] != '[') {\n\t\treturn \"\", \"\", \"\", \"\", i, false, false\n\t}\n\ti = 2\n\tj := 0 // start of value part\n\tdepth := 1\n\tfor ; i < len(query); i++ {\n\t\tif depth == 1 && j == 0 {\n\t\t\tswitch query[i] {\n\t\t\tcase '!', '=', '<', '>', '%':\n\t\t\t\t// start of the value part\n\t\t\t\tj = i\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tif query[i] == '\\\\' {\n\t\t\ti++\n\t\t} else if query[i] == '[' || query[i] == '(' {\n\t\t\tdepth++\n\t\t} else if query[i] == ']' || query[i] == ')' {\n\t\t\tdepth--\n\t\t\tif depth == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t} else if query[i] == '\"' {\n\t\t\t// inside selector string, balance quotes\n\t\t\ti++\n\t\t\tfor ; i < len(query); i++ {\n\t\t\t\tif query[i] == '\\\\' {\n\t\t\t\t\tvesc = true\n\t\t\t\t\ti++\n\t\t\t\t} else if query[i] == '\"' {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif depth > 0 {\n\t\treturn \"\", \"\", \"\", \"\", i, false, false\n\t}\n\tif j > 0 {\n\t\tpath = trim(query[2:j])\n\t\tvalue = trim(query[j:i])\n\t\tremain = query[i+1:]\n\t\t// parse the compare op from the value\n\t\tvar opsz int\n\t\tswitch {\n\t\tcase len(value) == 1:\n\t\t\topsz = 1\n\t\tcase value[0] == '!' && value[1] == '=':\n\t\t\topsz = 2\n\t\tcase value[0] == '!' && value[1] == '%':\n\t\t\topsz = 2\n\t\tcase value[0] == '<' && value[1] == '=':\n\t\t\topsz = 2\n\t\tcase value[0] == '>' && value[1] == '=':\n\t\t\topsz = 2\n\t\tcase value[0] == '=' && value[1] == '=':\n\t\t\tvalue = value[1:]\n\t\t\topsz = 1\n\t\tcase value[0] == '<':\n\t\t\topsz = 1\n\t\tcase value[0] == '>':\n\t\t\topsz = 1\n\t\tcase value[0] == '=':\n\t\t\topsz = 1\n\t\tcase value[0] == '%':\n\t\t\topsz = 1\n\t\t}\n\t\top = value[:opsz]\n\t\tvalue = trim(value[opsz:])\n\t} else {\n\t\tpath = trim(query[2:i])\n\t\tremain = query[i+1:]\n\t}\n\treturn path, op, value, remain, i + 1, vesc, true\n}\n\nfunc trim(s string) string {\nleft:\n\tif len(s) > 0 && s[0] <= ' ' {\n\t\ts = s[1:]\n\t\tgoto left\n\t}\nright:\n\tif len(s) > 0 && s[len(s)-1] <= ' ' {\n\t\ts = s[:len(s)-1]\n\t\tgoto right\n\t}\n\treturn s\n}\n\n// peek at the next byte and see if it's a '@', '[', or '{'.\nfunc isDotPiperChar(s string) bool {\n\tif DisableModifiers {\n\t\treturn false\n\t}\n\tc := s[0]\n\tif c == '@' {\n\t\t// check that the next component is *not* a modifier.\n\t\ti := 1\n\t\tfor ; i < len(s); i++ {\n\t\t\tif s[i] == '.' || s[i] == '|' || s[i] == ':' {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\t_, ok := modifiers[s[1:i]]\n\t\treturn ok\n\t}\n\treturn c == '[' || c == '{'\n}\n\ntype objectPathResult struct {\n\tpart  string\n\tpath  string\n\tpipe  string\n\tpiped bool\n\twild  bool\n\tmore  bool\n}\n\nfunc parseObjectPath(path string) (r objectPathResult) {\n\tfor i := 0; i < len(path); i++ {\n\t\tif path[i] == '|' {\n\t\t\tr.part = path[:i]\n\t\t\tr.pipe = path[i+1:]\n\t\t\tr.piped = true\n\t\t\treturn\n\t\t}\n\t\tif path[i] == '.' {\n\t\t\tr.part = path[:i]\n\t\t\tif i < len(path)-1 && isDotPiperChar(path[i+1:]) {\n\t\t\t\tr.pipe = path[i+1:]\n\t\t\t\tr.piped = true\n\t\t\t} else {\n\t\t\t\tr.path = path[i+1:]\n\t\t\t\tr.more = true\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif path[i] == '*' || path[i] == '?' {\n\t\t\tr.wild = true\n\t\t\tcontinue\n\t\t}\n\t\tif path[i] == '\\\\' {\n\t\t\t// go into escape mode. this is a slower path that\n\t\t\t// strips off the escape character from the part.\n\t\t\tepart := []byte(path[:i])\n\t\t\ti++\n\t\t\tif i < len(path) {\n\t\t\t\tepart = append(epart, path[i])\n\t\t\t\ti++\n\t\t\t\tfor ; i < len(path); i++ {\n\t\t\t\t\tif path[i] == '\\\\' {\n\t\t\t\t\t\ti++\n\t\t\t\t\t\tif i < len(path) {\n\t\t\t\t\t\t\tepart = append(epart, path[i])\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t} else if path[i] == '.' {\n\t\t\t\t\t\tr.part = string(epart)\n\t\t\t\t\t\tif i < len(path)-1 && isDotPiperChar(path[i+1:]) {\n\t\t\t\t\t\t\tr.pipe = path[i+1:]\n\t\t\t\t\t\t\tr.piped = true\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tr.path = path[i+1:]\n\t\t\t\t\t\t\tr.more = true\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn\n\t\t\t\t\t} else if path[i] == '|' {\n\t\t\t\t\t\tr.part = string(epart)\n\t\t\t\t\t\tr.pipe = path[i+1:]\n\t\t\t\t\t\tr.piped = true\n\t\t\t\t\t\treturn\n\t\t\t\t\t} else if path[i] == '*' || path[i] == '?' {\n\t\t\t\t\t\tr.wild = true\n\t\t\t\t\t}\n\t\t\t\t\tepart = append(epart, path[i])\n\t\t\t\t}\n\t\t\t}\n\t\t\t// append the last part\n\t\t\tr.part = string(epart)\n\t\t\treturn\n\t\t}\n\t}\n\tr.part = path\n\treturn\n}\n\nvar vchars = [256]byte{\n\t'\"': 2, '{': 3, '(': 3, '[': 3, '}': 1, ')': 1, ']': 1,\n}\n\nfunc parseSquash(json string, i int) (int, string) {\n\t// expects that the lead character is a '[' or '{' or '('\n\t// squash the value, ignoring all nested arrays and objects.\n\t// the first '[' or '{' or '(' has already been read\n\ts := i\n\ti++\n\tdepth := 1\n\tvar c byte\n\tfor i < len(json) {\n\t\tfor i < len(json)-8 {\n\t\t\tjslice := json[i : i+8]\n\t\t\tc = vchars[jslice[0]]\n\t\t\tif c != 0 {\n\t\t\t\ti += 0\n\t\t\t\tgoto token\n\t\t\t}\n\t\t\tc = vchars[jslice[1]]\n\t\t\tif c != 0 {\n\t\t\t\ti += 1\n\t\t\t\tgoto token\n\t\t\t}\n\t\t\tc = vchars[jslice[2]]\n\t\t\tif c != 0 {\n\t\t\t\ti += 2\n\t\t\t\tgoto token\n\t\t\t}\n\t\t\tc = vchars[jslice[3]]\n\t\t\tif c != 0 {\n\t\t\t\ti += 3\n\t\t\t\tgoto token\n\t\t\t}\n\t\t\tc = vchars[jslice[4]]\n\t\t\tif c != 0 {\n\t\t\t\ti += 4\n\t\t\t\tgoto token\n\t\t\t}\n\t\t\tc = vchars[jslice[5]]\n\t\t\tif c != 0 {\n\t\t\t\ti += 5\n\t\t\t\tgoto token\n\t\t\t}\n\t\t\tc = vchars[jslice[6]]\n\t\t\tif c != 0 {\n\t\t\t\ti += 6\n\t\t\t\tgoto token\n\t\t\t}\n\t\t\tc = vchars[jslice[7]]\n\t\t\tif c != 0 {\n\t\t\t\ti += 7\n\t\t\t\tgoto token\n\t\t\t}\n\t\t\ti += 8\n\t\t}\n\t\tc = vchars[json[i]]\n\t\tif c == 0 {\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\ttoken:\n\t\tif c == 2 {\n\t\t\t// '\"' string\n\t\t\ti++\n\t\t\ts2 := i\n\t\tnextquote:\n\t\t\tfor i < len(json)-8 {\n\t\t\t\tjslice := json[i : i+8]\n\t\t\t\tif jslice[0] == '\"' {\n\t\t\t\t\ti += 0\n\t\t\t\t\tgoto strchkesc\n\t\t\t\t}\n\t\t\t\tif jslice[1] == '\"' {\n\t\t\t\t\ti += 1\n\t\t\t\t\tgoto strchkesc\n\t\t\t\t}\n\t\t\t\tif jslice[2] == '\"' {\n\t\t\t\t\ti += 2\n\t\t\t\t\tgoto strchkesc\n\t\t\t\t}\n\t\t\t\tif jslice[3] == '\"' {\n\t\t\t\t\ti += 3\n\t\t\t\t\tgoto strchkesc\n\t\t\t\t}\n\t\t\t\tif jslice[4] == '\"' {\n\t\t\t\t\ti += 4\n\t\t\t\t\tgoto strchkesc\n\t\t\t\t}\n\t\t\t\tif jslice[5] == '\"' {\n\t\t\t\t\ti += 5\n\t\t\t\t\tgoto strchkesc\n\t\t\t\t}\n\t\t\t\tif jslice[6] == '\"' {\n\t\t\t\t\ti += 6\n\t\t\t\t\tgoto strchkesc\n\t\t\t\t}\n\t\t\t\tif jslice[7] == '\"' {\n\t\t\t\t\ti += 7\n\t\t\t\t\tgoto strchkesc\n\t\t\t\t}\n\t\t\t\ti += 8\n\t\t\t}\n\t\t\tgoto strchkstd\n\t\tstrchkesc:\n\t\t\tif json[i-1] != '\\\\' {\n\t\t\t\ti++\n\t\t\t\tcontinue\n\t\t\t}\n\t\tstrchkstd:\n\t\t\tfor i < len(json) {\n\t\t\t\tif json[i] > '\\\\' || json[i] != '\"' {\n\t\t\t\t\ti++\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// look for an escaped slash\n\t\t\t\tif json[i-1] == '\\\\' {\n\t\t\t\t\tn := 0\n\t\t\t\t\tfor j := i - 2; j > s2-1; j-- {\n\t\t\t\t\t\tif json[j] != '\\\\' {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tn++\n\t\t\t\t\t}\n\t\t\t\t\tif n%2 == 0 {\n\t\t\t\t\t\ti++\n\t\t\t\t\t\tgoto nextquote\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t} else {\n\t\t\t// '{', '[', '(', '}', ']', ')'\n\t\t\t// open close tokens\n\t\t\tdepth += int(c) - 2\n\t\t\tif depth == 0 {\n\t\t\t\ti++\n\t\t\t\treturn i, json[s:i]\n\t\t\t}\n\t\t}\n\t\ti++\n\t}\n\treturn i, json[s:]\n}\n\nfunc parseObject(c *parseContext, i int, path string) (int, bool) {\n\tvar pmatch, kesc, vesc, ok, hit bool\n\tvar key, val string\n\trp := parseObjectPath(path)\n\tif !rp.more && rp.piped {\n\t\tc.pipe = rp.pipe\n\t\tc.piped = true\n\t}\n\tfor i < len(c.json) {\n\t\tfor ; i < len(c.json); i++ {\n\t\t\tif c.json[i] == '\"' {\n\t\t\t\t// parse_key_string\n\t\t\t\t// this is slightly different from getting s string value\n\t\t\t\t// because we don't need the outer quotes.\n\t\t\t\ti++\n\t\t\t\tvar s = i\n\t\t\t\tfor ; i < len(c.json); i++ {\n\t\t\t\t\tif c.json[i] > '\\\\' {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif c.json[i] == '\"' {\n\t\t\t\t\t\ti, key, kesc, ok = i+1, c.json[s:i], false, true\n\t\t\t\t\t\tgoto parse_key_string_done\n\t\t\t\t\t}\n\t\t\t\t\tif c.json[i] == '\\\\' {\n\t\t\t\t\t\ti++\n\t\t\t\t\t\tfor ; i < len(c.json); i++ {\n\t\t\t\t\t\t\tif c.json[i] > '\\\\' {\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif c.json[i] == '\"' {\n\t\t\t\t\t\t\t\t// look for an escaped slash\n\t\t\t\t\t\t\t\tif c.json[i-1] == '\\\\' {\n\t\t\t\t\t\t\t\t\tn := 0\n\t\t\t\t\t\t\t\t\tfor j := i - 2; j > 0; j-- {\n\t\t\t\t\t\t\t\t\t\tif c.json[j] != '\\\\' {\n\t\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tn++\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif n%2 == 0 {\n\t\t\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\ti, key, kesc, ok = i+1, c.json[s:i], true, true\n\t\t\t\t\t\t\t\tgoto parse_key_string_done\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tkey, kesc, ok = c.json[s:], false, false\n\t\t\tparse_key_string_done:\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif c.json[i] == '}' {\n\t\t\t\treturn i + 1, false\n\t\t\t}\n\t\t}\n\t\tif !ok {\n\t\t\treturn i, false\n\t\t}\n\t\tif rp.wild {\n\t\t\tif kesc {\n\t\t\t\tpmatch = matchLimit(unescape(key), rp.part)\n\t\t\t} else {\n\t\t\t\tpmatch = matchLimit(key, rp.part)\n\t\t\t}\n\t\t} else {\n\t\t\tif kesc {\n\t\t\t\tpmatch = rp.part == unescape(key)\n\t\t\t} else {\n\t\t\t\tpmatch = rp.part == key\n\t\t\t}\n\t\t}\n\t\thit = pmatch && !rp.more\n\t\tfor ; i < len(c.json); i++ {\n\t\t\tvar num bool\n\t\t\tswitch c.json[i] {\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\tcase '\"':\n\t\t\t\ti++\n\t\t\t\ti, val, vesc, ok = parseString(c.json, i)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn i, false\n\t\t\t\t}\n\t\t\t\tif hit {\n\t\t\t\t\tif vesc {\n\t\t\t\t\t\tc.value.Str = unescape(val[1 : len(val)-1])\n\t\t\t\t\t} else {\n\t\t\t\t\t\tc.value.Str = val[1 : len(val)-1]\n\t\t\t\t\t}\n\t\t\t\t\tc.value.Raw = val\n\t\t\t\t\tc.value.Type = String\n\t\t\t\t\treturn i, true\n\t\t\t\t}\n\t\t\tcase '{':\n\t\t\t\tif pmatch && !hit {\n\t\t\t\t\ti, hit = parseObject(c, i+1, rp.path)\n\t\t\t\t\tif hit {\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ti, val = parseSquash(c.json, i)\n\t\t\t\t\tif hit {\n\t\t\t\t\t\tc.value.Raw = val\n\t\t\t\t\t\tc.value.Type = JSON\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase '[':\n\t\t\t\tif pmatch && !hit {\n\t\t\t\t\ti, hit = parseArray(c, i+1, rp.path)\n\t\t\t\t\tif hit {\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ti, val = parseSquash(c.json, i)\n\t\t\t\t\tif hit {\n\t\t\t\t\t\tc.value.Raw = val\n\t\t\t\t\t\tc.value.Type = JSON\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase 'n':\n\t\t\t\tif i+1 < len(c.json) && c.json[i+1] != 'u' {\n\t\t\t\t\tnum = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tfallthrough\n\t\t\tcase 't', 'f':\n\t\t\t\tvc := c.json[i]\n\t\t\t\ti, val = parseLiteral(c.json, i)\n\t\t\t\tif hit {\n\t\t\t\t\tc.value.Raw = val\n\t\t\t\t\tswitch vc {\n\t\t\t\t\tcase 't':\n\t\t\t\t\t\tc.value.Type = True\n\t\t\t\t\tcase 'f':\n\t\t\t\t\t\tc.value.Type = False\n\t\t\t\t\t}\n\t\t\t\t\treturn i, true\n\t\t\t\t}\n\t\t\tcase '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n\t\t\t\t'i', 'I', 'N':\n\t\t\t\tnum = true\n\t\t\t}\n\t\t\tif num {\n\t\t\t\ti, val = parseNumber(c.json, i)\n\t\t\t\tif hit {\n\t\t\t\t\tc.value.Raw = val\n\t\t\t\t\tc.value.Type = Number\n\t\t\t\t\tc.value.Num, _ = strconv.ParseFloat(val, 64)\n\t\t\t\t\treturn i, true\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\treturn i, false\n}\n\n// matchLimit will limit the complexity of the match operation to avoid ReDos\n// attacks from arbitrary inputs.\n// See the github.com/tidwall/match.MatchLimit function for more information.\nfunc matchLimit(str, pattern string) bool {\n\tmatched, _ := match.MatchLimit(str, pattern, 10000)\n\treturn matched\n}\n\nfunc falseish(t Result) bool {\n\tswitch t.Type {\n\tcase Null:\n\t\treturn true\n\tcase False:\n\t\treturn true\n\tcase String:\n\t\tb, err := strconv.ParseBool(strings.ToLower(t.Str))\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treturn !b\n\tcase Number:\n\t\treturn t.Num == 0\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc trueish(t Result) bool {\n\tswitch t.Type {\n\tcase True:\n\t\treturn true\n\tcase String:\n\t\tb, err := strconv.ParseBool(strings.ToLower(t.Str))\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treturn b\n\tcase Number:\n\t\treturn t.Num != 0\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc nullish(t Result) bool {\n\treturn t.Type == Null\n}\n\nfunc queryMatches(rp *arrayPathResult, value Result) bool {\n\trpv := rp.query.value\n\tif len(rpv) > 0 {\n\t\tif rpv[0] == '~' {\n\t\t\t// convert to bool\n\t\t\trpv = rpv[1:]\n\t\t\tvar ish, ok bool\n\t\t\tswitch rpv {\n\t\t\tcase \"*\":\n\t\t\t\tish, ok = value.Exists(), true\n\t\t\tcase \"null\":\n\t\t\t\tish, ok = nullish(value), true\n\t\t\tcase \"true\":\n\t\t\t\tish, ok = trueish(value), true\n\t\t\tcase \"false\":\n\t\t\t\tish, ok = falseish(value), true\n\t\t\t}\n\t\t\tif ok {\n\t\t\t\trpv = \"true\"\n\t\t\t\tif ish {\n\t\t\t\t\tvalue = Result{Type: True}\n\t\t\t\t} else {\n\t\t\t\t\tvalue = Result{Type: False}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trpv = \"\"\n\t\t\t\tvalue = Result{}\n\t\t\t}\n\t\t}\n\t}\n\tif !value.Exists() {\n\t\treturn false\n\t}\n\tif rp.query.op == \"\" {\n\t\t// the query is only looking for existence, such as:\n\t\t//   friends.#(name)\n\t\t// which makes sure that the array \"friends\" has an element of\n\t\t// \"name\" that exists\n\t\treturn true\n\t}\n\tswitch value.Type {\n\tcase String:\n\t\tswitch rp.query.op {\n\t\tcase \"=\":\n\t\t\treturn value.Str == rpv\n\t\tcase \"!=\":\n\t\t\treturn value.Str != rpv\n\t\tcase \"<\":\n\t\t\treturn value.Str < rpv\n\t\tcase \"<=\":\n\t\t\treturn value.Str <= rpv\n\t\tcase \">\":\n\t\t\treturn value.Str > rpv\n\t\tcase \">=\":\n\t\t\treturn value.Str >= rpv\n\t\tcase \"%\":\n\t\t\treturn matchLimit(value.Str, rpv)\n\t\tcase \"!%\":\n\t\t\treturn !matchLimit(value.Str, rpv)\n\t\t}\n\tcase Number:\n\t\trpvn, _ := strconv.ParseFloat(rpv, 64)\n\t\tswitch rp.query.op {\n\t\tcase \"=\":\n\t\t\treturn value.Num == rpvn\n\t\tcase \"!=\":\n\t\t\treturn value.Num != rpvn\n\t\tcase \"<\":\n\t\t\treturn value.Num < rpvn\n\t\tcase \"<=\":\n\t\t\treturn value.Num <= rpvn\n\t\tcase \">\":\n\t\t\treturn value.Num > rpvn\n\t\tcase \">=\":\n\t\t\treturn value.Num >= rpvn\n\t\t}\n\tcase True:\n\t\tswitch rp.query.op {\n\t\tcase \"=\":\n\t\t\treturn rpv == \"true\"\n\t\tcase \"!=\":\n\t\t\treturn rpv != \"true\"\n\t\tcase \">\":\n\t\t\treturn rpv == \"false\"\n\t\tcase \">=\":\n\t\t\treturn true\n\t\t}\n\tcase False:\n\t\tswitch rp.query.op {\n\t\tcase \"=\":\n\t\t\treturn rpv == \"false\"\n\t\tcase \"!=\":\n\t\t\treturn rpv != \"false\"\n\t\tcase \"<\":\n\t\t\treturn rpv == \"true\"\n\t\tcase \"<=\":\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\nfunc parseArray(c *parseContext, i int, path string) (int, bool) {\n\tvar pmatch, vesc, ok, hit bool\n\tvar val string\n\tvar h int\n\tvar alog []int\n\tvar partidx int\n\tvar multires []byte\n\tvar queryIndexes []int\n\trp := parseArrayPath(path)\n\tif !rp.arrch {\n\t\tn, ok := parseUint(rp.part)\n\t\tif !ok {\n\t\t\tpartidx = -1\n\t\t} else {\n\t\t\tpartidx = int(n)\n\t\t}\n\t}\n\tif !rp.more && rp.piped {\n\t\tc.pipe = rp.pipe\n\t\tc.piped = true\n\t}\n\n\tprocQuery := func(qval Result) bool {\n\t\tif rp.query.all {\n\t\t\tif len(multires) == 0 {\n\t\t\t\tmultires = append(multires, '[')\n\t\t\t}\n\t\t}\n\t\tvar tmp parseContext\n\t\ttmp.value = qval\n\t\tfillIndex(c.json, &tmp)\n\t\tparentIndex := tmp.value.Index\n\t\tvar res Result\n\t\tif qval.Type == JSON {\n\t\t\tres = qval.Get(rp.query.path)\n\t\t} else {\n\t\t\tif rp.query.path != \"\" {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tres = qval\n\t\t}\n\t\tif queryMatches(&rp, res) {\n\t\t\tif rp.more {\n\t\t\t\tleft, right, ok := splitPossiblePipe(rp.path)\n\t\t\t\tif ok {\n\t\t\t\t\trp.path = left\n\t\t\t\t\tc.pipe = right\n\t\t\t\t\tc.piped = true\n\t\t\t\t}\n\t\t\t\tres = qval.Get(rp.path)\n\t\t\t} else {\n\t\t\t\tres = qval\n\t\t\t}\n\t\t\tif rp.query.all {\n\t\t\t\traw := res.Raw\n\t\t\t\tif len(raw) == 0 {\n\t\t\t\t\traw = res.String()\n\t\t\t\t}\n\t\t\t\tif raw != \"\" {\n\t\t\t\t\tif len(multires) > 1 {\n\t\t\t\t\t\tmultires = append(multires, ',')\n\t\t\t\t\t}\n\t\t\t\t\tmultires = append(multires, raw...)\n\t\t\t\t\tqueryIndexes = append(queryIndexes, res.Index+parentIndex)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tc.value = res\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\tfor i < len(c.json)+1 {\n\t\tif !rp.arrch {\n\t\t\tpmatch = partidx == h\n\t\t\thit = pmatch && !rp.more\n\t\t}\n\t\th++\n\t\tif rp.alogok {\n\t\t\talog = append(alog, i)\n\t\t}\n\t\tfor ; ; i++ {\n\t\t\tvar ch byte\n\t\t\tif i > len(c.json) {\n\t\t\t\tbreak\n\t\t\t} else if i == len(c.json) {\n\t\t\t\tch = ']'\n\t\t\t} else {\n\t\t\t\tch = c.json[i]\n\t\t\t}\n\t\t\tvar num bool\n\t\t\tswitch ch {\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\tcase '\"':\n\t\t\t\ti++\n\t\t\t\ti, val, vesc, ok = parseString(c.json, i)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn i, false\n\t\t\t\t}\n\t\t\t\tif rp.query.on {\n\t\t\t\t\tvar qval Result\n\t\t\t\t\tif vesc {\n\t\t\t\t\t\tqval.Str = unescape(val[1 : len(val)-1])\n\t\t\t\t\t} else {\n\t\t\t\t\t\tqval.Str = val[1 : len(val)-1]\n\t\t\t\t\t}\n\t\t\t\t\tqval.Raw = val\n\t\t\t\t\tqval.Type = String\n\t\t\t\t\tif procQuery(qval) {\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t} else if hit {\n\t\t\t\t\tif rp.alogok {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tif vesc {\n\t\t\t\t\t\tc.value.Str = unescape(val[1 : len(val)-1])\n\t\t\t\t\t} else {\n\t\t\t\t\t\tc.value.Str = val[1 : len(val)-1]\n\t\t\t\t\t}\n\t\t\t\t\tc.value.Raw = val\n\t\t\t\t\tc.value.Type = String\n\t\t\t\t\treturn i, true\n\t\t\t\t}\n\t\t\tcase '{':\n\t\t\t\tif pmatch && !hit {\n\t\t\t\t\ti, hit = parseObject(c, i+1, rp.path)\n\t\t\t\t\tif hit {\n\t\t\t\t\t\tif rp.alogok {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ti, val = parseSquash(c.json, i)\n\t\t\t\t\tif rp.query.on {\n\t\t\t\t\t\tif procQuery(Result{Raw: val, Type: JSON}) {\n\t\t\t\t\t\t\treturn i, true\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if hit {\n\t\t\t\t\t\tif rp.alogok {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tc.value.Raw = val\n\t\t\t\t\t\tc.value.Type = JSON\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase '[':\n\t\t\t\tif pmatch && !hit {\n\t\t\t\t\ti, hit = parseArray(c, i+1, rp.path)\n\t\t\t\t\tif hit {\n\t\t\t\t\t\tif rp.alogok {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ti, val = parseSquash(c.json, i)\n\t\t\t\t\tif rp.query.on {\n\t\t\t\t\t\tif procQuery(Result{Raw: val, Type: JSON}) {\n\t\t\t\t\t\t\treturn i, true\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if hit {\n\t\t\t\t\t\tif rp.alogok {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tc.value.Raw = val\n\t\t\t\t\t\tc.value.Type = JSON\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase 'n':\n\t\t\t\tif i+1 < len(c.json) && c.json[i+1] != 'u' {\n\t\t\t\t\tnum = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tfallthrough\n\t\t\tcase 't', 'f':\n\t\t\t\tvc := c.json[i]\n\t\t\t\ti, val = parseLiteral(c.json, i)\n\t\t\t\tif rp.query.on {\n\t\t\t\t\tvar qval Result\n\t\t\t\t\tqval.Raw = val\n\t\t\t\t\tswitch vc {\n\t\t\t\t\tcase 't':\n\t\t\t\t\t\tqval.Type = True\n\t\t\t\t\tcase 'f':\n\t\t\t\t\t\tqval.Type = False\n\t\t\t\t\t}\n\t\t\t\t\tif procQuery(qval) {\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t} else if hit {\n\t\t\t\t\tif rp.alogok {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tc.value.Raw = val\n\t\t\t\t\tswitch vc {\n\t\t\t\t\tcase 't':\n\t\t\t\t\t\tc.value.Type = True\n\t\t\t\t\tcase 'f':\n\t\t\t\t\t\tc.value.Type = False\n\t\t\t\t\t}\n\t\t\t\t\treturn i, true\n\t\t\t\t}\n\t\t\tcase '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n\t\t\t\t'i', 'I', 'N':\n\t\t\t\tnum = true\n\t\t\tcase ']':\n\t\t\t\tif rp.arrch && rp.part == \"#\" {\n\t\t\t\t\tif rp.alogok {\n\t\t\t\t\t\tleft, right, ok := splitPossiblePipe(rp.alogkey)\n\t\t\t\t\t\tif ok {\n\t\t\t\t\t\t\trp.alogkey = left\n\t\t\t\t\t\t\tc.pipe = right\n\t\t\t\t\t\t\tc.piped = true\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvar indexes = make([]int, 0, 64)\n\t\t\t\t\t\tvar jsons = make([]byte, 0, 64)\n\t\t\t\t\t\tjsons = append(jsons, '[')\n\t\t\t\t\t\tfor j, k := 0, 0; j < len(alog); j++ {\n\t\t\t\t\t\t\tidx := alog[j]\n\t\t\t\t\t\t\tfor idx < len(c.json) {\n\t\t\t\t\t\t\t\tswitch c.json[idx] {\n\t\t\t\t\t\t\t\tcase ' ', '\\t', '\\r', '\\n':\n\t\t\t\t\t\t\t\t\tidx++\n\t\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif idx < len(c.json) && c.json[idx] != ']' {\n\t\t\t\t\t\t\t\t_, res, ok := parseAny(c.json, idx, true)\n\t\t\t\t\t\t\t\tif ok {\n\t\t\t\t\t\t\t\t\tres := res.Get(rp.alogkey)\n\t\t\t\t\t\t\t\t\tif res.Exists() {\n\t\t\t\t\t\t\t\t\t\tif k > 0 {\n\t\t\t\t\t\t\t\t\t\t\tjsons = append(jsons, ',')\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\traw := res.Raw\n\t\t\t\t\t\t\t\t\t\tif len(raw) == 0 {\n\t\t\t\t\t\t\t\t\t\t\traw = res.String()\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tjsons = append(jsons, []byte(raw)...)\n\t\t\t\t\t\t\t\t\t\tindexes = append(indexes, res.Index)\n\t\t\t\t\t\t\t\t\t\tk++\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tjsons = append(jsons, ']')\n\t\t\t\t\t\tc.value.Type = JSON\n\t\t\t\t\t\tc.value.Raw = string(jsons)\n\t\t\t\t\t\tc.value.Indexes = indexes\n\t\t\t\t\t\treturn i + 1, true\n\t\t\t\t\t}\n\t\t\t\t\tif rp.alogok {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\n\t\t\t\t\tc.value.Type = Number\n\t\t\t\t\tc.value.Num = float64(h - 1)\n\t\t\t\t\tc.value.Raw = strconv.Itoa(h - 1)\n\t\t\t\t\tc.calcd = true\n\t\t\t\t\treturn i + 1, true\n\t\t\t\t}\n\t\t\t\tif !c.value.Exists() {\n\t\t\t\t\tif len(multires) > 0 {\n\t\t\t\t\t\tc.value = Result{\n\t\t\t\t\t\t\tRaw:     string(append(multires, ']')),\n\t\t\t\t\t\t\tType:    JSON,\n\t\t\t\t\t\t\tIndexes: queryIndexes,\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if rp.query.all {\n\t\t\t\t\t\tc.value = Result{\n\t\t\t\t\t\t\tRaw:  \"[]\",\n\t\t\t\t\t\t\tType: JSON,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn i + 1, false\n\t\t\t}\n\t\t\tif num {\n\t\t\t\ti, val = parseNumber(c.json, i)\n\t\t\t\tif rp.query.on {\n\t\t\t\t\tvar qval Result\n\t\t\t\t\tqval.Raw = val\n\t\t\t\t\tqval.Type = Number\n\t\t\t\t\tqval.Num, _ = strconv.ParseFloat(val, 64)\n\t\t\t\t\tif procQuery(qval) {\n\t\t\t\t\t\treturn i, true\n\t\t\t\t\t}\n\t\t\t\t} else if hit {\n\t\t\t\t\tif rp.alogok {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tc.value.Raw = val\n\t\t\t\t\tc.value.Type = Number\n\t\t\t\t\tc.value.Num, _ = strconv.ParseFloat(val, 64)\n\t\t\t\t\treturn i, true\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\treturn i, false\n}\n\nfunc splitPossiblePipe(path string) (left, right string, ok bool) {\n\t// take a quick peek for the pipe character. If found we'll split the piped\n\t// part of the path into the c.pipe field and shorten the rp.\n\tvar possible bool\n\tfor i := 0; i < len(path); i++ {\n\t\tif path[i] == '|' {\n\t\t\tpossible = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !possible {\n\t\treturn\n\t}\n\n\tif len(path) > 0 && path[0] == '{' {\n\t\tsquashed := squash(path[1:])\n\t\tif len(squashed) < len(path)-1 {\n\t\t\tsquashed = path[:len(squashed)+1]\n\t\t\tremain := path[len(squashed):]\n\t\t\tif remain[0] == '|' {\n\t\t\t\treturn squashed, remain[1:], true\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\t// split the left and right side of the path with the pipe character as\n\t// the delimiter. This is a little tricky because we'll need to basically\n\t// parse the entire path.\n\tfor i := 0; i < len(path); i++ {\n\t\tif path[i] == '\\\\' {\n\t\t\ti++\n\t\t} else if path[i] == '.' {\n\t\t\tif i == len(path)-1 {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif path[i+1] == '#' {\n\t\t\t\ti += 2\n\t\t\t\tif i == len(path) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif path[i] == '[' || path[i] == '(' {\n\t\t\t\t\tvar start, end byte\n\t\t\t\t\tif path[i] == '[' {\n\t\t\t\t\t\tstart, end = '[', ']'\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstart, end = '(', ')'\n\t\t\t\t\t}\n\t\t\t\t\t// inside selector, balance brackets\n\t\t\t\t\ti++\n\t\t\t\t\tdepth := 1\n\t\t\t\t\tfor ; i < len(path); i++ {\n\t\t\t\t\t\tif path[i] == '\\\\' {\n\t\t\t\t\t\t\ti++\n\t\t\t\t\t\t} else if path[i] == start {\n\t\t\t\t\t\t\tdepth++\n\t\t\t\t\t\t} else if path[i] == end {\n\t\t\t\t\t\t\tdepth--\n\t\t\t\t\t\t\tif depth == 0 {\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if path[i] == '\"' {\n\t\t\t\t\t\t\t// inside selector string, balance quotes\n\t\t\t\t\t\t\ti++\n\t\t\t\t\t\t\tfor ; i < len(path); i++ {\n\t\t\t\t\t\t\t\tif path[i] == '\\\\' {\n\t\t\t\t\t\t\t\t\ti++\n\t\t\t\t\t\t\t\t} else if path[i] == '\"' {\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if path[i] == '|' {\n\t\t\treturn path[:i], path[i+1:], true\n\t\t}\n\t}\n\treturn\n}\n\n// ForEachLine iterates through lines of JSON as specified by the JSON Lines\n// format (http://jsonlines.org/).\n// Each line is returned as a GJSON Result.\nfunc ForEachLine(json string, iterator func(line Result) bool) {\n\tvar res Result\n\tvar i int\n\tfor {\n\t\ti, res, _ = parseAny(json, i, true)\n\t\tif !res.Exists() {\n\t\t\tbreak\n\t\t}\n\t\tif !iterator(res) {\n\t\t\treturn\n\t\t}\n\t}\n}\n\ntype subSelector struct {\n\tname string\n\tpath string\n}\n\n// parseSubSelectors returns the subselectors belonging to a '[path1,path2]' or\n// '{\"field1\":path1,\"field2\":path2}' type subSelection. It's expected that the\n// first character in path is either '[' or '{', and has already been checked\n// prior to calling this function.\nfunc parseSubSelectors(path string) (sels []subSelector, out string, ok bool) {\n\tmodifier := 0\n\tdepth := 1\n\tcolon := 0\n\tstart := 1\n\ti := 1\n\tpushSel := func() {\n\t\tvar sel subSelector\n\t\tif colon == 0 {\n\t\t\tsel.path = path[start:i]\n\t\t} else {\n\t\t\tsel.name = path[start:colon]\n\t\t\tsel.path = path[colon+1 : i]\n\t\t}\n\t\tsels = append(sels, sel)\n\t\tcolon = 0\n\t\tmodifier = 0\n\t\tstart = i + 1\n\t}\n\tfor ; i < len(path); i++ {\n\t\tswitch path[i] {\n\t\tcase '\\\\':\n\t\t\ti++\n\t\tcase '@':\n\t\t\tif modifier == 0 && i > 0 && (path[i-1] == '.' || path[i-1] == '|') {\n\t\t\t\tmodifier = i\n\t\t\t}\n\t\tcase ':':\n\t\t\tif modifier == 0 && colon == 0 && depth == 1 {\n\t\t\t\tcolon = i\n\t\t\t}\n\t\tcase ',':\n\t\t\tif depth == 1 {\n\t\t\t\tpushSel()\n\t\t\t}\n\t\tcase '\"':\n\t\t\ti++\n\t\tloop:\n\t\t\tfor ; i < len(path); i++ {\n\t\t\t\tswitch path[i] {\n\t\t\t\tcase '\\\\':\n\t\t\t\t\ti++\n\t\t\t\tcase '\"':\n\t\t\t\t\tbreak loop\n\t\t\t\t}\n\t\t\t}\n\t\tcase '[', '(', '{':\n\t\t\tdepth++\n\t\tcase ']', ')', '}':\n\t\t\tdepth--\n\t\t\tif depth == 0 {\n\t\t\t\tpushSel()\n\t\t\t\tpath = path[i+1:]\n\t\t\t\treturn sels, path, true\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// nameOfLast returns the name of the last component\nfunc nameOfLast(path string) string {\n\tfor i := len(path) - 1; i >= 0; i-- {\n\t\tif path[i] == '|' || path[i] == '.' {\n\t\t\tif i > 0 {\n\t\t\t\tif path[i-1] == '\\\\' {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn path[i+1:]\n\t\t}\n\t}\n\treturn path\n}\n\nfunc isSimpleName(component string) bool {\n\tfor i := 0; i < len(component); i++ {\n\t\tif component[i] < ' ' {\n\t\t\treturn false\n\t\t}\n\t\tswitch component[i] {\n\t\tcase '[', ']', '{', '}', '(', ')', '#', '|', '!':\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nvar hexchars = [...]byte{\n\t'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n\t'a', 'b', 'c', 'd', 'e', 'f',\n}\n\nfunc appendHex16(dst []byte, x uint16) []byte {\n\treturn append(dst,\n\t\thexchars[x>>12&0xF], hexchars[x>>8&0xF],\n\t\thexchars[x>>4&0xF], hexchars[x>>0&0xF],\n\t)\n}\n\n// DisableEscapeHTML will disable the automatic escaping of certain\n// \"problamatic\" HTML characters when encoding to JSON.\n// These character include '>', '<' and '&', which get escaped to \\u003e,\n// \\u0026, and \\u003c respectively.\n//\n// This is a global flag and will affect all further gjson operations.\n// Ideally, if used, it should be set one time before other gjson functions\n// are called.\nvar DisableEscapeHTML = false\n\n// AppendJSONString is a convenience function that converts the provided string\n// to a valid JSON string and appends it to dst.\nfunc AppendJSONString(dst []byte, s string) []byte {\n\tdst = append(dst, make([]byte, len(s)+2)...)\n\tdst = append(dst[:len(dst)-len(s)-2], '\"')\n\tfor i := 0; i < len(s); i++ {\n\t\tif s[i] < ' ' {\n\t\t\tdst = append(dst, '\\\\')\n\t\t\tswitch s[i] {\n\t\t\tcase '\\b':\n\t\t\t\tdst = append(dst, 'b')\n\t\t\tcase '\\f':\n\t\t\t\tdst = append(dst, 'f')\n\t\t\tcase '\\n':\n\t\t\t\tdst = append(dst, 'n')\n\t\t\tcase '\\r':\n\t\t\t\tdst = append(dst, 'r')\n\t\t\tcase '\\t':\n\t\t\t\tdst = append(dst, 't')\n\t\t\tdefault:\n\t\t\t\tdst = append(dst, 'u')\n\t\t\t\tdst = appendHex16(dst, uint16(s[i]))\n\t\t\t}\n\t\t} else if !DisableEscapeHTML &&\n\t\t\t(s[i] == '>' || s[i] == '<' || s[i] == '&') {\n\t\t\tdst = append(dst, '\\\\', 'u')\n\t\t\tdst = appendHex16(dst, uint16(s[i]))\n\t\t} else if s[i] == '\\\\' {\n\t\t\tdst = append(dst, '\\\\', '\\\\')\n\t\t} else if s[i] == '\"' {\n\t\t\tdst = append(dst, '\\\\', '\"')\n\t\t} else if s[i] > 127 {\n\t\t\t// read utf8 character\n\t\t\tr, n := utf8.DecodeRuneInString(s[i:])\n\t\t\tif n == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif r == utf8.RuneError && n == 1 {\n\t\t\t\tdst = append(dst, `\\ufffd`...)\n\t\t\t} else if r == '\\u2028' || r == '\\u2029' {\n\t\t\t\tdst = append(dst, `\\u202`...)\n\t\t\t\tdst = append(dst, hexchars[r&0xF])\n\t\t\t} else {\n\t\t\t\tdst = append(dst, s[i:i+n]...)\n\t\t\t}\n\t\t\ti = i + n - 1\n\t\t} else {\n\t\t\tdst = append(dst, s[i])\n\t\t}\n\t}\n\treturn append(dst, '\"')\n}\n\ntype parseContext struct {\n\tjson  string\n\tvalue Result\n\tpipe  string\n\tpiped bool\n\tcalcd bool\n\tlines bool\n}\n\n// Get searches json for the specified path.\n// A path is in dot syntax, such as \"name.last\" or \"age\".\n// When the value is found it's returned immediately.\n//\n// A path is a series of keys separated by a dot.\n// A key may contain special wildcard characters '*' and '?'.\n// To access an array value use the index as the key.\n// To get the number of elements in an array or to access a child path, use\n// the '#' character.\n// The dot and wildcard character can be escaped with '\\'.\n//\n//\t{\n//\t  \"name\": {\"first\": \"Tom\", \"last\": \"Anderson\"},\n//\t  \"age\":37,\n//\t  \"children\": [\"Sara\",\"Alex\",\"Jack\"],\n//\t  \"friends\": [\n//\t    {\"first\": \"James\", \"last\": \"Murphy\"},\n//\t    {\"first\": \"Roger\", \"last\": \"Craig\"}\n//\t  ]\n//\t}\n//\t\"name.last\"          >> \"Anderson\"\n//\t\"age\"                >> 37\n//\t\"children\"           >> [\"Sara\",\"Alex\",\"Jack\"]\n//\t\"children.#\"         >> 3\n//\t\"children.1\"         >> \"Alex\"\n//\t\"child*.2\"           >> \"Jack\"\n//\t\"c?ildren.0\"         >> \"Sara\"\n//\t\"friends.#.first\"    >> [\"James\",\"Roger\"]\n//\n// This function expects that the json is well-formed, and does not validate.\n// Invalid json will not panic, but it may return back unexpected results.\n// If you are consuming JSON from an unpredictable source then you may want to\n// use the Valid function first.\nfunc Get(json, path string) Result {\n\tif len(path) > 1 {\n\t\tif (path[0] == '@' && !DisableModifiers) || path[0] == '!' {\n\t\t\t// possible modifier\n\t\t\tvar ok bool\n\t\t\tvar npath string\n\t\t\tvar rjson string\n\t\t\tif path[0] == '@' && !DisableModifiers {\n\t\t\t\tnpath, rjson, ok = execModifier(json, path)\n\t\t\t} else if path[0] == '!' {\n\t\t\t\tnpath, rjson, ok = execStatic(json, path)\n\t\t\t}\n\t\t\tif ok {\n\t\t\t\tpath = npath\n\t\t\t\tif len(path) > 0 && (path[0] == '|' || path[0] == '.') {\n\t\t\t\t\tres := Get(rjson, path[1:])\n\t\t\t\t\tres.Index = 0\n\t\t\t\t\tres.Indexes = nil\n\t\t\t\t\treturn res\n\t\t\t\t}\n\t\t\t\treturn Parse(rjson)\n\t\t\t}\n\t\t}\n\t\tif path[0] == '[' || path[0] == '{' {\n\t\t\t// using a subselector path\n\t\t\tkind := path[0]\n\t\t\tvar ok bool\n\t\t\tvar subs []subSelector\n\t\t\tsubs, path, ok = parseSubSelectors(path)\n\t\t\tif ok {\n\t\t\t\tif len(path) == 0 || (path[0] == '|' || path[0] == '.') {\n\t\t\t\t\tvar b []byte\n\t\t\t\t\tb = append(b, kind)\n\t\t\t\t\tvar i int\n\t\t\t\t\tfor _, sub := range subs {\n\t\t\t\t\t\tres := Get(json, sub.path)\n\t\t\t\t\t\tif res.Exists() {\n\t\t\t\t\t\t\tif i > 0 {\n\t\t\t\t\t\t\t\tb = append(b, ',')\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif kind == '{' {\n\t\t\t\t\t\t\t\tif len(sub.name) > 0 {\n\t\t\t\t\t\t\t\t\tif sub.name[0] == '\"' && Valid(sub.name) {\n\t\t\t\t\t\t\t\t\t\tb = append(b, sub.name...)\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tb = AppendJSONString(b, sub.name)\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tlast := nameOfLast(sub.path)\n\t\t\t\t\t\t\t\t\tif isSimpleName(last) {\n\t\t\t\t\t\t\t\t\t\tb = AppendJSONString(b, last)\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tb = AppendJSONString(b, \"_\")\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tb = append(b, ':')\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tvar raw string\n\t\t\t\t\t\t\tif len(res.Raw) == 0 {\n\t\t\t\t\t\t\t\traw = res.String()\n\t\t\t\t\t\t\t\tif len(raw) == 0 {\n\t\t\t\t\t\t\t\t\traw = \"null\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\traw = res.Raw\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tb = append(b, raw...)\n\t\t\t\t\t\t\ti++\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tb = append(b, kind+2)\n\t\t\t\t\tvar res Result\n\t\t\t\t\tres.Raw = string(b)\n\t\t\t\t\tres.Type = JSON\n\t\t\t\t\tif len(path) > 0 {\n\t\t\t\t\t\tres = res.Get(path[1:])\n\t\t\t\t\t}\n\t\t\t\t\tres.Index = 0\n\t\t\t\t\treturn res\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tvar i int\n\tvar c = &parseContext{json: json}\n\tif len(path) >= 2 && path[0] == '.' && path[1] == '.' {\n\t\tc.lines = true\n\t\tparseArray(c, 0, path[2:])\n\t} else {\n\t\tfor ; i < len(c.json); i++ {\n\t\t\tif c.json[i] == '{' {\n\t\t\t\ti++\n\t\t\t\tparseObject(c, i, path)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif c.json[i] == '[' {\n\t\t\t\ti++\n\t\t\t\tparseArray(c, i, path)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif c.piped {\n\t\tres := c.value.Get(c.pipe)\n\t\tres.Index = 0\n\t\treturn res\n\t}\n\tfillIndex(json, c)\n\treturn c.value\n}\n\n// GetBytes searches json for the specified path.\n// If working with bytes, this method preferred over Get(string(data), path)\nfunc GetBytes(json []byte, path string) Result {\n\treturn getBytes(json, path)\n}\n\n// runeit returns the rune from the the \\uXXXX\nfunc runeit(json string) rune {\n\tn, _ := strconv.ParseUint(json[:4], 16, 64)\n\treturn rune(n)\n}\n\n// unescape unescapes a string\nfunc unescape(json string) string {\n\tvar str = make([]byte, 0, len(json))\n\tfor i := 0; i < len(json); i++ {\n\t\tswitch {\n\t\tdefault:\n\t\t\tstr = append(str, json[i])\n\t\tcase json[i] < ' ':\n\t\t\treturn string(str)\n\t\tcase json[i] == '\\\\':\n\t\t\ti++\n\t\t\tif i >= len(json) {\n\t\t\t\treturn string(str)\n\t\t\t}\n\t\t\tswitch json[i] {\n\t\t\tdefault:\n\t\t\t\treturn string(str)\n\t\t\tcase '\\\\':\n\t\t\t\tstr = append(str, '\\\\')\n\t\t\tcase '/':\n\t\t\t\tstr = append(str, '/')\n\t\t\tcase 'b':\n\t\t\t\tstr = append(str, '\\b')\n\t\t\tcase 'f':\n\t\t\t\tstr = append(str, '\\f')\n\t\t\tcase 'n':\n\t\t\t\tstr = append(str, '\\n')\n\t\t\tcase 'r':\n\t\t\t\tstr = append(str, '\\r')\n\t\t\tcase 't':\n\t\t\t\tstr = append(str, '\\t')\n\t\t\tcase '\"':\n\t\t\t\tstr = append(str, '\"')\n\t\t\tcase 'u':\n\t\t\t\tif i+5 > len(json) {\n\t\t\t\t\treturn string(str)\n\t\t\t\t}\n\t\t\t\tr := runeit(json[i+1:])\n\t\t\t\ti += 5\n\t\t\t\tif utf16.IsSurrogate(r) {\n\t\t\t\t\t// need another code\n\t\t\t\t\tif len(json[i:]) >= 6 && json[i] == '\\\\' &&\n\t\t\t\t\t\tjson[i+1] == 'u' {\n\t\t\t\t\t\t// we expect it to be correct so just consume it\n\t\t\t\t\t\tr = utf16.DecodeRune(r, runeit(json[i+2:]))\n\t\t\t\t\t\ti += 6\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// provide enough space to encode the largest utf8 possible\n\t\t\t\tstr = append(str, 0, 0, 0, 0, 0, 0, 0, 0)\n\t\t\t\tn := utf8.EncodeRune(str[len(str)-8:], r)\n\t\t\t\tstr = str[:len(str)-8+n]\n\t\t\t\ti-- // backtrack index by one\n\t\t\t}\n\t\t}\n\t}\n\treturn string(str)\n}\n\n// Less return true if a token is less than another token.\n// The caseSensitive parameter is used when the tokens are Strings.\n// The order when comparing two different type is:\n//\n//\tNull < False < Number < String < True < JSON\nfunc (t Result) Less(token Result, caseSensitive bool) bool {\n\tif t.Type < token.Type {\n\t\treturn true\n\t}\n\tif t.Type > token.Type {\n\t\treturn false\n\t}\n\tif t.Type == String {\n\t\tif caseSensitive {\n\t\t\treturn t.Str < token.Str\n\t\t}\n\t\treturn stringLessInsensitive(t.Str, token.Str)\n\t}\n\tif t.Type == Number {\n\t\treturn t.Num < token.Num\n\t}\n\treturn t.Raw < token.Raw\n}\n\nfunc stringLessInsensitive(a, b string) bool {\n\tfor i := 0; i < len(a) && i < len(b); i++ {\n\t\tif a[i] >= 'A' && a[i] <= 'Z' {\n\t\t\tif b[i] >= 'A' && b[i] <= 'Z' {\n\t\t\t\t// both are uppercase, do nothing\n\t\t\t\tif a[i] < b[i] {\n\t\t\t\t\treturn true\n\t\t\t\t} else if a[i] > b[i] {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// a is uppercase, convert a to lowercase\n\t\t\t\tif a[i]+32 < b[i] {\n\t\t\t\t\treturn true\n\t\t\t\t} else if a[i]+32 > b[i] {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t} else if b[i] >= 'A' && b[i] <= 'Z' {\n\t\t\t// b is uppercase, convert b to lowercase\n\t\t\tif a[i] < b[i]+32 {\n\t\t\t\treturn true\n\t\t\t} else if a[i] > b[i]+32 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t} else {\n\t\t\t// neither are uppercase\n\t\t\tif a[i] < b[i] {\n\t\t\t\treturn true\n\t\t\t} else if a[i] > b[i] {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn len(a) < len(b)\n}\n\n// parseAny parses the next value from a json string.\n// A Result is returned when the hit param is set.\n// The return values are (i int, res Result, ok bool)\nfunc parseAny(json string, i int, hit bool) (int, Result, bool) {\n\tvar res Result\n\tvar val string\n\tfor ; i < len(json); i++ {\n\t\tif json[i] == '{' || json[i] == '[' {\n\t\t\ti, val = parseSquash(json, i)\n\t\t\tif hit {\n\t\t\t\tres.Raw = val\n\t\t\t\tres.Type = JSON\n\t\t\t}\n\t\t\tvar tmp parseContext\n\t\t\ttmp.value = res\n\t\t\tfillIndex(json, &tmp)\n\t\t\treturn i, tmp.value, true\n\t\t}\n\t\tif json[i] <= ' ' {\n\t\t\tcontinue\n\t\t}\n\t\tvar num bool\n\t\tswitch json[i] {\n\t\tcase '\"':\n\t\t\ti++\n\t\t\tvar vesc bool\n\t\t\tvar ok bool\n\t\t\ti, val, vesc, ok = parseString(json, i)\n\t\t\tif !ok {\n\t\t\t\treturn i, res, false\n\t\t\t}\n\t\t\tif hit {\n\t\t\t\tres.Type = String\n\t\t\t\tres.Raw = val\n\t\t\t\tif vesc {\n\t\t\t\t\tres.Str = unescape(val[1 : len(val)-1])\n\t\t\t\t} else {\n\t\t\t\t\tres.Str = val[1 : len(val)-1]\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn i, res, true\n\t\tcase 'n':\n\t\t\tif i+1 < len(json) && json[i+1] != 'u' {\n\t\t\t\tnum = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfallthrough\n\t\tcase 't', 'f':\n\t\t\tvc := json[i]\n\t\t\ti, val = parseLiteral(json, i)\n\t\t\tif hit {\n\t\t\t\tres.Raw = val\n\t\t\t\tswitch vc {\n\t\t\t\tcase 't':\n\t\t\t\t\tres.Type = True\n\t\t\t\tcase 'f':\n\t\t\t\t\tres.Type = False\n\t\t\t\t}\n\t\t\t\treturn i, res, true\n\t\t\t}\n\t\tcase '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n\t\t\t'i', 'I', 'N':\n\t\t\tnum = true\n\t\t}\n\t\tif num {\n\t\t\ti, val = parseNumber(json, i)\n\t\t\tif hit {\n\t\t\t\tres.Raw = val\n\t\t\t\tres.Type = Number\n\t\t\t\tres.Num, _ = strconv.ParseFloat(val, 64)\n\t\t\t}\n\t\t\treturn i, res, true\n\t\t}\n\n\t}\n\treturn i, res, false\n}\n\n// GetMany searches json for the multiple paths.\n// The return value is a Result array where the number of items\n// will be equal to the number of input paths.\nfunc GetMany(json string, path ...string) []Result {\n\tres := make([]Result, len(path))\n\tfor i, path := range path {\n\t\tres[i] = Get(json, path)\n\t}\n\treturn res\n}\n\n// GetManyBytes searches json for the multiple paths.\n// The return value is a Result array where the number of items\n// will be equal to the number of input paths.\nfunc GetManyBytes(json []byte, path ...string) []Result {\n\tres := make([]Result, len(path))\n\tfor i, path := range path {\n\t\tres[i] = GetBytes(json, path)\n\t}\n\treturn res\n}\n\nfunc validpayload(data []byte, i int) (outi int, ok bool) {\n\tfor ; i < len(data); i++ {\n\t\tswitch data[i] {\n\t\tdefault:\n\t\t\ti, ok = validany(data, i)\n\t\t\tif !ok {\n\t\t\t\treturn i, false\n\t\t\t}\n\t\t\tfor ; i < len(data); i++ {\n\t\t\t\tswitch data[i] {\n\t\t\t\tdefault:\n\t\t\t\t\treturn i, false\n\t\t\t\tcase ' ', '\\t', '\\n', '\\r':\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn i, true\n\t\tcase ' ', '\\t', '\\n', '\\r':\n\t\t\tcontinue\n\t\t}\n\t}\n\treturn i, false\n}\nfunc validany(data []byte, i int) (outi int, ok bool) {\n\tfor ; i < len(data); i++ {\n\t\tswitch data[i] {\n\t\tdefault:\n\t\t\treturn i, false\n\t\tcase ' ', '\\t', '\\n', '\\r':\n\t\t\tcontinue\n\t\tcase '{':\n\t\t\treturn validobject(data, i+1)\n\t\tcase '[':\n\t\t\treturn validarray(data, i+1)\n\t\tcase '\"':\n\t\t\treturn validstring(data, i+1)\n\t\tcase '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':\n\t\t\treturn validnumber(data, i+1)\n\t\tcase 't':\n\t\t\treturn validtrue(data, i+1)\n\t\tcase 'f':\n\t\t\treturn validfalse(data, i+1)\n\t\tcase 'n':\n\t\t\treturn validnull(data, i+1)\n\t\t}\n\t}\n\treturn i, false\n}\nfunc validobject(data []byte, i int) (outi int, ok bool) {\n\tfor ; i < len(data); i++ {\n\t\tswitch data[i] {\n\t\tdefault:\n\t\t\treturn i, false\n\t\tcase ' ', '\\t', '\\n', '\\r':\n\t\t\tcontinue\n\t\tcase '}':\n\t\t\treturn i + 1, true\n\t\tcase '\"':\n\t\tkey:\n\t\t\tif i, ok = validstring(data, i+1); !ok {\n\t\t\t\treturn i, false\n\t\t\t}\n\t\t\tif i, ok = validcolon(data, i); !ok {\n\t\t\t\treturn i, false\n\t\t\t}\n\t\t\tif i, ok = validany(data, i); !ok {\n\t\t\t\treturn i, false\n\t\t\t}\n\t\t\tif i, ok = validcomma(data, i, '}'); !ok {\n\t\t\t\treturn i, false\n\t\t\t}\n\t\t\tif data[i] == '}' {\n\t\t\t\treturn i + 1, true\n\t\t\t}\n\t\t\ti++\n\t\t\tfor ; i < len(data); i++ {\n\t\t\t\tswitch data[i] {\n\t\t\t\tdefault:\n\t\t\t\t\treturn i, false\n\t\t\t\tcase ' ', '\\t', '\\n', '\\r':\n\t\t\t\t\tcontinue\n\t\t\t\tcase '\"':\n\t\t\t\t\tgoto key\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn i, false\n\t\t}\n\t}\n\treturn i, false\n}\nfunc validcolon(data []byte, i int) (outi int, ok bool) {\n\tfor ; i < len(data); i++ {\n\t\tswitch data[i] {\n\t\tdefault:\n\t\t\treturn i, false\n\t\tcase ' ', '\\t', '\\n', '\\r':\n\t\t\tcontinue\n\t\tcase ':':\n\t\t\treturn i + 1, true\n\t\t}\n\t}\n\treturn i, false\n}\nfunc validcomma(data []byte, i int, end byte) (outi int, ok bool) {\n\tfor ; i < len(data); i++ {\n\t\tswitch data[i] {\n\t\tdefault:\n\t\t\treturn i, false\n\t\tcase ' ', '\\t', '\\n', '\\r':\n\t\t\tcontinue\n\t\tcase ',':\n\t\t\treturn i, true\n\t\tcase end:\n\t\t\treturn i, true\n\t\t}\n\t}\n\treturn i, false\n}\nfunc validarray(data []byte, i int) (outi int, ok bool) {\n\tfor ; i < len(data); i++ {\n\t\tswitch data[i] {\n\t\tdefault:\n\t\t\tfor ; i < len(data); i++ {\n\t\t\t\tif i, ok = validany(data, i); !ok {\n\t\t\t\t\treturn i, false\n\t\t\t\t}\n\t\t\t\tif i, ok = validcomma(data, i, ']'); !ok {\n\t\t\t\t\treturn i, false\n\t\t\t\t}\n\t\t\t\tif data[i] == ']' {\n\t\t\t\t\treturn i + 1, true\n\t\t\t\t}\n\t\t\t}\n\t\tcase ' ', '\\t', '\\n', '\\r':\n\t\t\tcontinue\n\t\tcase ']':\n\t\t\treturn i + 1, true\n\t\t}\n\t}\n\treturn i, false\n}\nfunc validstring(data []byte, i int) (outi int, ok bool) {\n\tfor ; i < len(data); i++ {\n\t\tif data[i] < ' ' {\n\t\t\treturn i, false\n\t\t} else if data[i] == '\\\\' {\n\t\t\ti++\n\t\t\tif i == len(data) {\n\t\t\t\treturn i, false\n\t\t\t}\n\t\t\tswitch data[i] {\n\t\t\tdefault:\n\t\t\t\treturn i, false\n\t\t\tcase '\"', '\\\\', '/', 'b', 'f', 'n', 'r', 't':\n\t\t\tcase 'u':\n\t\t\t\tfor j := 0; j < 4; j++ {\n\t\t\t\t\ti++\n\t\t\t\t\tif i >= len(data) {\n\t\t\t\t\t\treturn i, false\n\t\t\t\t\t}\n\t\t\t\t\tif !((data[i] >= '0' && data[i] <= '9') ||\n\t\t\t\t\t\t(data[i] >= 'a' && data[i] <= 'f') ||\n\t\t\t\t\t\t(data[i] >= 'A' && data[i] <= 'F')) {\n\t\t\t\t\t\treturn i, false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if data[i] == '\"' {\n\t\t\treturn i + 1, true\n\t\t}\n\t}\n\treturn i, false\n}\nfunc validnumber(data []byte, i int) (outi int, ok bool) {\n\ti--\n\t// sign\n\tif data[i] == '-' {\n\t\ti++\n\t\tif i == len(data) {\n\t\t\treturn i, false\n\t\t}\n\t\tif data[i] < '0' || data[i] > '9' {\n\t\t\treturn i, false\n\t\t}\n\t}\n\t// int\n\tif i == len(data) {\n\t\treturn i, false\n\t}\n\tif data[i] == '0' {\n\t\ti++\n\t} else {\n\t\tfor ; i < len(data); i++ {\n\t\t\tif data[i] >= '0' && data[i] <= '9' {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\t// frac\n\tif i == len(data) {\n\t\treturn i, true\n\t}\n\tif data[i] == '.' {\n\t\ti++\n\t\tif i == len(data) {\n\t\t\treturn i, false\n\t\t}\n\t\tif data[i] < '0' || data[i] > '9' {\n\t\t\treturn i, false\n\t\t}\n\t\ti++\n\t\tfor ; i < len(data); i++ {\n\t\t\tif data[i] >= '0' && data[i] <= '9' {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\t// exp\n\tif i == len(data) {\n\t\treturn i, true\n\t}\n\tif data[i] == 'e' || data[i] == 'E' {\n\t\ti++\n\t\tif i == len(data) {\n\t\t\treturn i, false\n\t\t}\n\t\tif data[i] == '+' || data[i] == '-' {\n\t\t\ti++\n\t\t}\n\t\tif i == len(data) {\n\t\t\treturn i, false\n\t\t}\n\t\tif data[i] < '0' || data[i] > '9' {\n\t\t\treturn i, false\n\t\t}\n\t\ti++\n\t\tfor ; i < len(data); i++ {\n\t\t\tif data[i] >= '0' && data[i] <= '9' {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\treturn i, true\n}\n\nfunc validtrue(data []byte, i int) (outi int, ok bool) {\n\tif i+3 <= len(data) && data[i] == 'r' && data[i+1] == 'u' &&\n\t\tdata[i+2] == 'e' {\n\t\treturn i + 3, true\n\t}\n\treturn i, false\n}\nfunc validfalse(data []byte, i int) (outi int, ok bool) {\n\tif i+4 <= len(data) && data[i] == 'a' && data[i+1] == 'l' &&\n\t\tdata[i+2] == 's' && data[i+3] == 'e' {\n\t\treturn i + 4, true\n\t}\n\treturn i, false\n}\nfunc validnull(data []byte, i int) (outi int, ok bool) {\n\tif i+3 <= len(data) && data[i] == 'u' && data[i+1] == 'l' &&\n\t\tdata[i+2] == 'l' {\n\t\treturn i + 3, true\n\t}\n\treturn i, false\n}\n\n// Valid returns true if the input is valid json.\n//\n//\tif !gjson.Valid(json) {\n//\t\treturn errors.New(\"invalid json\")\n//\t}\n//\tvalue := gjson.Get(json, \"name.last\")\nfunc Valid(json string) bool {\n\t_, ok := validpayload(stringBytes(json), 0)\n\treturn ok\n}\n\n// ValidBytes returns true if the input is valid json.\n//\n//\tif !gjson.Valid(json) {\n//\t\treturn errors.New(\"invalid json\")\n//\t}\n//\tvalue := gjson.Get(json, \"name.last\")\n//\n// If working with bytes, this method preferred over ValidBytes(string(data))\nfunc ValidBytes(json []byte) bool {\n\t_, ok := validpayload(json, 0)\n\treturn ok\n}\n\nfunc parseUint(s string) (n uint64, ok bool) {\n\tvar i int\n\tif i == len(s) {\n\t\treturn 0, false\n\t}\n\tfor ; i < len(s); i++ {\n\t\tif s[i] >= '0' && s[i] <= '9' {\n\t\t\tn = n*10 + uint64(s[i]-'0')\n\t\t} else {\n\t\t\treturn 0, false\n\t\t}\n\t}\n\treturn n, true\n}\n\nfunc parseInt(s string) (n int64, ok bool) {\n\tvar i int\n\tvar sign bool\n\tif len(s) > 0 && s[0] == '-' {\n\t\tsign = true\n\t\ti++\n\t}\n\tif i == len(s) {\n\t\treturn 0, false\n\t}\n\tfor ; i < len(s); i++ {\n\t\tif s[i] >= '0' && s[i] <= '9' {\n\t\t\tn = n*10 + int64(s[i]-'0')\n\t\t} else {\n\t\t\treturn 0, false\n\t\t}\n\t}\n\tif sign {\n\t\treturn n * -1, true\n\t}\n\treturn n, true\n}\n\n// safeInt validates a given JSON number\n// ensures it lies within the minimum and maximum representable JSON numbers\nfunc safeInt(f float64) (n int64, ok bool) {\n\t// https://tc39.es/ecma262/#sec-number.min_safe_integer\n\t// https://tc39.es/ecma262/#sec-number.max_safe_integer\n\tif f < -9007199254740991 || f > 9007199254740991 {\n\t\treturn 0, false\n\t}\n\treturn int64(f), true\n}\n\n// execStatic parses the path to find a static value.\n// The input expects that the path already starts with a '!'\nfunc execStatic(json, path string) (pathOut, res string, ok bool) {\n\tname := path[1:]\n\tif len(name) > 0 {\n\t\tswitch name[0] {\n\t\tcase '{', '[', '\"', '+', '-', '0', '1', '2', '3', '4', '5', '6', '7',\n\t\t\t'8', '9':\n\t\t\t_, res = parseSquash(name, 0)\n\t\t\tpathOut = name[len(res):]\n\t\t\treturn pathOut, res, true\n\t\t}\n\t}\n\tfor i := 1; i < len(path); i++ {\n\t\tif path[i] == '|' {\n\t\t\tpathOut = path[i:]\n\t\t\tname = path[1:i]\n\t\t\tbreak\n\t\t}\n\t\tif path[i] == '.' {\n\t\t\tpathOut = path[i:]\n\t\t\tname = path[1:i]\n\t\t\tbreak\n\t\t}\n\t}\n\tswitch strings.ToLower(name) {\n\tcase \"true\", \"false\", \"null\", \"nan\", \"inf\":\n\t\treturn pathOut, name, true\n\t}\n\treturn pathOut, res, false\n}\n\n// execModifier parses the path to find a matching modifier function.\n// The input expects that the path already starts with a '@'\nfunc execModifier(json, path string) (pathOut, res string, ok bool) {\n\tname := path[1:]\n\tvar hasArgs bool\n\tfor i := 1; i < len(path); i++ {\n\t\tif path[i] == ':' {\n\t\t\tpathOut = path[i+1:]\n\t\t\tname = path[1:i]\n\t\t\thasArgs = len(pathOut) > 0\n\t\t\tbreak\n\t\t}\n\t\tif path[i] == '|' {\n\t\t\tpathOut = path[i:]\n\t\t\tname = path[1:i]\n\t\t\tbreak\n\t\t}\n\t\tif path[i] == '.' {\n\t\t\tpathOut = path[i:]\n\t\t\tname = path[1:i]\n\t\t\tbreak\n\t\t}\n\t}\n\tif fn, ok := modifiers[name]; ok {\n\t\tvar args string\n\t\tif hasArgs {\n\t\t\tvar parsedArgs bool\n\t\t\tswitch pathOut[0] {\n\t\t\tcase '{', '[', '\"':\n\t\t\t\t// json arg\n\t\t\t\tres := Parse(pathOut)\n\t\t\t\tif res.Exists() {\n\t\t\t\t\targs = squash(pathOut)\n\t\t\t\t\tpathOut = pathOut[len(args):]\n\t\t\t\t\tparsedArgs = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !parsedArgs {\n\t\t\t\t// simple arg\n\t\t\t\ti := 0\n\t\t\t\tfor ; i < len(pathOut); i++ {\n\t\t\t\t\tif pathOut[i] == '|' {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tswitch pathOut[i] {\n\t\t\t\t\tcase '{', '[', '\"', '(':\n\t\t\t\t\t\ts := squash(pathOut[i:])\n\t\t\t\t\t\ti += len(s) - 1\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\targs = pathOut[:i]\n\t\t\t\tpathOut = pathOut[i:]\n\t\t\t}\n\t\t}\n\t\treturn pathOut, fn(json, args), true\n\t}\n\treturn pathOut, res, false\n}\n\n// unwrap removes the '[]' or '{}' characters around json\nfunc unwrap(json string) string {\n\tjson = trim(json)\n\tif len(json) >= 2 && (json[0] == '[' || json[0] == '{') {\n\t\tjson = json[1 : len(json)-1]\n\t}\n\treturn json\n}\n\n// DisableModifiers will disable the modifier syntax\nvar DisableModifiers = false\n\nvar modifiers map[string]func(json, arg string) string\n\nfunc init() {\n\tmodifiers = map[string]func(json, arg string) string{\n\t\t\"pretty\":  modPretty,\n\t\t\"ugly\":    modUgly,\n\t\t\"reverse\": modReverse,\n\t\t\"this\":    modThis,\n\t\t\"flatten\": modFlatten,\n\t\t\"join\":    modJoin,\n\t\t\"valid\":   modValid,\n\t\t\"keys\":    modKeys,\n\t\t\"values\":  modValues,\n\t\t\"tostr\":   modToStr,\n\t\t\"fromstr\": modFromStr,\n\t\t\"group\":   modGroup,\n\t\t\"dig\":     modDig,\n\t}\n}\n\n// AddModifier binds a custom modifier command to the GJSON syntax.\n// This operation is not thread safe and should be executed prior to\n// using all other gjson function.\nfunc AddModifier(name string, fn func(json, arg string) string) {\n\tmodifiers[name] = fn\n}\n\n// ModifierExists returns true when the specified modifier exists.\nfunc ModifierExists(name string, fn func(json, arg string) string) bool {\n\t_, ok := modifiers[name]\n\treturn ok\n}\n\n// cleanWS remove any non-whitespace from string\nfunc cleanWS(s string) string {\n\tfor i := 0; i < len(s); i++ {\n\t\tswitch s[i] {\n\t\tcase ' ', '\\t', '\\n', '\\r':\n\t\t\tcontinue\n\t\tdefault:\n\t\t\tvar s2 []byte\n\t\t\tfor i := 0; i < len(s); i++ {\n\t\t\t\tswitch s[i] {\n\t\t\t\tcase ' ', '\\t', '\\n', '\\r':\n\t\t\t\t\ts2 = append(s2, s[i])\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn string(s2)\n\t\t}\n\t}\n\treturn s\n}\n\n// @pretty modifier makes the json look nice.\nfunc modPretty(json, arg string) string {\n\tif len(arg) > 0 {\n\t\topts := *pretty.DefaultOptions\n\t\tParse(arg).ForEach(func(key, value Result) bool {\n\t\t\tswitch key.String() {\n\t\t\tcase \"sortKeys\":\n\t\t\t\topts.SortKeys = value.Bool()\n\t\t\tcase \"indent\":\n\t\t\t\topts.Indent = cleanWS(value.String())\n\t\t\tcase \"prefix\":\n\t\t\t\topts.Prefix = cleanWS(value.String())\n\t\t\tcase \"width\":\n\t\t\t\topts.Width = int(value.Int())\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t\treturn bytesString(pretty.PrettyOptions(stringBytes(json), &opts))\n\t}\n\treturn bytesString(pretty.Pretty(stringBytes(json)))\n}\n\n// @this returns the current element. Can be used to retrieve the root element.\nfunc modThis(json, arg string) string {\n\treturn json\n}\n\n// @ugly modifier removes all whitespace.\nfunc modUgly(json, arg string) string {\n\treturn bytesString(pretty.Ugly(stringBytes(json)))\n}\n\n// @reverse reverses array elements or root object members.\nfunc modReverse(json, arg string) string {\n\tres := Parse(json)\n\tif res.IsArray() {\n\t\tvar values []Result\n\t\tres.ForEach(func(_, value Result) bool {\n\t\t\tvalues = append(values, value)\n\t\t\treturn true\n\t\t})\n\t\tout := make([]byte, 0, len(json))\n\t\tout = append(out, '[')\n\t\tfor i, j := len(values)-1, 0; i >= 0; i, j = i-1, j+1 {\n\t\t\tif j > 0 {\n\t\t\t\tout = append(out, ',')\n\t\t\t}\n\t\t\tout = append(out, values[i].Raw...)\n\t\t}\n\t\tout = append(out, ']')\n\t\treturn bytesString(out)\n\t}\n\tif res.IsObject() {\n\t\tvar keyValues []Result\n\t\tres.ForEach(func(key, value Result) bool {\n\t\t\tkeyValues = append(keyValues, key, value)\n\t\t\treturn true\n\t\t})\n\t\tout := make([]byte, 0, len(json))\n\t\tout = append(out, '{')\n\t\tfor i, j := len(keyValues)-2, 0; i >= 0; i, j = i-2, j+1 {\n\t\t\tif j > 0 {\n\t\t\t\tout = append(out, ',')\n\t\t\t}\n\t\t\tout = append(out, keyValues[i+0].Raw...)\n\t\t\tout = append(out, ':')\n\t\t\tout = append(out, keyValues[i+1].Raw...)\n\t\t}\n\t\tout = append(out, '}')\n\t\treturn bytesString(out)\n\t}\n\treturn json\n}\n\n// @flatten an array with child arrays.\n//\n//\t[1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,[6,7]]\n//\n// The {\"deep\":true} arg can be provide for deep flattening.\n//\n//\t[1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,6,7]\n//\n// The original json is returned when the json is not an array.\nfunc modFlatten(json, arg string) string {\n\tres := Parse(json)\n\tif !res.IsArray() {\n\t\treturn json\n\t}\n\tvar deep bool\n\tif arg != \"\" {\n\t\tParse(arg).ForEach(func(key, value Result) bool {\n\t\t\tif key.String() == \"deep\" {\n\t\t\t\tdeep = value.Bool()\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}\n\tvar out []byte\n\tout = append(out, '[')\n\tvar idx int\n\tres.ForEach(func(_, value Result) bool {\n\t\tvar raw string\n\t\tif value.IsArray() {\n\t\t\tif deep {\n\t\t\t\traw = unwrap(modFlatten(value.Raw, arg))\n\t\t\t} else {\n\t\t\t\traw = unwrap(value.Raw)\n\t\t\t}\n\t\t} else {\n\t\t\traw = value.Raw\n\t\t}\n\t\traw = strings.TrimSpace(raw)\n\t\tif len(raw) > 0 {\n\t\t\tif idx > 0 {\n\t\t\t\tout = append(out, ',')\n\t\t\t}\n\t\t\tout = append(out, raw...)\n\t\t\tidx++\n\t\t}\n\t\treturn true\n\t})\n\tout = append(out, ']')\n\treturn bytesString(out)\n}\n\n// @keys extracts the keys from an object.\n//\n//\t{\"first\":\"Tom\",\"last\":\"Smith\"} -> [\"first\",\"last\"]\nfunc modKeys(json, arg string) string {\n\tv := Parse(json)\n\tif !v.Exists() {\n\t\treturn \"[]\"\n\t}\n\tobj := v.IsObject()\n\tvar out strings.Builder\n\tout.WriteByte('[')\n\tvar i int\n\tv.ForEach(func(key, _ Result) bool {\n\t\tif i > 0 {\n\t\t\tout.WriteByte(',')\n\t\t}\n\t\tif obj {\n\t\t\tout.WriteString(key.Raw)\n\t\t} else {\n\t\t\tout.WriteString(\"null\")\n\t\t}\n\t\ti++\n\t\treturn true\n\t})\n\tout.WriteByte(']')\n\treturn out.String()\n}\n\n// @values extracts the values from an object.\n//\n//\t{\"first\":\"Tom\",\"last\":\"Smith\"} -> [\"Tom\",\"Smith\"]\nfunc modValues(json, arg string) string {\n\tv := Parse(json)\n\tif !v.Exists() {\n\t\treturn \"[]\"\n\t}\n\tif v.IsArray() {\n\t\treturn json\n\t}\n\tvar out strings.Builder\n\tout.WriteByte('[')\n\tvar i int\n\tv.ForEach(func(_, value Result) bool {\n\t\tif i > 0 {\n\t\t\tout.WriteByte(',')\n\t\t}\n\t\tout.WriteString(value.Raw)\n\t\ti++\n\t\treturn true\n\t})\n\tout.WriteByte(']')\n\treturn out.String()\n}\n\n// @join multiple objects into a single object.\n//\n//\t[{\"first\":\"Tom\"},{\"last\":\"Smith\"}] -> {\"first\",\"Tom\",\"last\":\"Smith\"}\n//\n// The arg can be \"true\" to specify that duplicate keys should be preserved.\n//\n//\t[{\"first\":\"Tom\",\"age\":37},{\"age\":41}] -> {\"first\",\"Tom\",\"age\":37,\"age\":41}\n//\n// Without preserved keys:\n//\n//\t[{\"first\":\"Tom\",\"age\":37},{\"age\":41}] -> {\"first\",\"Tom\",\"age\":41}\n//\n// The original json is returned when the json is not an object.\nfunc modJoin(json, arg string) string {\n\tres := Parse(json)\n\tif !res.IsArray() {\n\t\treturn json\n\t}\n\tvar preserve bool\n\tif arg != \"\" {\n\t\tParse(arg).ForEach(func(key, value Result) bool {\n\t\t\tif key.String() == \"preserve\" {\n\t\t\t\tpreserve = value.Bool()\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}\n\tvar out []byte\n\tout = append(out, '{')\n\tif preserve {\n\t\t// Preserve duplicate keys.\n\t\tvar idx int\n\t\tres.ForEach(func(_, value Result) bool {\n\t\t\tif !value.IsObject() {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif idx > 0 {\n\t\t\t\tout = append(out, ',')\n\t\t\t}\n\t\t\tout = append(out, unwrap(value.Raw)...)\n\t\t\tidx++\n\t\t\treturn true\n\t\t})\n\t} else {\n\t\t// Deduplicate keys and generate an object with stable ordering.\n\t\tvar keys []Result\n\t\tkvals := make(map[string]Result)\n\t\tres.ForEach(func(_, value Result) bool {\n\t\t\tif !value.IsObject() {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tvalue.ForEach(func(key, value Result) bool {\n\t\t\t\tk := key.String()\n\t\t\t\tif _, ok := kvals[k]; !ok {\n\t\t\t\t\tkeys = append(keys, key)\n\t\t\t\t}\n\t\t\t\tkvals[k] = value\n\t\t\t\treturn true\n\t\t\t})\n\t\t\treturn true\n\t\t})\n\t\tfor i := 0; i < len(keys); i++ {\n\t\t\tif i > 0 {\n\t\t\t\tout = append(out, ',')\n\t\t\t}\n\t\t\tout = append(out, keys[i].Raw...)\n\t\t\tout = append(out, ':')\n\t\t\tout = append(out, kvals[keys[i].String()].Raw...)\n\t\t}\n\t}\n\tout = append(out, '}')\n\treturn bytesString(out)\n}\n\n// @valid ensures that the json is valid before moving on. An empty string is\n// returned when the json is not valid, otherwise it returns the original json.\nfunc modValid(json, arg string) string {\n\tif !Valid(json) {\n\t\treturn \"\"\n\t}\n\treturn json\n}\n\n// @fromstr converts a string to json\n//\n//\t\"{\\\"id\\\":1023,\\\"name\\\":\\\"alert\\\"}\" -> {\"id\":1023,\"name\":\"alert\"}\nfunc modFromStr(json, arg string) string {\n\tif !Valid(json) {\n\t\treturn \"\"\n\t}\n\treturn Parse(json).String()\n}\n\n// @tostr converts a string to json\n//\n//\t{\"id\":1023,\"name\":\"alert\"} -> \"{\\\"id\\\":1023,\\\"name\\\":\\\"alert\\\"}\"\nfunc modToStr(str, arg string) string {\n\treturn string(AppendJSONString(nil, str))\n}\n\nfunc modGroup(json, arg string) string {\n\tres := Parse(json)\n\tif !res.IsObject() {\n\t\treturn \"\"\n\t}\n\tvar all [][]byte\n\tres.ForEach(func(key, value Result) bool {\n\t\tif !value.IsArray() {\n\t\t\treturn true\n\t\t}\n\t\tvar idx int\n\t\tvalue.ForEach(func(_, value Result) bool {\n\t\t\tif idx == len(all) {\n\t\t\t\tall = append(all, []byte{})\n\t\t\t}\n\t\t\tall[idx] = append(all[idx], (\",\" + key.Raw + \":\" + value.Raw)...)\n\t\t\tidx++\n\t\t\treturn true\n\t\t})\n\t\treturn true\n\t})\n\tvar data []byte\n\tdata = append(data, '[')\n\tfor i, item := range all {\n\t\tif i > 0 {\n\t\t\tdata = append(data, ',')\n\t\t}\n\t\tdata = append(data, '{')\n\t\tdata = append(data, item[1:]...)\n\t\tdata = append(data, '}')\n\t}\n\tdata = append(data, ']')\n\treturn string(data)\n}\n\n// stringHeader instead of reflect.StringHeader\ntype stringHeader struct {\n\tdata unsafe.Pointer\n\tlen  int\n}\n\n// sliceHeader instead of reflect.SliceHeader\ntype sliceHeader struct {\n\tdata unsafe.Pointer\n\tlen  int\n\tcap  int\n}\n\n// getBytes casts the input json bytes to a string and safely returns the\n// results as uniquely allocated data. This operation is intended to minimize\n// copies and allocations for the large json string->[]byte.\nfunc getBytes(json []byte, path string) Result {\n\tvar result Result\n\tif json != nil {\n\t\t// unsafe cast to string\n\t\tresult = Get(*(*string)(unsafe.Pointer(&json)), path)\n\t\t// safely get the string headers\n\t\trawhi := *(*stringHeader)(unsafe.Pointer(&result.Raw))\n\t\tstrhi := *(*stringHeader)(unsafe.Pointer(&result.Str))\n\t\t// create byte slice headers\n\t\trawh := sliceHeader{data: rawhi.data, len: rawhi.len, cap: rawhi.len}\n\t\tstrh := sliceHeader{data: strhi.data, len: strhi.len, cap: rawhi.len}\n\t\tif strh.data == nil {\n\t\t\t// str is nil\n\t\t\tif rawh.data == nil {\n\t\t\t\t// raw is nil\n\t\t\t\tresult.Raw = \"\"\n\t\t\t} else {\n\t\t\t\t// raw has data, safely copy the slice header to a string\n\t\t\t\tresult.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))\n\t\t\t}\n\t\t\tresult.Str = \"\"\n\t\t} else if rawh.data == nil {\n\t\t\t// raw is nil\n\t\t\tresult.Raw = \"\"\n\t\t\t// str has data, safely copy the slice header to a string\n\t\t\tresult.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))\n\t\t} else if uintptr(strh.data) >= uintptr(rawh.data) &&\n\t\t\tuintptr(strh.data)+uintptr(strh.len) <=\n\t\t\t\tuintptr(rawh.data)+uintptr(rawh.len) {\n\t\t\t// Str is a substring of Raw.\n\t\t\tstart := uintptr(strh.data) - uintptr(rawh.data)\n\t\t\t// safely copy the raw slice header\n\t\t\tresult.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))\n\t\t\t// substring the raw\n\t\t\tresult.Str = result.Raw[start : start+uintptr(strh.len)]\n\t\t} else {\n\t\t\t// safely copy both the raw and str slice headers to strings\n\t\t\tresult.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))\n\t\t\tresult.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))\n\t\t}\n\t}\n\treturn result\n}\n\n// fillIndex finds the position of Raw data and assigns it to the Index field\n// of the resulting value. If the position cannot be found then Index zero is\n// used instead.\nfunc fillIndex(json string, c *parseContext) {\n\tif len(c.value.Raw) > 0 && !c.calcd {\n\t\tjhdr := *(*stringHeader)(unsafe.Pointer(&json))\n\t\trhdr := *(*stringHeader)(unsafe.Pointer(&(c.value.Raw)))\n\t\tc.value.Index = int(uintptr(rhdr.data) - uintptr(jhdr.data))\n\t\tif c.value.Index < 0 || c.value.Index >= len(json) {\n\t\t\tc.value.Index = 0\n\t\t}\n\t}\n}\n\nfunc stringBytes(s string) []byte {\n\treturn *(*[]byte)(unsafe.Pointer(&sliceHeader{\n\t\tdata: (*stringHeader)(unsafe.Pointer(&s)).data,\n\t\tlen:  len(s),\n\t\tcap:  len(s),\n\t}))\n}\n\nfunc bytesString(b []byte) string {\n\treturn *(*string)(unsafe.Pointer(&b))\n}\n\nfunc revSquash(json string) string {\n\t// reverse squash\n\t// expects that the tail character is a ']' or '}' or ')' or '\"'\n\t// squash the value, ignoring all nested arrays and objects.\n\ti := len(json) - 1\n\tvar depth int\n\tif json[i] != '\"' {\n\t\tdepth++\n\t}\n\tif json[i] == '}' || json[i] == ']' || json[i] == ')' {\n\t\ti--\n\t}\n\tfor ; i >= 0; i-- {\n\t\tswitch json[i] {\n\t\tcase '\"':\n\t\t\ti--\n\t\t\tfor ; i >= 0; i-- {\n\t\t\t\tif json[i] == '\"' {\n\t\t\t\t\tesc := 0\n\t\t\t\t\tfor i > 0 && json[i-1] == '\\\\' {\n\t\t\t\t\t\ti--\n\t\t\t\t\t\tesc++\n\t\t\t\t\t}\n\t\t\t\t\tif esc%2 == 1 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\ti += esc\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif depth == 0 {\n\t\t\t\tif i < 0 {\n\t\t\t\t\ti = 0\n\t\t\t\t}\n\t\t\t\treturn json[i:]\n\t\t\t}\n\t\tcase '}', ']', ')':\n\t\t\tdepth++\n\t\tcase '{', '[', '(':\n\t\t\tdepth--\n\t\t\tif depth == 0 {\n\t\t\t\treturn json[i:]\n\t\t\t}\n\t\t}\n\t}\n\treturn json\n}\n\n// Paths returns the original GJSON paths for a Result where the Result came\n// from a simple query path that returns an array, like:\n//\n//\tgjson.Get(json, \"friends.#.first\")\n//\n// The returned value will be in the form of a JSON array:\n//\n//\t[\"friends.0.first\",\"friends.1.first\",\"friends.2.first\"]\n//\n// The param 'json' must be the original JSON used when calling Get.\n//\n// Returns an empty string if the paths cannot be determined, which can happen\n// when the Result came from a path that contained a multipath, modifier,\n// or a nested query.\nfunc (t Result) Paths(json string) []string {\n\tif t.Indexes == nil {\n\t\treturn nil\n\t}\n\tpaths := make([]string, 0, len(t.Indexes))\n\tt.ForEach(func(_, value Result) bool {\n\t\tpaths = append(paths, value.Path(json))\n\t\treturn true\n\t})\n\tif len(paths) != len(t.Indexes) {\n\t\treturn nil\n\t}\n\treturn paths\n}\n\n// Path returns the original GJSON path for a Result where the Result came\n// from a simple path that returns a single value, like:\n//\n//\tgjson.Get(json, \"friends.#(last=Murphy)\")\n//\n// The returned value will be in the form of a JSON string:\n//\n//\t\"friends.0\"\n//\n// The param 'json' must be the original JSON used when calling Get.\n//\n// Returns an empty string if the paths cannot be determined, which can happen\n// when the Result came from a path that contained a multipath, modifier,\n// or a nested query.\nfunc (t Result) Path(json string) string {\n\tvar path []byte\n\tvar comps []string // raw components\n\ti := t.Index - 1\n\tif t.Index+len(t.Raw) > len(json) {\n\t\t// JSON cannot safely contain Result.\n\t\tgoto fail\n\t}\n\tif !strings.HasPrefix(json[t.Index:], t.Raw) {\n\t\t// Result is not at the JSON index as expected.\n\t\tgoto fail\n\t}\n\tfor ; i >= 0; i-- {\n\t\tif json[i] <= ' ' {\n\t\t\tcontinue\n\t\t}\n\t\tif json[i] == ':' {\n\t\t\t// inside of object, get the key\n\t\t\tfor ; i >= 0; i-- {\n\t\t\t\tif json[i] != '\"' {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\traw := revSquash(json[:i+1])\n\t\t\ti = i - len(raw)\n\t\t\tcomps = append(comps, raw)\n\t\t\t// key gotten, now squash the rest\n\t\t\traw = revSquash(json[:i+1])\n\t\t\ti = i - len(raw)\n\t\t\ti++ // increment the index for next loop step\n\t\t} else if json[i] == '{' {\n\t\t\t// Encountered an open object. The original result was probably an\n\t\t\t// object key.\n\t\t\tgoto fail\n\t\t} else if json[i] == ',' || json[i] == '[' {\n\t\t\t// inside of an array, count the position\n\t\t\tvar arrIdx int\n\t\t\tif json[i] == ',' {\n\t\t\t\tarrIdx++\n\t\t\t\ti--\n\t\t\t}\n\t\t\tfor ; i >= 0; i-- {\n\t\t\t\tif json[i] == ':' {\n\t\t\t\t\t// Encountered an unexpected colon. The original result was\n\t\t\t\t\t// probably an object key.\n\t\t\t\t\tgoto fail\n\t\t\t\t} else if json[i] == ',' {\n\t\t\t\t\tarrIdx++\n\t\t\t\t} else if json[i] == '[' {\n\t\t\t\t\tcomps = append(comps, strconv.Itoa(arrIdx))\n\t\t\t\t\tbreak\n\t\t\t\t} else if json[i] == ']' || json[i] == '}' || json[i] == '\"' {\n\t\t\t\t\traw := revSquash(json[:i+1])\n\t\t\t\t\ti = i - len(raw) + 1\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif len(comps) == 0 {\n\t\tif DisableModifiers {\n\t\t\tgoto fail\n\t\t}\n\t\treturn \"@this\"\n\t}\n\tfor i := len(comps) - 1; i >= 0; i-- {\n\t\trcomp := Parse(comps[i])\n\t\tif !rcomp.Exists() {\n\t\t\tgoto fail\n\t\t}\n\t\tcomp := Escape(rcomp.String())\n\t\tpath = append(path, '.')\n\t\tpath = append(path, comp...)\n\t}\n\tif len(path) > 0 {\n\t\tpath = path[1:]\n\t}\n\treturn string(path)\nfail:\n\treturn \"\"\n}\n\n// isSafePathKeyChar returns true if the input character is safe for not\n// needing escaping.\nfunc isSafePathKeyChar(c byte) bool {\n\treturn (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||\n\t\t(c >= '0' && c <= '9') || c <= ' ' || c > '~' || c == '_' ||\n\t\tc == '-' || c == ':'\n}\n\n// Escape returns an escaped path component.\n//\n//\tjson := `{\n//\t  \"user\":{\n//\t     \"first.name\": \"Janet\",\n//\t     \"last.name\": \"Prichard\"\n//\t   }\n//\t}`\n//\tuser := gjson.Get(json, \"user\")\n//\tprintln(user.Get(gjson.Escape(\"first.name\"))\n//\tprintln(user.Get(gjson.Escape(\"last.name\"))\n//\t// Output:\n//\t// Janet\n//\t// Prichard\nfunc Escape(comp string) string {\n\tfor i := 0; i < len(comp); i++ {\n\t\tif !isSafePathKeyChar(comp[i]) {\n\t\t\tncomp := make([]byte, len(comp)+1)\n\t\t\tcopy(ncomp, comp[:i])\n\t\t\tncomp = ncomp[:i]\n\t\t\tfor ; i < len(comp); i++ {\n\t\t\t\tif !isSafePathKeyChar(comp[i]) {\n\t\t\t\t\tncomp = append(ncomp, '\\\\')\n\t\t\t\t}\n\t\t\t\tncomp = append(ncomp, comp[i])\n\t\t\t}\n\t\t\treturn string(ncomp)\n\t\t}\n\t}\n\treturn comp\n}\n\nfunc parseRecursiveDescent(all []Result, parent Result, path string) []Result {\n\tif res := parent.Get(path); res.Exists() {\n\t\tall = append(all, res)\n\t}\n\tif parent.IsArray() || parent.IsObject() {\n\t\tparent.ForEach(func(_, val Result) bool {\n\t\t\tall = parseRecursiveDescent(all, val, path)\n\t\t\treturn true\n\t\t})\n\t}\n\treturn all\n}\n\nfunc modDig(json, arg string) string {\n\tall := parseRecursiveDescent(nil, Parse(json), arg)\n\tvar out []byte\n\tout = append(out, '[')\n\tfor i, res := range all {\n\t\tif i > 0 {\n\t\t\tout = append(out, ',')\n\t\t}\n\t\tout = append(out, res.Raw...)\n\t}\n\tout = append(out, ']')\n\treturn string(out)\n}\n"
  },
  {
    "path": "gjson_test.go",
    "content": "package gjson\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"math/rand\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/tidwall/pretty\"\n)\n\n// TestRandomData is a fuzzing test that throws random data at the Parse\n// function looking for panics.\nfunc TestRandomData(t *testing.T) {\n\tvar lstr string\n\tdefer func() {\n\t\tif v := recover(); v != nil {\n\t\t\tprintln(\"'\" + hex.EncodeToString([]byte(lstr)) + \"'\")\n\t\t\tprintln(\"'\" + lstr + \"'\")\n\t\t\tpanic(v)\n\t\t}\n\t}()\n\trand.Seed(time.Now().UnixNano())\n\tb := make([]byte, 200)\n\tfor i := 0; i < 2000000; i++ {\n\t\tn, err := rand.Read(b[:rand.Int()%len(b)])\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tlstr = string(b[:n])\n\t\tGetBytes([]byte(lstr), \"zzzz\")\n\t\tParse(lstr)\n\t}\n}\n\nfunc TestRandomValidStrings(t *testing.T) {\n\trand.Seed(time.Now().UnixNano())\n\tb := make([]byte, 200)\n\tfor i := 0; i < 100000; i++ {\n\t\tn, err := rand.Read(b[:rand.Int()%len(b)])\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tsm, err := json.Marshal(string(b[:n]))\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tvar su string\n\t\tif err := json.Unmarshal([]byte(sm), &su); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\ttoken := Get(`{\"str\":`+string(sm)+`}`, \"str\")\n\t\tif token.Type != String || token.Str != su {\n\t\t\tprintln(\"[\"+token.Raw+\"]\", \"[\"+token.Str+\"]\", \"[\"+su+\"]\",\n\t\t\t\t\"[\"+string(sm)+\"]\")\n\t\t\tt.Fatal(\"string mismatch\")\n\t\t}\n\t}\n}\n\nfunc TestEmoji(t *testing.T) {\n\tconst input = `{\"utf8\":\"Example emoji, KO: \\ud83d\\udd13, \\ud83c\\udfc3 ` +\n\t\t`OK: \\u2764\\ufe0f \"}`\n\tvalue := Get(input, \"utf8\")\n\tvar s string\n\tjson.Unmarshal([]byte(value.Raw), &s)\n\tif value.String() != s {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", s, value.String())\n\t}\n}\n\nfunc testEscapePath(t *testing.T, json, path, expect string) {\n\tif Get(json, path).String() != expect {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", expect, Get(json, path).String())\n\t}\n}\n\nfunc TestEscapePath(t *testing.T) {\n\tjson := `{\n\t\t\"test\":{\n\t\t\t\"*\":\"valZ\",\n\t\t\t\"*v\":\"val0\",\n\t\t\t\"keyv*\":\"val1\",\n\t\t\t\"key*v\":\"val2\",\n\t\t\t\"keyv?\":\"val3\",\n\t\t\t\"key?v\":\"val4\",\n\t\t\t\"keyv.\":\"val5\",\n\t\t\t\"key.v\":\"val6\",\n\t\t\t\"keyk*\":{\"key?\":\"val7\"}\n\t\t}\n\t}`\n\n\ttestEscapePath(t, json, \"test.\\\\*\", \"valZ\")\n\ttestEscapePath(t, json, \"test.\\\\*v\", \"val0\")\n\ttestEscapePath(t, json, \"test.keyv\\\\*\", \"val1\")\n\ttestEscapePath(t, json, \"test.key\\\\*v\", \"val2\")\n\ttestEscapePath(t, json, \"test.keyv\\\\?\", \"val3\")\n\ttestEscapePath(t, json, \"test.key\\\\?v\", \"val4\")\n\ttestEscapePath(t, json, \"test.keyv\\\\.\", \"val5\")\n\ttestEscapePath(t, json, \"test.key\\\\.v\", \"val6\")\n\ttestEscapePath(t, json, \"test.keyk\\\\*.key\\\\?\", \"val7\")\n}\n\n// this json block is poorly formed on purpose.\nvar basicJSON = `  {\"age\":100, \"name\":{\"here\":\"B\\\\\\\"R\"},\n\t\"noop\":{\"what is a wren?\":\"a bird\"},\n\t\"happy\":true,\"immortal\":false,\n\t\"items\":[1,2,3,{\"tags\":[1,2,3],\"points\":[[1,2],[3,4]]},4,5,6,7],\n\t\"arr\":[\"1\",2,\"3\",{\"hello\":\"world\"},\"4\",5],\n\t\"vals\":[1,2,3,{\"sadf\":sdf\"asdf\"}],\"name\":{\"first\":\"tom\",\"last\":null},\n\t\"created\":\"2014-05-16T08:28:06.989Z\",\n\t\"loggy\":{\n\t\t\"programmers\": [\n    \t    {\n    \t        \"firstName\": \"Brett\",\n    \t        \"lastName\": \"McLaughlin\",\n    \t        \"email\": \"aaaa\",\n\t\t\t\t\"tag\": \"good\"\n    \t    },\n    \t    {\n    \t        \"firstName\": \"Jason\",\n    \t        \"lastName\": \"Hunter\",\n    \t        \"email\": \"bbbb\",\n\t\t\t\t\"tag\": \"bad\"\n    \t    },\n    \t    {\n    \t        \"firstName\": \"Elliotte\",\n    \t        \"lastName\": \"Harold\",\n    \t        \"email\": \"cccc\",\n\t\t\t\t\"tag\":, \"good\"\n    \t    },\n\t\t\t{\n\t\t\t\t\"firstName\": 1002.3,\n\t\t\t\t\"age\": 101\n\t\t\t}\n    \t]\n\t},\n\t\"lastly\":{\"end...ing\":\"soon\",\"yay\":\"final\"}\n}`\n\nfunc TestPath(t *testing.T) {\n\tjson := basicJSON\n\tr := Get(json, \"@this\")\n\tpath := r.Path(json)\n\tif path != \"@this\" {\n\t\tt.FailNow()\n\t}\n\n\tr = Parse(json)\n\tpath = r.Path(json)\n\tif path != \"@this\" {\n\t\tt.FailNow()\n\t}\n\n\tobj := Parse(json)\n\tobj.ForEach(func(key, val Result) bool {\n\t\tkp := key.Path(json)\n\t\tassert(t, kp == \"\")\n\t\tvp := val.Path(json)\n\t\tif vp == \"name\" {\n\t\t\t// there are two \"name\" keys\n\t\t\treturn true\n\t\t}\n\t\tval2 := obj.Get(vp)\n\t\tassert(t, val2.Raw == val.Raw)\n\t\treturn true\n\t})\n\tarr := obj.Get(\"loggy.programmers\")\n\tarr.ForEach(func(_, val Result) bool {\n\t\tvp := val.Path(json)\n\t\tval2 := Get(json, vp)\n\t\tassert(t, val2.Raw == val.Raw)\n\t\treturn true\n\t})\n\tget := func(path string) {\n\t\tr1 := Get(json, path)\n\t\tpath2 := r1.Path(json)\n\t\tr2 := Get(json, path2)\n\t\tassert(t, r1.Raw == r2.Raw)\n\t}\n\tget(\"age\")\n\tget(\"name\")\n\tget(\"name.here\")\n\tget(\"noop\")\n\tget(\"noop.what is a wren?\")\n\tget(\"arr.0\")\n\tget(\"arr.1\")\n\tget(\"arr.2\")\n\tget(\"arr.3\")\n\tget(\"arr.3.hello\")\n\tget(\"arr.4\")\n\tget(\"arr.5\")\n\tget(\"loggy.programmers.2.email\")\n\tget(\"lastly.end\\\\.\\\\.\\\\.ing\")\n\tget(\"lastly.yay\")\n\n}\n\nfunc TestTimeResult(t *testing.T) {\n\tassert(t, Get(basicJSON, \"created\").String() ==\n\t\tGet(basicJSON, \"created\").Time().Format(time.RFC3339Nano))\n}\n\nfunc TestParseAny(t *testing.T) {\n\tassert(t, Parse(\"100\").Float() == 100)\n\tassert(t, Parse(\"true\").Bool())\n\tassert(t, Parse(\"false\").Bool() == false)\n\tassert(t, Parse(\"yikes\").Exists() == false)\n}\n\nfunc TestManyVariousPathCounts(t *testing.T) {\n\tjson := `{\"a\":\"a\",\"b\":\"b\",\"c\":\"c\"}`\n\tcounts := []int{3, 4, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127,\n\t\t128, 129, 255, 256, 257, 511, 512, 513}\n\tpaths := []string{\"a\", \"b\", \"c\"}\n\texpects := []string{\"a\", \"b\", \"c\"}\n\tfor _, count := range counts {\n\t\tvar gpaths []string\n\t\tfor i := 0; i < count; i++ {\n\t\t\tif i < len(paths) {\n\t\t\t\tgpaths = append(gpaths, paths[i])\n\t\t\t} else {\n\t\t\t\tgpaths = append(gpaths, fmt.Sprintf(\"not%d\", i))\n\t\t\t}\n\t\t}\n\t\tresults := GetMany(json, gpaths...)\n\t\tfor i := 0; i < len(paths); i++ {\n\t\t\tif results[i].String() != expects[i] {\n\t\t\t\tt.Fatalf(\"expected '%v', got '%v'\", expects[i],\n\t\t\t\t\tresults[i].String())\n\t\t\t}\n\t\t}\n\t}\n}\nfunc TestManyRecursion(t *testing.T) {\n\tvar json string\n\tvar path string\n\tfor i := 0; i < 100; i++ {\n\t\tjson += `{\"a\":`\n\t\tpath += \".a\"\n\t}\n\tjson += `\"b\"`\n\tfor i := 0; i < 100; i++ {\n\t\tjson += `}`\n\t}\n\tpath = path[1:]\n\tassert(t, GetMany(json, path)[0].String() == \"b\")\n}\nfunc TestByteSafety(t *testing.T) {\n\tjsonb := []byte(`{\"name\":\"Janet\",\"age\":38}`)\n\tmtok := GetBytes(jsonb, \"name\")\n\tif mtok.String() != \"Janet\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"Jason\", mtok.String())\n\t}\n\tmtok2 := GetBytes(jsonb, \"age\")\n\tif mtok2.Raw != \"38\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"Jason\", mtok2.Raw)\n\t}\n\tjsonb[9] = 'T'\n\tjsonb[12] = 'd'\n\tjsonb[13] = 'y'\n\tif mtok.String() != \"Janet\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"Jason\", mtok.String())\n\t}\n}\n\nfunc get(json, path string) Result {\n\treturn GetBytes([]byte(json), path)\n}\n\nfunc TestBasic(t *testing.T) {\n\tvar mtok Result\n\tmtok = get(basicJSON, `loggy.programmers.#[tag=\"good\"].firstName`)\n\tif mtok.String() != \"Brett\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"Brett\", mtok.String())\n\t}\n\tmtok = get(basicJSON, `loggy.programmers.#[tag=\"good\"]#.firstName`)\n\tif mtok.String() != `[\"Brett\",\"Elliotte\"]` {\n\t\tt.Fatalf(\"expected %v, got %v\", `[\"Brett\",\"Elliotte\"]`, mtok.String())\n\t}\n}\n\nfunc TestIsArrayIsObject(t *testing.T) {\n\tmtok := get(basicJSON, \"loggy\")\n\tassert(t, mtok.IsObject())\n\tassert(t, !mtok.IsArray())\n\n\tmtok = get(basicJSON, \"loggy.programmers\")\n\tassert(t, !mtok.IsObject())\n\tassert(t, mtok.IsArray())\n\n\tmtok = get(basicJSON, `loggy.programmers.#[tag=\"good\"]#.firstName`)\n\tassert(t, mtok.IsArray())\n\n\tmtok = get(basicJSON, `loggy.programmers.0.firstName`)\n\tassert(t, !mtok.IsObject())\n\tassert(t, !mtok.IsArray())\n}\n\nfunc TestPlus53BitInts(t *testing.T) {\n\tjson := `{\"IdentityData\":{\"GameInstanceId\":634866135153775564}}`\n\tvalue := Get(json, \"IdentityData.GameInstanceId\")\n\tassert(t, value.Uint() == 634866135153775564)\n\tassert(t, value.Int() == 634866135153775564)\n\tassert(t, value.Float() == 634866135153775616)\n\n\tjson = `{\"IdentityData\":{\"GameInstanceId\":634866135153775564.88172}}`\n\tvalue = Get(json, \"IdentityData.GameInstanceId\")\n\tassert(t, value.Uint() == 634866135153775616)\n\tassert(t, value.Int() == 634866135153775616)\n\tassert(t, value.Float() == 634866135153775616.88172)\n\n\tjson = `{\n\t\t\"min_uint64\": 0,\n\t\t\"max_uint64\": 18446744073709551615,\n\t\t\"overflow_uint64\": 18446744073709551616,\n\t\t\"min_int64\": -9223372036854775808,\n\t\t\"max_int64\": 9223372036854775807,\n\t\t\"overflow_int64\": 9223372036854775808,\n\t\t\"min_uint53\":  0,\n\t\t\"max_uint53\":  4503599627370495,\n\t\t\"overflow_uint53\": 4503599627370496,\n\t\t\"min_int53\": -2251799813685248,\n\t\t\"max_int53\": 2251799813685247,\n\t\t\"overflow_int53\": 2251799813685248\n\t}`\n\n\tassert(t, Get(json, \"min_uint53\").Uint() == 0)\n\tassert(t, Get(json, \"max_uint53\").Uint() == 4503599627370495)\n\tassert(t, Get(json, \"overflow_uint53\").Int() == 4503599627370496)\n\tassert(t, Get(json, \"min_int53\").Int() == -2251799813685248)\n\tassert(t, Get(json, \"max_int53\").Int() == 2251799813685247)\n\tassert(t, Get(json, \"overflow_int53\").Int() == 2251799813685248)\n\tassert(t, Get(json, \"min_uint64\").Uint() == 0)\n\tassert(t, Get(json, \"max_uint64\").Uint() == 18446744073709551615)\n\t// this next value overflows the max uint64 by one which will just\n\t// flip the number to zero\n\tassert(t, Get(json, \"overflow_uint64\").Int() == 0)\n\tassert(t, Get(json, \"min_int64\").Int() == -9223372036854775808)\n\tassert(t, Get(json, \"max_int64\").Int() == 9223372036854775807)\n\t// this next value overflows the max int64 by one which will just\n\t// flip the number to the negative sign.\n\tassert(t, Get(json, \"overflow_int64\").Int() == -9223372036854775808)\n}\nfunc TestIssue38(t *testing.T) {\n\t// These should not fail, even though the unicode is invalid.\n\tGet(`[\"S3O PEDRO DO BUTI\\udf93\"]`, \"0\")\n\tGet(`[\"S3O PEDRO DO BUTI\\udf93asdf\"]`, \"0\")\n\tGet(`[\"S3O PEDRO DO BUTI\\udf93\\u\"]`, \"0\")\n\tGet(`[\"S3O PEDRO DO BUTI\\udf93\\u1\"]`, \"0\")\n\tGet(`[\"S3O PEDRO DO BUTI\\udf93\\u13\"]`, \"0\")\n\tGet(`[\"S3O PEDRO DO BUTI\\udf93\\u134\"]`, \"0\")\n\tGet(`[\"S3O PEDRO DO BUTI\\udf93\\u1345\"]`, \"0\")\n\tGet(`[\"S3O PEDRO DO BUTI\\udf93\\u1345asd\"]`, \"0\")\n}\nfunc TestTypes(t *testing.T) {\n\tassert(t, (Result{Type: String}).Type.String() == \"String\")\n\tassert(t, (Result{Type: Number}).Type.String() == \"Number\")\n\tassert(t, (Result{Type: Null}).Type.String() == \"Null\")\n\tassert(t, (Result{Type: False}).Type.String() == \"False\")\n\tassert(t, (Result{Type: True}).Type.String() == \"True\")\n\tassert(t, (Result{Type: JSON}).Type.String() == \"JSON\")\n\tassert(t, (Result{Type: 100}).Type.String() == \"\")\n\t// bool\n\tassert(t, (Result{Type: True}).Bool() == true)\n\tassert(t, (Result{Type: False}).Bool() == false)\n\tassert(t, (Result{Type: Number, Num: 1}).Bool() == true)\n\tassert(t, (Result{Type: Number, Num: 0}).Bool() == false)\n\tassert(t, (Result{Type: String, Str: \"1\"}).Bool() == true)\n\tassert(t, (Result{Type: String, Str: \"T\"}).Bool() == true)\n\tassert(t, (Result{Type: String, Str: \"t\"}).Bool() == true)\n\tassert(t, (Result{Type: String, Str: \"true\"}).Bool() == true)\n\tassert(t, (Result{Type: String, Str: \"True\"}).Bool() == true)\n\tassert(t, (Result{Type: String, Str: \"TRUE\"}).Bool() == true)\n\tassert(t, (Result{Type: String, Str: \"tRuE\"}).Bool() == true)\n\tassert(t, (Result{Type: String, Str: \"0\"}).Bool() == false)\n\tassert(t, (Result{Type: String, Str: \"f\"}).Bool() == false)\n\tassert(t, (Result{Type: String, Str: \"F\"}).Bool() == false)\n\tassert(t, (Result{Type: String, Str: \"false\"}).Bool() == false)\n\tassert(t, (Result{Type: String, Str: \"False\"}).Bool() == false)\n\tassert(t, (Result{Type: String, Str: \"FALSE\"}).Bool() == false)\n\tassert(t, (Result{Type: String, Str: \"fAlSe\"}).Bool() == false)\n\tassert(t, (Result{Type: String, Str: \"random\"}).Bool() == false)\n\n\t// int\n\tassert(t, (Result{Type: String, Str: \"1\"}).Int() == 1)\n\tassert(t, (Result{Type: True}).Int() == 1)\n\tassert(t, (Result{Type: False}).Int() == 0)\n\tassert(t, (Result{Type: Number, Num: 1}).Int() == 1)\n\t// uint\n\tassert(t, (Result{Type: String, Str: \"1\"}).Uint() == 1)\n\tassert(t, (Result{Type: True}).Uint() == 1)\n\tassert(t, (Result{Type: False}).Uint() == 0)\n\tassert(t, (Result{Type: Number, Num: 1}).Uint() == 1)\n\t// float\n\tassert(t, (Result{Type: String, Str: \"1\"}).Float() == 1)\n\tassert(t, (Result{Type: True}).Float() == 1)\n\tassert(t, (Result{Type: False}).Float() == 0)\n\tassert(t, (Result{Type: Number, Num: 1}).Float() == 1)\n}\nfunc TestForEach(t *testing.T) {\n\tResult{}.ForEach(nil)\n\tResult{Type: String, Str: \"Hello\"}.ForEach(func(_, value Result) bool {\n\t\tassert(t, value.String() == \"Hello\")\n\t\treturn false\n\t})\n\tResult{Type: JSON, Raw: \"*invalid*\"}.ForEach(nil)\n\n\tjson := ` {\"name\": {\"first\": \"Janet\",\"last\": \"Prichard\"},\n\t\"asd\\nf\":\"\\ud83d\\udd13\",\"age\": 47}`\n\tvar count int\n\tParseBytes([]byte(json)).ForEach(func(key, value Result) bool {\n\t\tcount++\n\t\treturn true\n\t})\n\tassert(t, count == 3)\n\tParseBytes([]byte(`{\"bad`)).ForEach(nil)\n\tParseBytes([]byte(`{\"ok\":\"bad`)).ForEach(nil)\n}\nfunc TestMap(t *testing.T) {\n\tassert(t, len(ParseBytes([]byte(`\"asdf\"`)).Map()) == 0)\n\tassert(t, ParseBytes([]byte(`{\"asdf\":\"ghjk\"`)).Map()[\"asdf\"].String() ==\n\t\t\"ghjk\")\n\tassert(t, len(Result{Type: JSON, Raw: \"**invalid**\"}.Map()) == 0)\n\tassert(t, Result{Type: JSON, Raw: \"**invalid**\"}.Value() == nil)\n\tassert(t, Result{Type: JSON, Raw: \"{\"}.Map() != nil)\n}\nfunc TestBasic1(t *testing.T) {\n\tmtok := get(basicJSON, `loggy.programmers`)\n\tvar count int\n\tmtok.ForEach(func(key, value Result) bool {\n\t\tassert(t, key.Exists())\n\t\tassert(t, key.String() == fmt.Sprint(count))\n\t\tassert(t, key.Int() == int64(count))\n\t\tcount++\n\t\tif count == 3 {\n\t\t\treturn false\n\t\t}\n\t\tif count == 1 {\n\t\t\ti := 0\n\t\t\tvalue.ForEach(func(key, value Result) bool {\n\t\t\t\tswitch i {\n\t\t\t\tcase 0:\n\t\t\t\t\tif key.String() != \"firstName\" ||\n\t\t\t\t\t\tvalue.String() != \"Brett\" {\n\t\t\t\t\t\tt.Fatalf(\"expected %v/%v got %v/%v\", \"firstName\",\n\t\t\t\t\t\t\t\"Brett\", key.String(), value.String())\n\t\t\t\t\t}\n\t\t\t\tcase 1:\n\t\t\t\t\tif key.String() != \"lastName\" ||\n\t\t\t\t\t\tvalue.String() != \"McLaughlin\" {\n\t\t\t\t\t\tt.Fatalf(\"expected %v/%v got %v/%v\", \"lastName\",\n\t\t\t\t\t\t\t\"McLaughlin\", key.String(), value.String())\n\t\t\t\t\t}\n\t\t\t\tcase 2:\n\t\t\t\t\tif key.String() != \"email\" || value.String() != \"aaaa\" {\n\t\t\t\t\t\tt.Fatalf(\"expected %v/%v got %v/%v\", \"email\", \"aaaa\",\n\t\t\t\t\t\t\tkey.String(), value.String())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti++\n\t\t\t\treturn true\n\t\t\t})\n\t\t}\n\t\treturn true\n\t})\n\tif count != 3 {\n\t\tt.Fatalf(\"expected %v, got %v\", 3, count)\n\t}\n}\nfunc TestBasic2(t *testing.T) {\n\tmtok := get(basicJSON, `loggy.programmers.#[age=101].firstName`)\n\tif mtok.String() != \"1002.3\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"1002.3\", mtok.String())\n\t}\n\tmtok = get(basicJSON,\n\t\t`loggy.programmers.#[firstName != \"Brett\"].firstName`)\n\tif mtok.String() != \"Jason\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"Jason\", mtok.String())\n\t}\n\tmtok = get(basicJSON, `loggy.programmers.#[firstName % \"Bre*\"].email`)\n\tif mtok.String() != \"aaaa\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"aaaa\", mtok.String())\n\t}\n\tmtok = get(basicJSON, `loggy.programmers.#[firstName !% \"Bre*\"].email`)\n\tif mtok.String() != \"bbbb\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"bbbb\", mtok.String())\n\t}\n\tmtok = get(basicJSON, `loggy.programmers.#[firstName == \"Brett\"].email`)\n\tif mtok.String() != \"aaaa\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"aaaa\", mtok.String())\n\t}\n\tmtok = get(basicJSON, \"loggy\")\n\tif mtok.Type != JSON {\n\t\tt.Fatalf(\"expected %v, got %v\", JSON, mtok.Type)\n\t}\n\tif len(mtok.Map()) != 1 {\n\t\tt.Fatalf(\"expected %v, got %v\", 1, len(mtok.Map()))\n\t}\n\tprogrammers := mtok.Map()[\"programmers\"]\n\tif programmers.Array()[1].Map()[\"firstName\"].Str != \"Jason\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"Jason\",\n\t\t\tmtok.Map()[\"programmers\"].Array()[1].Map()[\"firstName\"].Str)\n\t}\n}\nfunc TestBasic3(t *testing.T) {\n\tvar mtok Result\n\tif Parse(basicJSON).Get(\"loggy.programmers\").Get(\"1\").\n\t\tGet(\"firstName\").Str != \"Jason\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"Jason\", Parse(basicJSON).\n\t\t\tGet(\"loggy.programmers\").Get(\"1\").Get(\"firstName\").Str)\n\t}\n\tvar token Result\n\tif token = Parse(\"-102\"); token.Num != -102 {\n\t\tt.Fatalf(\"expected %v, got %v\", -102, token.Num)\n\t}\n\tif token = Parse(\"102\"); token.Num != 102 {\n\t\tt.Fatalf(\"expected %v, got %v\", 102, token.Num)\n\t}\n\tif token = Parse(\"102.2\"); token.Num != 102.2 {\n\t\tt.Fatalf(\"expected %v, got %v\", 102.2, token.Num)\n\t}\n\tif token = Parse(`\"hello\"`); token.Str != \"hello\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"hello\", token.Str)\n\t}\n\tif token = Parse(`\"\\\"he\\nllo\\\"\"`); token.Str != \"\\\"he\\nllo\\\"\" {\n\t\tt.Fatalf(\"expected %v, got %v\", \"\\\"he\\nllo\\\"\", token.Str)\n\t}\n\tmtok = get(basicJSON, \"loggy.programmers.#.firstName\")\n\tif len(mtok.Array()) != 4 {\n\t\tt.Fatalf(\"expected 4, got %v\", len(mtok.Array()))\n\t}\n\tfor i, ex := range []string{\"Brett\", \"Jason\", \"Elliotte\", \"1002.3\"} {\n\t\tif mtok.Array()[i].String() != ex {\n\t\t\tt.Fatalf(\"expected '%v', got '%v'\", ex, mtok.Array()[i].String())\n\t\t}\n\t}\n\tmtok = get(basicJSON, \"loggy.programmers.#.asd\")\n\tif mtok.Type != JSON {\n\t\tt.Fatalf(\"expected %v, got %v\", JSON, mtok.Type)\n\t}\n\tif len(mtok.Array()) != 0 {\n\t\tt.Fatalf(\"expected 0, got %v\", len(mtok.Array()))\n\t}\n}\nfunc TestBasic4(t *testing.T) {\n\tif get(basicJSON, \"items.3.tags.#\").Num != 3 {\n\t\tt.Fatalf(\"expected 3, got %v\", get(basicJSON, \"items.3.tags.#\").Num)\n\t}\n\tif get(basicJSON, \"items.3.points.1.#\").Num != 2 {\n\t\tt.Fatalf(\"expected 2, got %v\",\n\t\t\tget(basicJSON, \"items.3.points.1.#\").Num)\n\t}\n\tif get(basicJSON, \"items.#\").Num != 8 {\n\t\tt.Fatalf(\"expected 6, got %v\", get(basicJSON, \"items.#\").Num)\n\t}\n\tif get(basicJSON, \"vals.#\").Num != 4 {\n\t\tt.Fatalf(\"expected 4, got %v\", get(basicJSON, \"vals.#\").Num)\n\t}\n\tif !get(basicJSON, \"name.last\").Exists() {\n\t\tt.Fatal(\"expected true, got false\")\n\t}\n\ttoken := get(basicJSON, \"name.here\")\n\tif token.String() != \"B\\\\\\\"R\" {\n\t\tt.Fatal(\"expecting 'B\\\\\\\"R'\", \"got\", token.String())\n\t}\n\ttoken = get(basicJSON, \"arr.#\")\n\tif token.String() != \"6\" {\n\t\tfmt.Printf(\"%#v\\n\", token)\n\t\tt.Fatal(\"expecting 6\", \"got\", token.String())\n\t}\n\ttoken = get(basicJSON, \"arr.3.hello\")\n\tif token.String() != \"world\" {\n\t\tt.Fatal(\"expecting 'world'\", \"got\", token.String())\n\t}\n\t_ = token.Value().(string)\n\ttoken = get(basicJSON, \"name.first\")\n\tif token.String() != \"tom\" {\n\t\tt.Fatal(\"expecting 'tom'\", \"got\", token.String())\n\t}\n\t_ = token.Value().(string)\n\ttoken = get(basicJSON, \"name.last\")\n\tif token.String() != \"\" {\n\t\tt.Fatal(\"expecting ''\", \"got\", token.String())\n\t}\n\tif token.Value() != nil {\n\t\tt.Fatal(\"should be nil\")\n\t}\n}\nfunc TestBasic5(t *testing.T) {\n\ttoken := get(basicJSON, \"age\")\n\tif token.String() != \"100\" {\n\t\tt.Fatal(\"expecting '100'\", \"got\", token.String())\n\t}\n\t_ = token.Value().(float64)\n\ttoken = get(basicJSON, \"happy\")\n\tif token.String() != \"true\" {\n\t\tt.Fatal(\"expecting 'true'\", \"got\", token.String())\n\t}\n\t_ = token.Value().(bool)\n\ttoken = get(basicJSON, \"immortal\")\n\tif token.String() != \"false\" {\n\t\tt.Fatal(\"expecting 'false'\", \"got\", token.String())\n\t}\n\t_ = token.Value().(bool)\n\ttoken = get(basicJSON, \"noop\")\n\tif token.String() != `{\"what is a wren?\":\"a bird\"}` {\n\t\tt.Fatal(\"expecting '\"+`{\"what is a wren?\":\"a bird\"}`+\"'\", \"got\",\n\t\t\ttoken.String())\n\t}\n\t_ = token.Value().(map[string]interface{})\n\n\tif get(basicJSON, \"\").Value() != nil {\n\t\tt.Fatal(\"should be nil\")\n\t}\n\n\tget(basicJSON, \"vals.hello\")\n\n\ttype msi = map[string]interface{}\n\ttype fi = []interface{}\n\tmm := Parse(basicJSON).Value().(msi)\n\tfn := mm[\"loggy\"].(msi)[\"programmers\"].(fi)[1].(msi)[\"firstName\"].(string)\n\tif fn != \"Jason\" {\n\t\tt.Fatalf(\"expecting %v, got %v\", \"Jason\", fn)\n\t}\n}\nfunc TestUnicode(t *testing.T) {\n\tvar json = `{\"key\":0,\"的情况下解\":{\"key\":1,\"的情况\":2}}`\n\tif Get(json, \"的情况下解.key\").Num != 1 {\n\t\tt.Fatal(\"fail\")\n\t}\n\tif Get(json, \"的情况下解.的情况\").Num != 2 {\n\t\tt.Fatal(\"fail\")\n\t}\n\tif Get(json, \"的情况下解.的?况\").Num != 2 {\n\t\tt.Fatal(\"fail\")\n\t}\n\tif Get(json, \"的情况下解.的?*\").Num != 2 {\n\t\tt.Fatal(\"fail\")\n\t}\n\tif Get(json, \"的情况下解.*?况\").Num != 2 {\n\t\tt.Fatal(\"fail\")\n\t}\n\tif Get(json, \"的情?下解.*?况\").Num != 2 {\n\t\tt.Fatal(\"fail\")\n\t}\n\tif Get(json, \"的情下解.*?况\").Num != 0 {\n\t\tt.Fatal(\"fail\")\n\t}\n}\n\nfunc TestUnescape(t *testing.T) {\n\tunescape(string([]byte{'\\\\', '\\\\', 0}))\n\tunescape(string([]byte{'\\\\', '/', '\\\\', 'b', '\\\\', 'f'}))\n}\nfunc assert(t testing.TB, cond bool) {\n\tif !cond {\n\t\tpanic(\"assert failed\")\n\t}\n}\nfunc TestLess(t *testing.T) {\n\tassert(t, !Result{Type: Null}.Less(Result{Type: Null}, true))\n\tassert(t, Result{Type: Null}.Less(Result{Type: False}, true))\n\tassert(t, Result{Type: Null}.Less(Result{Type: True}, true))\n\tassert(t, Result{Type: Null}.Less(Result{Type: JSON}, true))\n\tassert(t, Result{Type: Null}.Less(Result{Type: Number}, true))\n\tassert(t, Result{Type: Null}.Less(Result{Type: String}, true))\n\tassert(t, !Result{Type: False}.Less(Result{Type: Null}, true))\n\tassert(t, Result{Type: False}.Less(Result{Type: True}, true))\n\tassert(t, Result{Type: String, Str: \"abc\"}.Less(Result{Type: String,\n\t\tStr: \"bcd\"}, true))\n\tassert(t, Result{Type: String, Str: \"ABC\"}.Less(Result{Type: String,\n\t\tStr: \"abc\"}, true))\n\tassert(t, !Result{Type: String, Str: \"ABC\"}.Less(Result{Type: String,\n\t\tStr: \"abc\"}, false))\n\tassert(t, Result{Type: Number, Num: 123}.Less(Result{Type: Number,\n\t\tNum: 456}, true))\n\tassert(t, !Result{Type: Number, Num: 456}.Less(Result{Type: Number,\n\t\tNum: 123}, true))\n\tassert(t, !Result{Type: Number, Num: 456}.Less(Result{Type: Number,\n\t\tNum: 456}, true))\n\tassert(t, stringLessInsensitive(\"abcde\", \"BBCDE\"))\n\tassert(t, stringLessInsensitive(\"abcde\", \"bBCDE\"))\n\tassert(t, stringLessInsensitive(\"Abcde\", \"BBCDE\"))\n\tassert(t, stringLessInsensitive(\"Abcde\", \"bBCDE\"))\n\tassert(t, !stringLessInsensitive(\"bbcde\", \"aBCDE\"))\n\tassert(t, !stringLessInsensitive(\"bbcde\", \"ABCDE\"))\n\tassert(t, !stringLessInsensitive(\"Bbcde\", \"aBCDE\"))\n\tassert(t, !stringLessInsensitive(\"Bbcde\", \"ABCDE\"))\n\tassert(t, !stringLessInsensitive(\"abcde\", \"ABCDE\"))\n\tassert(t, !stringLessInsensitive(\"Abcde\", \"ABCDE\"))\n\tassert(t, !stringLessInsensitive(\"abcde\", \"ABCDE\"))\n\tassert(t, !stringLessInsensitive(\"ABCDE\", \"ABCDE\"))\n\tassert(t, !stringLessInsensitive(\"abcde\", \"abcde\"))\n\tassert(t, !stringLessInsensitive(\"123abcde\", \"123Abcde\"))\n\tassert(t, !stringLessInsensitive(\"123Abcde\", \"123Abcde\"))\n\tassert(t, !stringLessInsensitive(\"123Abcde\", \"123abcde\"))\n\tassert(t, !stringLessInsensitive(\"123abcde\", \"123abcde\"))\n\tassert(t, !stringLessInsensitive(\"124abcde\", \"123abcde\"))\n\tassert(t, !stringLessInsensitive(\"124Abcde\", \"123Abcde\"))\n\tassert(t, !stringLessInsensitive(\"124Abcde\", \"123abcde\"))\n\tassert(t, !stringLessInsensitive(\"124abcde\", \"123abcde\"))\n\tassert(t, stringLessInsensitive(\"124abcde\", \"125abcde\"))\n\tassert(t, stringLessInsensitive(\"124Abcde\", \"125Abcde\"))\n\tassert(t, stringLessInsensitive(\"124Abcde\", \"125abcde\"))\n\tassert(t, stringLessInsensitive(\"124abcde\", \"125abcde\"))\n}\n\nfunc TestIssue6(t *testing.T) {\n\tdata := `{\n      \"code\": 0,\n      \"msg\": \"\",\n      \"data\": {\n        \"sz002024\": {\n          \"qfqday\": [\n            [\n              \"2014-01-02\",\n              \"8.93\",\n              \"9.03\",\n              \"9.17\",\n              \"8.88\",\n              \"621143.00\"\n            ],\n            [\n              \"2014-01-03\",\n              \"9.03\",\n              \"9.30\",\n              \"9.47\",\n              \"8.98\",\n              \"1624438.00\"\n            ]\n          ]\n        }\n      }\n    }`\n\n\tvar num []string\n\tfor _, v := range Get(data, \"data.sz002024.qfqday.0\").Array() {\n\t\tnum = append(num, v.String())\n\t}\n\tif fmt.Sprintf(\"%v\", num) != \"[2014-01-02 8.93 9.03 9.17 8.88 621143.00]\" {\n\t\tt.Fatalf(\"invalid result\")\n\t}\n}\n\nvar exampleJSON = `{\n\t\"widget\": {\n\t\t\"debug\": \"on\",\n\t\t\"window\": {\n\t\t\t\"title\": \"Sample Konfabulator Widget\",\n\t\t\t\"name\": \"main_window\",\n\t\t\t\"width\": 500,\n\t\t\t\"height\": 500\n\t\t},\n\t\t\"image\": {\n\t\t\t\"src\": \"Images/Sun.png\",\n\t\t\t\"hOffset\": 250,\n\t\t\t\"vOffset\": 250,\n\t\t\t\"alignment\": \"center\"\n\t\t},\n\t\t\"text\": {\n\t\t\t\"data\": \"Click Here\",\n\t\t\t\"size\": 36,\n\t\t\t\"style\": \"bold\",\n\t\t\t\"vOffset\": 100,\n\t\t\t\"alignment\": \"center\",\n\t\t\t\"onMouseUp\": \"sun1.opacity = (sun1.opacity / 100) * 90;\"\n\t\t}\n\t}\n}`\n\nfunc TestUnmarshalMap(t *testing.T) {\n\tvar m1 = Parse(exampleJSON).Value().(map[string]interface{})\n\tvar m2 map[string]interface{}\n\tif err := json.Unmarshal([]byte(exampleJSON), &m2); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tb1, err := json.Marshal(m1)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tb2, err := json.Marshal(m2)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Equal(b1, b2) {\n\t\tt.Fatal(\"b1 != b2\")\n\t}\n}\n\nfunc TestSingleArrayValue(t *testing.T) {\n\tvar json = `{\"key\": \"value\",\"key2\":[1,2,3,4,\"A\"]}`\n\tvar result = Get(json, \"key\")\n\tvar array = result.Array()\n\tif len(array) != 1 {\n\t\tt.Fatal(\"array is empty\")\n\t}\n\tif array[0].String() != \"value\" {\n\t\tt.Fatalf(\"got %s, should be %s\", array[0].String(), \"value\")\n\t}\n\n\tarray = Get(json, \"key2.#\").Array()\n\tif len(array) != 1 {\n\t\tt.Fatalf(\"got '%v', expected '%v'\", len(array), 1)\n\t}\n\n\tarray = Get(json, \"key3\").Array()\n\tif len(array) != 0 {\n\t\tt.Fatalf(\"got '%v', expected '%v'\", len(array), 0)\n\t}\n\n}\n\nvar manyJSON = `  {\n\t\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\n\t\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\n\t\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\n\t\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\n\t\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\n\t\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\n\t\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"hello\":\"world\"\n\t}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}\n\t\"position\":{\"type\":\"Point\",\"coordinates\":[-115.24,33.09]},\n\t\"loves\":[\"world peace\"],\n\t\"name\":{\"last\":\"Anderson\",\"first\":\"Nancy\"},\n\t\"age\":31\n\t\"\":{\"a\":\"emptya\",\"b\":\"emptyb\"},\n\t\"name.last\":\"Yellow\",\n\t\"name.first\":\"Cat\",\n}`\n\nvar testWatchForFallback bool\n\nfunc TestManyBasic(t *testing.T) {\n\ttestWatchForFallback = true\n\tdefer func() {\n\t\ttestWatchForFallback = false\n\t}()\n\ttestMany := func(shouldFallback bool, expect string, paths ...string) {\n\t\tresults := GetManyBytes(\n\t\t\t[]byte(manyJSON),\n\t\t\tpaths...,\n\t\t)\n\t\tif len(results) != len(paths) {\n\t\t\tt.Fatalf(\"expected %v, got %v\", len(paths), len(results))\n\t\t}\n\t\tif fmt.Sprintf(\"%v\", results) != expect {\n\t\t\tfmt.Printf(\"%v\\n\", paths)\n\t\t\tt.Fatalf(\"expected %v, got %v\", expect, results)\n\t\t}\n\t}\n\ttestMany(false, \"[Point]\", \"position.type\")\n\ttestMany(false, `[emptya [\"world peace\"] 31]`, \".a\", \"loves\", \"age\")\n\ttestMany(false, `[[\"world peace\"]]`, \"loves\")\n\ttestMany(false, `[{\"last\":\"Anderson\",\"first\":\"Nancy\"} Nancy]`, \"name\",\n\t\t\"name.first\")\n\ttestMany(true, `[]`, strings.Repeat(\"a.\", 40)+\"hello\")\n\tres := Get(manyJSON, strings.Repeat(\"a.\", 48)+\"a\")\n\ttestMany(true, `[`+res.String()+`]`, strings.Repeat(\"a.\", 48)+\"a\")\n\t// these should fallback\n\ttestMany(true, `[Cat Nancy]`, \"name\\\\.first\", \"name.first\")\n\ttestMany(true, `[world]`, strings.Repeat(\"a.\", 70)+\"hello\")\n}\nfunc testMany(t *testing.T, json string, paths, expected []string) {\n\ttestManyAny(t, json, paths, expected, true)\n\ttestManyAny(t, json, paths, expected, false)\n}\nfunc testManyAny(t *testing.T, json string, paths, expected []string,\n\tbytes bool) {\n\tvar result []Result\n\tfor i := 0; i < 2; i++ {\n\t\tvar which string\n\t\tif i == 0 {\n\t\t\twhich = \"Get\"\n\t\t\tresult = nil\n\t\t\tfor j := 0; j < len(expected); j++ {\n\t\t\t\tif bytes {\n\t\t\t\t\tresult = append(result, GetBytes([]byte(json), paths[j]))\n\t\t\t\t} else {\n\t\t\t\t\tresult = append(result, Get(json, paths[j]))\n\t\t\t\t}\n\t\t\t}\n\t\t} else if i == 1 {\n\t\t\twhich = \"GetMany\"\n\t\t\tif bytes {\n\t\t\t\tresult = GetManyBytes([]byte(json), paths...)\n\t\t\t} else {\n\t\t\t\tresult = GetMany(json, paths...)\n\t\t\t}\n\t\t}\n\t\tfor j := 0; j < len(expected); j++ {\n\t\t\tif result[j].String() != expected[j] {\n\t\t\t\tt.Fatalf(\"Using key '%s' for '%s'\\nexpected '%v', got '%v'\",\n\t\t\t\t\tpaths[j], which, expected[j], result[j].String())\n\t\t\t}\n\t\t}\n\t}\n}\nfunc TestIssue20(t *testing.T) {\n\tjson := `{ \"name\": \"FirstName\", \"name1\": \"FirstName1\", ` +\n\t\t`\"address\": \"address1\", \"addressDetails\": \"address2\", }`\n\tpaths := []string{\"name\", \"name1\", \"address\", \"addressDetails\"}\n\texpected := []string{\"FirstName\", \"FirstName1\", \"address1\", \"address2\"}\n\tt.Run(\"SingleMany\", func(t *testing.T) {\n\t\ttestMany(t, json, paths,\n\t\t\texpected)\n\t})\n}\n\nfunc TestIssue21(t *testing.T) {\n\tjson := `{ \"Level1Field1\":3,\n\t           \"Level1Field4\":4,\n\t\t\t   \"Level1Field2\":{ \"Level2Field1\":[ \"value1\", \"value2\" ],\n\t\t\t   \"Level2Field2\":{ \"Level3Field1\":[ { \"key1\":\"value1\" } ] } } }`\n\tpaths := []string{\"Level1Field1\", \"Level1Field2.Level2Field1\",\n\t\t\"Level1Field2.Level2Field2.Level3Field1\", \"Level1Field4\"}\n\texpected := []string{\"3\", `[ \"value1\", \"value2\" ]`,\n\t\t`[ { \"key1\":\"value1\" } ]`, \"4\"}\n\tt.Run(\"SingleMany\", func(t *testing.T) {\n\t\ttestMany(t, json, paths,\n\t\t\texpected)\n\t})\n}\n\nfunc TestRandomMany(t *testing.T) {\n\tvar lstr string\n\tdefer func() {\n\t\tif v := recover(); v != nil {\n\t\t\tprintln(\"'\" + hex.EncodeToString([]byte(lstr)) + \"'\")\n\t\t\tprintln(\"'\" + lstr + \"'\")\n\t\t\tpanic(v)\n\t\t}\n\t}()\n\trand.Seed(time.Now().UnixNano())\n\tb := make([]byte, 512)\n\tfor i := 0; i < 50000; i++ {\n\t\tn, err := rand.Read(b[:rand.Int()%len(b)])\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tlstr = string(b[:n])\n\t\tpaths := make([]string, rand.Int()%64)\n\t\tfor i := range paths {\n\t\t\tvar b []byte\n\t\t\tn := rand.Int() % 5\n\t\t\tfor j := 0; j < n; j++ {\n\t\t\t\tif j > 0 {\n\t\t\t\t\tb = append(b, '.')\n\t\t\t\t}\n\t\t\t\tnn := rand.Int() % 10\n\t\t\t\tfor k := 0; k < nn; k++ {\n\t\t\t\t\tb = append(b, 'a'+byte(rand.Int()%26))\n\t\t\t\t}\n\t\t\t}\n\t\t\tpaths[i] = string(b)\n\t\t}\n\t\tGetMany(lstr, paths...)\n\t}\n}\n\nvar complicatedJSON = `\n{\n\t\"tagged\": \"OK\",\n\t\"Tagged\": \"KO\",\n\t\"NotTagged\": true,\n\t\"unsettable\": 101,\n\t\"Nested\": {\n\t\t\"Yellow\": \"Green\",\n\t\t\"yellow\": \"yellow\"\n\t},\n\t\"nestedTagged\": {\n\t\t\"Green\": \"Green\",\n\t\t\"Map\": {\n\t\t\t\"this\": \"that\",\n\t\t\t\"and\": \"the other thing\"\n\t\t},\n\t\t\"Ints\": {\n\t\t\t\"Uint\": 99,\n\t\t\t\"Uint16\": 16,\n\t\t\t\"Uint32\": 32,\n\t\t\t\"Uint64\": 65\n\t\t},\n\t\t\"Uints\": {\n\t\t\t\"int\": -99,\n\t\t\t\"Int\": -98,\n\t\t\t\"Int16\": -16,\n\t\t\t\"Int32\": -32,\n\t\t\t\"int64\": -64,\n\t\t\t\"Int64\": -65\n\t\t},\n\t\t\"Uints\": {\n\t\t\t\"Float32\": 32.32,\n\t\t\t\"Float64\": 64.64\n\t\t},\n\t\t\"Byte\": 254,\n\t\t\"Bool\": true\n\t},\n\t\"LeftOut\": \"you shouldn't be here\",\n\t\"SelfPtr\": {\"tagged\":\"OK\",\"nestedTagged\":{\"Ints\":{\"Uint32\":32}}},\n\t\"SelfSlice\": [{\"tagged\":\"OK\",\"nestedTagged\":{\"Ints\":{\"Uint32\":32}}}],\n\t\"SelfSlicePtr\": [{\"tagged\":\"OK\",\"nestedTagged\":{\"Ints\":{\"Uint32\":32}}}],\n\t\"SelfPtrSlice\": [{\"tagged\":\"OK\",\"nestedTagged\":{\"Ints\":{\"Uint32\":32}}}],\n\t\"interface\": \"Tile38 Rocks!\",\n\t\"Interface\": \"Please Download\",\n\t\"Array\": [0,2,3,4,5],\n\t\"time\": \"2017-05-07T13:24:43-07:00\",\n\t\"Binary\": \"R0lGODlhPQBEAPeo\",\n\t\"NonBinary\": [9,3,100,115]\n}\n`\n\nfunc testvalid(t *testing.T, json string, expect bool) {\n\tt.Helper()\n\t_, ok := validpayload([]byte(json), 0)\n\tif ok != expect {\n\t\tt.Fatal(\"mismatch\")\n\t}\n}\n\nfunc TestValidBasic(t *testing.T) {\n\ttestvalid(t, \"0\", true)\n\ttestvalid(t, \"00\", false)\n\ttestvalid(t, \"-00\", false)\n\ttestvalid(t, \"-.\", false)\n\ttestvalid(t, \"-.123\", false)\n\ttestvalid(t, \"0.0\", true)\n\ttestvalid(t, \"10.0\", true)\n\ttestvalid(t, \"10e1\", true)\n\ttestvalid(t, \"10EE\", false)\n\ttestvalid(t, \"10E-\", false)\n\ttestvalid(t, \"10E+\", false)\n\ttestvalid(t, \"10E123\", true)\n\ttestvalid(t, \"10E-123\", true)\n\ttestvalid(t, \"10E-0123\", true)\n\ttestvalid(t, \"\", false)\n\ttestvalid(t, \" \", false)\n\ttestvalid(t, \"{}\", true)\n\ttestvalid(t, \"{\", false)\n\ttestvalid(t, \"-\", false)\n\ttestvalid(t, \"-1\", true)\n\ttestvalid(t, \"-1.\", false)\n\ttestvalid(t, \"-1.0\", true)\n\ttestvalid(t, \" -1.0\", true)\n\ttestvalid(t, \" -1.0 \", true)\n\ttestvalid(t, \"-1.0 \", true)\n\ttestvalid(t, \"-1.0 i\", false)\n\ttestvalid(t, \"-1.0 i\", false)\n\ttestvalid(t, \"true\", true)\n\ttestvalid(t, \" true\", true)\n\ttestvalid(t, \" true \", true)\n\ttestvalid(t, \" True \", false)\n\ttestvalid(t, \" tru\", false)\n\ttestvalid(t, \"false\", true)\n\ttestvalid(t, \" false\", true)\n\ttestvalid(t, \" false \", true)\n\ttestvalid(t, \" False \", false)\n\ttestvalid(t, \" fals\", false)\n\ttestvalid(t, \"null\", true)\n\ttestvalid(t, \" null\", true)\n\ttestvalid(t, \" null \", true)\n\ttestvalid(t, \" Null \", false)\n\ttestvalid(t, \" nul\", false)\n\ttestvalid(t, \" []\", true)\n\ttestvalid(t, \" [true]\", true)\n\ttestvalid(t, \" [ true, null ]\", true)\n\ttestvalid(t, \" [ true,]\", false)\n\ttestvalid(t, `{\"hello\":\"world\"}`, true)\n\ttestvalid(t, `{ \"hello\": \"world\" }`, true)\n\ttestvalid(t, `{ \"hello\": \"world\", }`, false)\n\ttestvalid(t, `{\"a\":\"b\",}`, false)\n\ttestvalid(t, `{\"a\":\"b\",\"a\"}`, false)\n\ttestvalid(t, `{\"a\":\"b\",\"a\":}`, false)\n\ttestvalid(t, `{\"a\":\"b\",\"a\":1}`, true)\n\ttestvalid(t, `{\"a\":\"b\",2\"1\":2}`, false)\n\ttestvalid(t, `{\"a\":\"b\",\"a\": 1, \"c\":{\"hi\":\"there\"} }`, true)\n\ttestvalid(t, `{\"a\":\"b\",\"a\": 1, \"c\":{\"hi\":\"there\", \"easy\":[\"going\",`+\n\t\t`{\"mixed\":\"bag\"}]} }`, true)\n\ttestvalid(t, `\"\"`, true)\n\ttestvalid(t, `\"`, false)\n\ttestvalid(t, `\"\\n\"`, true)\n\ttestvalid(t, `\"\\\"`, false)\n\ttestvalid(t, `\"\\\\\"`, true)\n\ttestvalid(t, `\"a\\\\b\"`, true)\n\ttestvalid(t, `\"a\\\\b\\\\\\\"a\"`, true)\n\ttestvalid(t, `\"a\\\\b\\\\\\uFFAAa\"`, true)\n\ttestvalid(t, `\"a\\\\b\\\\\\uFFAZa\"`, false)\n\ttestvalid(t, `\"a\\\\b\\\\\\uFFA\"`, false)\n\ttestvalid(t, string(complicatedJSON), true)\n\ttestvalid(t, string(exampleJSON), true)\n\ttestvalid(t, \"[-]\", false)\n\ttestvalid(t, \"[-.123]\", false)\n}\n\nvar jsonchars = []string{\"{\", \"[\", \",\", \":\", \"}\", \"]\", \"1\", \"0\", \"true\",\n\t\"false\", \"null\", `\"\"`, `\"\\\"\"`, `\"a\"`}\n\nfunc makeRandomJSONChars(b []byte) {\n\tvar bb []byte\n\tfor len(bb) < len(b) {\n\t\tbb = append(bb, jsonchars[rand.Int()%len(jsonchars)]...)\n\t}\n\tcopy(b, bb[:len(b)])\n}\n\nfunc TestValidRandom(t *testing.T) {\n\trand.Seed(time.Now().UnixNano())\n\tb := make([]byte, 100000)\n\tstart := time.Now()\n\tfor time.Since(start) < time.Second*3 {\n\t\tn := rand.Int() % len(b)\n\t\trand.Read(b[:n])\n\t\tvalidpayload(b[:n], 0)\n\t}\n\n\tstart = time.Now()\n\tfor time.Since(start) < time.Second*3 {\n\t\tn := rand.Int() % len(b)\n\t\tmakeRandomJSONChars(b[:n])\n\t\tvalidpayload(b[:n], 0)\n\t}\n}\n\nfunc TestGetMany47(t *testing.T) {\n\tjson := `{\"bar\": {\"id\": 99, \"mybar\": \"my mybar\" }, \"foo\": ` +\n\t\t`{\"myfoo\": [605]}}`\n\tpaths := []string{\"foo.myfoo\", \"bar.id\", \"bar.mybar\", \"bar.mybarx\"}\n\texpected := []string{\"[605]\", \"99\", \"my mybar\", \"\"}\n\tresults := GetMany(json, paths...)\n\tif len(expected) != len(results) {\n\t\tt.Fatalf(\"expected %v, got %v\", len(expected), len(results))\n\t}\n\tfor i, path := range paths {\n\t\tif results[i].String() != expected[i] {\n\t\t\tt.Fatalf(\"expected '%v', got '%v' for path '%v'\", expected[i],\n\t\t\t\tresults[i].String(), path)\n\t\t}\n\t}\n}\n\nfunc TestGetMany48(t *testing.T) {\n\tjson := `{\"bar\": {\"id\": 99, \"xyz\": \"my xyz\"}, \"foo\": {\"myfoo\": [605]}}`\n\tpaths := []string{\"foo.myfoo\", \"bar.id\", \"bar.xyz\", \"bar.abc\"}\n\texpected := []string{\"[605]\", \"99\", \"my xyz\", \"\"}\n\tresults := GetMany(json, paths...)\n\tif len(expected) != len(results) {\n\t\tt.Fatalf(\"expected %v, got %v\", len(expected), len(results))\n\t}\n\tfor i, path := range paths {\n\t\tif results[i].String() != expected[i] {\n\t\t\tt.Fatalf(\"expected '%v', got '%v' for path '%v'\", expected[i],\n\t\t\t\tresults[i].String(), path)\n\t\t}\n\t}\n}\n\nfunc TestResultRawForLiteral(t *testing.T) {\n\tfor _, lit := range []string{\"null\", \"true\", \"false\"} {\n\t\tresult := Parse(lit)\n\t\tif result.Raw != lit {\n\t\t\tt.Fatalf(\"expected '%v', got '%v'\", lit, result.Raw)\n\t\t}\n\t}\n}\n\nfunc TestNullArray(t *testing.T) {\n\tn := len(Get(`{\"data\":null}`, \"data\").Array())\n\tif n != 0 {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", 0, n)\n\t}\n\tn = len(Get(`{}`, \"data\").Array())\n\tif n != 0 {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", 0, n)\n\t}\n\tn = len(Get(`{\"data\":[]}`, \"data\").Array())\n\tif n != 0 {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", 0, n)\n\t}\n\tn = len(Get(`{\"data\":[null]}`, \"data\").Array())\n\tif n != 1 {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", 1, n)\n\t}\n}\n\nfunc TestIssue54(t *testing.T) {\n\tvar r []Result\n\tjson := `{\"MarketName\":null,\"Nounce\":6115}`\n\tr = GetMany(json, \"Nounce\", \"Buys\", \"Sells\", \"Fills\")\n\tif strings.Replace(fmt.Sprintf(\"%v\", r), \" \", \"\", -1) != \"[6115]\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"[6115]\",\n\t\t\tstrings.Replace(fmt.Sprintf(\"%v\", r), \" \", \"\", -1))\n\t}\n\tr = GetMany(json, \"Nounce\", \"Buys\", \"Sells\")\n\tif strings.Replace(fmt.Sprintf(\"%v\", r), \" \", \"\", -1) != \"[6115]\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"[6115]\",\n\t\t\tstrings.Replace(fmt.Sprintf(\"%v\", r), \" \", \"\", -1))\n\t}\n\tr = GetMany(json, \"Nounce\")\n\tif strings.Replace(fmt.Sprintf(\"%v\", r), \" \", \"\", -1) != \"[6115]\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"[6115]\",\n\t\t\tstrings.Replace(fmt.Sprintf(\"%v\", r), \" \", \"\", -1))\n\t}\n}\n\nfunc TestIssue55(t *testing.T) {\n\tjson := `{\"one\": {\"two\": 2, \"three\": 3}, \"four\": 4, \"five\": 5}`\n\tresults := GetMany(json, \"four\", \"five\", \"one.two\", \"one.six\")\n\texpected := []string{\"4\", \"5\", \"2\", \"\"}\n\tfor i, r := range results {\n\t\tif r.String() != expected[i] {\n\t\t\tt.Fatalf(\"expected %v, got %v\", expected[i], r.String())\n\t\t}\n\t}\n}\nfunc TestIssue58(t *testing.T) {\n\tjson := `{\"data\":[{\"uid\": 1},{\"uid\": 2}]}`\n\tres := Get(json, `data.#[uid!=1]`).Raw\n\tif res != `{\"uid\": 2}` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", `{\"uid\": 1}`, res)\n\t}\n}\n\nfunc TestObjectGrouping(t *testing.T) {\n\tjson := `\n[\n\ttrue,\n\t{\"name\":\"tom\"},\n\tfalse,\n\t{\"name\":\"janet\"},\n\tnull\n]\n`\n\tres := Get(json, \"#.name\")\n\tif res.String() != `[\"tom\",\"janet\"]` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", `[\"tom\",\"janet\"]`, res.String())\n\t}\n}\n\nfunc TestJSONLines(t *testing.T) {\n\tjson := `\ntrue\nfalse\n{\"name\":\"tom\"}\n[1,2,3,4,5]\n{\"name\":\"janet\"}\nnull\n12930.1203\n\t`\n\tpaths := []string{\"..#\", \"..0\", \"..2.name\", \"..#.name\", \"..6\", \"..7\"}\n\tress := []string{\"7\", \"true\", \"tom\", `[\"tom\",\"janet\"]`, \"12930.1203\", \"\"}\n\tfor i, path := range paths {\n\t\tres := Get(json, path)\n\t\tif res.String() != ress[i] {\n\t\t\tt.Fatalf(\"expected '%v', got '%v'\", ress[i], res.String())\n\t\t}\n\t}\n\n\tjson = `\n{\"name\": \"Gilbert\", \"wins\": [[\"straight\", \"7♣\"], [\"one pair\", \"10♥\"]]}\n{\"name\": \"Alexa\", \"wins\": [[\"two pair\", \"4♠\"], [\"two pair\", \"9♠\"]]}\n{\"name\": \"May\", \"wins\": []}\n{\"name\": \"Deloise\", \"wins\": [[\"three of a kind\", \"5♣\"]]}\n`\n\n\tvar i int\n\tlines := strings.Split(strings.TrimSpace(json), \"\\n\")\n\tForEachLine(json, func(line Result) bool {\n\t\tif line.Raw != lines[i] {\n\t\t\tt.Fatalf(\"expected '%v', got '%v'\", lines[i], line.Raw)\n\t\t}\n\t\ti++\n\t\treturn true\n\t})\n\tif i != 4 {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", 4, i)\n\t}\n\n}\n\nfunc TestNumUint64String(t *testing.T) {\n\tvar i int64 = 9007199254740993 //2^53 + 1\n\tj := fmt.Sprintf(`{\"data\":  [  %d, \"hello\" ] }`, i)\n\tres := Get(j, \"data.0\")\n\tif res.String() != \"9007199254740993\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"9007199254740993\", res.String())\n\t}\n}\n\nfunc TestNumInt64String(t *testing.T) {\n\tvar i int64 = -9007199254740993\n\tj := fmt.Sprintf(`{\"data\":[ \"hello\", %d ]}`, i)\n\tres := Get(j, \"data.1\")\n\tif res.String() != \"-9007199254740993\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"-9007199254740993\", res.String())\n\t}\n}\n\nfunc TestNumBigString(t *testing.T) {\n\ti := \"900719925474099301239109123101\" // very big\n\tj := fmt.Sprintf(`{\"data\":[ \"hello\", \"%s\" ]}`, i)\n\tres := Get(j, \"data.1\")\n\tif res.String() != \"900719925474099301239109123101\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"900719925474099301239109123101\",\n\t\t\tres.String())\n\t}\n}\n\nfunc TestNumFloatString(t *testing.T) {\n\tvar i int64 = -9007199254740993\n\tj := fmt.Sprintf(`{\"data\":[ \"hello\", %d ]}`, i) //No quotes around value!!\n\tres := Get(j, \"data.1\")\n\tif res.String() != \"-9007199254740993\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"-9007199254740993\", res.String())\n\t}\n}\n\nfunc TestDuplicateKeys(t *testing.T) {\n\t// this is valid json according to the JSON spec\n\tvar json = `{\"name\": \"Alex\",\"name\": \"Peter\"}`\n\tif Parse(json).Get(\"name\").String() !=\n\t\tParse(json).Map()[\"name\"].String() {\n\t\tt.Fatalf(\"expected '%v', got '%v'\",\n\t\t\tParse(json).Get(\"name\").String(),\n\t\t\tParse(json).Map()[\"name\"].String(),\n\t\t)\n\t}\n\tif !Valid(json) {\n\t\tt.Fatal(\"should be valid\")\n\t}\n}\n\nfunc TestArrayValues(t *testing.T) {\n\tvar json = `{\"array\": [\"PERSON1\",\"PERSON2\",0],}`\n\tvalues := Get(json, \"array\").Array()\n\tvar output string\n\tfor i, val := range values {\n\t\tif i > 0 {\n\t\t\toutput += \"\\n\"\n\t\t}\n\t\toutput += fmt.Sprintf(\"%#v\", val)\n\t}\n\texpect := strings.Join([]string{\n\t\t`gjson.Result{Type:3, Raw:\"\\\"PERSON1\\\"\", Str:\"PERSON1\", Num:0, ` +\n\t\t\t`Index:11, Indexes:[]int(nil)}`,\n\t\t`gjson.Result{Type:3, Raw:\"\\\"PERSON2\\\"\", Str:\"PERSON2\", Num:0, ` +\n\t\t\t`Index:21, Indexes:[]int(nil)}`,\n\t\t`gjson.Result{Type:2, Raw:\"0\", Str:\"\", Num:0, Index:31, Indexes:[]int(nil)}`,\n\t}, \"\\n\")\n\tif output != expect {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", expect, output)\n\t}\n\n}\n\nfunc BenchmarkValid(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tValid(complicatedJSON)\n\t}\n}\n\nfunc BenchmarkValidBytes(b *testing.B) {\n\tcomplicatedJSON := []byte(complicatedJSON)\n\tfor i := 0; i < b.N; i++ {\n\t\tValidBytes(complicatedJSON)\n\t}\n}\n\nfunc BenchmarkGoStdlibValidBytes(b *testing.B) {\n\tcomplicatedJSON := []byte(complicatedJSON)\n\tfor i := 0; i < b.N; i++ {\n\t\tjson.Valid(complicatedJSON)\n\t}\n}\n\nfunc TestModifier(t *testing.T) {\n\tjson := `{\"other\":{\"hello\":\"world\"},\"arr\":[1,2,3,4,5,6]}`\n\topts := *pretty.DefaultOptions\n\topts.SortKeys = true\n\texp := string(pretty.PrettyOptions([]byte(json), &opts))\n\tres := Get(json, `@pretty:{\"sortKeys\":true}`).String()\n\tif res != exp {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", exp, res)\n\t}\n\tres = Get(res, \"@pretty|@reverse|@ugly\").String()\n\tif res != json {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", json, res)\n\t}\n\tif res := Get(res, \"@this\").String(); res != json {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", json, res)\n\t}\n\tif res := Get(res, \"other.@this\").String(); res != `{\"hello\":\"world\"}` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", json, res)\n\t}\n\tres = Get(res, \"@pretty|@reverse|arr|@reverse|2\").String()\n\tif res != \"4\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"4\", res)\n\t}\n\tAddModifier(\"case\", func(json, arg string) string {\n\t\tif arg == \"upper\" {\n\t\t\treturn strings.ToUpper(json)\n\t\t}\n\t\tif arg == \"lower\" {\n\t\t\treturn strings.ToLower(json)\n\t\t}\n\t\treturn json\n\t})\n\tres = Get(json, \"other|@case:upper\").String()\n\tif res != `{\"HELLO\":\"WORLD\"}` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", `{\"HELLO\":\"WORLD\"}`, res)\n\t}\n}\n\nfunc TestChaining(t *testing.T) {\n\tjson := `{\n\t\t\"info\": {\n\t\t\t\"friends\": [\n\t\t\t\t{\"first\": \"Dale\", \"last\": \"Murphy\", \"age\": 44},\n\t\t\t\t{\"first\": \"Roger\", \"last\": \"Craig\", \"age\": 68},\n\t\t\t\t{\"first\": \"Jane\", \"last\": \"Murphy\", \"age\": 47}\n\t\t\t]\n\t\t}\n\t  }`\n\tres := Get(json, \"info.friends|0|first\").String()\n\tif res != \"Dale\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Dale\", res)\n\t}\n\tres = Get(json, \"info.friends|@reverse|0|age\").String()\n\tif res != \"47\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"47\", res)\n\t}\n\tres = Get(json, \"@ugly|i\\\\nfo|friends.0.first\").String()\n\tif res != \"Dale\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Dale\", res)\n\t}\n}\n\nfunc TestSplitPipe(t *testing.T) {\n\tsplit := func(t *testing.T, path, el, er string, eo bool) {\n\t\tt.Helper()\n\t\tleft, right, ok := splitPossiblePipe(path)\n\t\t// fmt.Printf(\"%-40s [%v] [%v] [%v]\\n\", path, left, right, ok)\n\t\tif left != el || right != er || ok != eo {\n\t\t\tt.Fatalf(\"expected '%v/%v/%v', got '%v/%v/%v\",\n\t\t\t\tel, er, eo, left, right, ok)\n\t\t}\n\t}\n\n\tsplit(t, \"hello\", \"\", \"\", false)\n\tsplit(t, \"hello.world\", \"\", \"\", false)\n\tsplit(t, \"hello|world\", \"hello\", \"world\", true)\n\tsplit(t, \"hello\\\\|world\", \"\", \"\", false)\n\tsplit(t, \"hello.#\", \"\", \"\", false)\n\tsplit(t, `hello.#[a|1=\"asdf\\\"|1324\"]#\\|that`, \"\", \"\", false)\n\tsplit(t, `hello.#[a|1=\"asdf\\\"|1324\"]#|that.more|yikes`,\n\t\t`hello.#[a|1=\"asdf\\\"|1324\"]#`, \"that.more|yikes\", true)\n\tsplit(t, `a.#[]#\\|b`, \"\", \"\", false)\n\n}\n\nfunc TestArrayEx(t *testing.T) {\n\tjson := `\n\t[\n\t\t{\n\t\t\t\"c\":[\n\t\t\t\t{\"a\":10.11}\n\t\t\t]\n\t\t}, {\n\t\t\t\"c\":[\n\t\t\t\t{\"a\":11.11}\n\t\t\t]\n\t\t}\n\t]`\n\tres := Get(json, \"@ugly|#.c.#[a=10.11]\").String()\n\tif res != `[{\"a\":10.11}]` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", `[{\"a\":10.11}]`, res)\n\t}\n\tres = Get(json, \"@ugly|#.c.#\").String()\n\tif res != `[1,1]` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", `[1,1]`, res)\n\t}\n\tres = Get(json, \"@reverse|0|c|0|a\").String()\n\tif res != \"11.11\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"11.11\", res)\n\t}\n\tres = Get(json, \"#.c|#\").String()\n\tif res != \"2\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"2\", res)\n\t}\n}\n\nfunc TestPipeDotMixing(t *testing.T) {\n\tjson := `{\n\t\t\"info\": {\n\t\t\t\"friends\": [\n\t\t\t\t{\"first\": \"Dale\", \"last\": \"Murphy\", \"age\": 44},\n\t\t\t\t{\"first\": \"Roger\", \"last\": \"Craig\", \"age\": 68},\n\t\t\t\t{\"first\": \"Jane\", \"last\": \"Murphy\", \"age\": 47}\n\t\t\t]\n\t\t}\n\t  }`\n\tvar res string\n\tres = Get(json, `info.friends.#[first=\"Dale\"].last`).String()\n\tif res != \"Murphy\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Murphy\", res)\n\t}\n\tres = Get(json, `info|friends.#[first=\"Dale\"].last`).String()\n\tif res != \"Murphy\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Murphy\", res)\n\t}\n\tres = Get(json, `info|friends.#[first=\"Dale\"]|last`).String()\n\tif res != \"Murphy\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Murphy\", res)\n\t}\n\tres = Get(json, `info|friends|#[first=\"Dale\"]|last`).String()\n\tif res != \"Murphy\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Murphy\", res)\n\t}\n\tres = Get(json, `@ugly|info|friends|#[first=\"Dale\"]|last`).String()\n\tif res != \"Murphy\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Murphy\", res)\n\t}\n\tres = Get(json, `@ugly|info.@ugly|friends|#[first=\"Dale\"]|last`).String()\n\tif res != \"Murphy\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Murphy\", res)\n\t}\n\tres = Get(json, `@ugly.info|@ugly.friends|#[first=\"Dale\"]|last`).String()\n\tif res != \"Murphy\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Murphy\", res)\n\t}\n}\n\nfunc TestDeepSelectors(t *testing.T) {\n\tjson := `{\n\t\t\"info\": {\n\t\t\t\"friends\": [\n\t\t\t\t{\n\t\t\t\t\t\"first\": \"Dale\", \"last\": \"Murphy\",\n\t\t\t\t\t\"extra\": [10,20,30],\n\t\t\t\t\t\"details\": {\n\t\t\t\t\t\t\"city\": \"Tempe\",\n\t\t\t\t\t\t\"state\": \"Arizona\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"first\": \"Roger\", \"last\": \"Craig\",\n\t\t\t\t\t\"extra\": [40,50,60],\n\t\t\t\t\t\"details\": {\n\t\t\t\t\t\t\"city\": \"Phoenix\",\n\t\t\t\t\t\t\"state\": \"Arizona\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t  }`\n\tvar res string\n\tres = Get(json, `info.friends.#[first=\"Dale\"].extra.0`).String()\n\tif res != \"10\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"10\", res)\n\t}\n\tres = Get(json, `info.friends.#[first=\"Dale\"].extra|0`).String()\n\tif res != \"10\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"10\", res)\n\t}\n\tres = Get(json, `info.friends.#[first=\"Dale\"]|extra|0`).String()\n\tif res != \"10\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"10\", res)\n\t}\n\tres = Get(json, `info.friends.#[details.city=\"Tempe\"].last`).String()\n\tif res != \"Murphy\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Murphy\", res)\n\t}\n\tres = Get(json, `info.friends.#[details.city=\"Phoenix\"].last`).String()\n\tif res != \"Craig\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Craig\", res)\n\t}\n\tres = Get(json, `info.friends.#[details.state=\"Arizona\"].last`).String()\n\tif res != \"Murphy\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Murphy\", res)\n\t}\n}\n\nfunc TestMultiArrayEx(t *testing.T) {\n\tjson := `{\n\t\t\"info\": {\n\t\t\t\"friends\": [\n\t\t\t\t{\n\t\t\t\t\t\"first\": \"Dale\", \"last\": \"Murphy\", \"kind\": \"Person\",\n\t\t\t\t\t\"cust1\": true,\n\t\t\t\t\t\"extra\": [10,20,30],\n\t\t\t\t\t\"details\": {\n\t\t\t\t\t\t\"city\": \"Tempe\",\n\t\t\t\t\t\t\"state\": \"Arizona\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"first\": \"Roger\", \"last\": \"Craig\", \"kind\": \"Person\",\n\t\t\t\t\t\"cust2\": false,\n\t\t\t\t\t\"extra\": [40,50,60],\n\t\t\t\t\t\"details\": {\n\t\t\t\t\t\t\"city\": \"Phoenix\",\n\t\t\t\t\t\t\"state\": \"Arizona\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t  }`\n\n\tvar res string\n\n\tres = Get(json, `info.friends.#[kind=\"Person\"]#.kind|0`).String()\n\tif res != \"Person\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Person\", res)\n\t}\n\tres = Get(json, `info.friends.#.kind|0`).String()\n\tif res != \"Person\" {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", \"Person\", res)\n\t}\n\n\tres = Get(json, `info.friends.#[kind=\"Person\"]#.kind`).String()\n\tif res != `[\"Person\",\"Person\"]` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", `[\"Person\",\"Person\"]`, res)\n\t}\n\tres = Get(json, `info.friends.#.kind`).String()\n\tif res != `[\"Person\",\"Person\"]` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", `[\"Person\",\"Person\"]`, res)\n\t}\n\n\tres = Get(json, `info.friends.#[kind=\"Person\"]#|kind`).String()\n\tif res != `` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", ``, res)\n\t}\n\tres = Get(json, `info.friends.#|kind`).String()\n\tif res != `` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", ``, res)\n\t}\n\n\tres = Get(json, `i*.f*.#[kind=\"Other\"]#`).String()\n\tif res != `[]` {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", `[]`, res)\n\t}\n}\n\nfunc TestQueries(t *testing.T) {\n\tjson := `{\n\t\t\"info\": {\n\t\t\t\"friends\": [\n\t\t\t\t{\n\t\t\t\t\t\"first\": \"Dale\", \"last\": \"Murphy\", \"kind\": \"Person\",\n\t\t\t\t\t\"cust1\": true,\n\t\t\t\t\t\"extra\": [10,20,30],\n\t\t\t\t\t\"details\": {\n\t\t\t\t\t\t\"city\": \"Tempe\",\n\t\t\t\t\t\t\"state\": \"Arizona\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"first\": \"Roger\", \"last\": \"Craig\", \"kind\": \"Person\",\n\t\t\t\t\t\"cust2\": false,\n\t\t\t\t\t\"extra\": [40,50,60],\n\t\t\t\t\t\"details\": {\n\t\t\t\t\t\t\"city\": \"Phoenix\",\n\t\t\t\t\t\t\"state\": \"Arizona\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t  }`\n\n\t// numbers\n\tassert(t, Get(json, \"i*.f*.#[extra.0<11].first\").Exists())\n\tassert(t, Get(json, \"i*.f*.#[extra.0<=11].first\").Exists())\n\tassert(t, !Get(json, \"i*.f*.#[extra.0<10].first\").Exists())\n\tassert(t, Get(json, \"i*.f*.#[extra.0<=10].first\").Exists())\n\tassert(t, Get(json, \"i*.f*.#[extra.0=10].first\").Exists())\n\tassert(t, !Get(json, \"i*.f*.#[extra.0=11].first\").Exists())\n\tassert(t, Get(json, \"i*.f*.#[extra.0!=10].first\").String() == \"Roger\")\n\tassert(t, Get(json, \"i*.f*.#[extra.0>10].first\").String() == \"Roger\")\n\tassert(t, Get(json, \"i*.f*.#[extra.0>=10].first\").String() == \"Dale\")\n\n\t// strings\n\tassert(t, Get(json, `i*.f*.#[extra.0<\"11\"].first`).Exists())\n\tassert(t, Get(json, `i*.f*.#[first>\"Dale\"].last`).String() == \"Craig\")\n\tassert(t, Get(json, `i*.f*.#[first>=\"Dale\"].last`).String() == \"Murphy\")\n\tassert(t, Get(json, `i*.f*.#[first=\"Dale\"].last`).String() == \"Murphy\")\n\tassert(t, Get(json, `i*.f*.#[first!=\"Dale\"].last`).String() == \"Craig\")\n\tassert(t, !Get(json, `i*.f*.#[first<\"Dale\"].last`).Exists())\n\tassert(t, Get(json, `i*.f*.#[first<=\"Dale\"].last`).Exists())\n\tassert(t, Get(json, `i*.f*.#[first%\"Da*\"].last`).Exists())\n\tassert(t, Get(json, `i*.f*.#[first%\"Dale\"].last`).Exists())\n\tassert(t, Get(json, `i*.f*.#[first%\"*a*\"]#|#`).String() == \"1\")\n\tassert(t, Get(json, `i*.f*.#[first%\"*e*\"]#|#`).String() == \"2\")\n\tassert(t, Get(json, `i*.f*.#[first!%\"*e*\"]#|#`).String() == \"0\")\n\n\t// trues\n\tassert(t, Get(json, `i*.f*.#[cust1=true].first`).String() == \"Dale\")\n\tassert(t, Get(json, `i*.f*.#[cust2=false].first`).String() == \"Roger\")\n\tassert(t, Get(json, `i*.f*.#[cust1!=false].first`).String() == \"Dale\")\n\tassert(t, Get(json, `i*.f*.#[cust2!=true].first`).String() == \"Roger\")\n\tassert(t, !Get(json, `i*.f*.#[cust1>true].first`).Exists())\n\tassert(t, Get(json, `i*.f*.#[cust1>=true].first`).Exists())\n\tassert(t, !Get(json, `i*.f*.#[cust2<false].first`).Exists())\n\tassert(t, Get(json, `i*.f*.#[cust2<=false].first`).Exists())\n\n}\n\nfunc TestQueryArrayValues(t *testing.T) {\n\tjson := `{\n\t\t\"artists\": [\n\t\t\t[\"Bob Dylan\"],\n\t\t\t\"John Lennon\",\n\t\t\t\"Mick Jagger\",\n\t\t\t\"Elton John\",\n\t\t\t\"Michael Jackson\",\n\t\t\t\"John Smith\",\n\t\t\ttrue,\n\t\t\t123,\n\t\t\t456,\n\t\t\tfalse,\n\t\t\tnull\n\t\t]\n\t}`\n\tassert(t, Get(json, `a*.#[0=\"Bob Dylan\"]#|#`).String() == \"1\")\n\tassert(t, Get(json, `a*.#[0=\"Bob Dylan 2\"]#|#`).String() == \"0\")\n\tassert(t, Get(json, `a*.#[%\"John*\"]#|#`).String() == \"2\")\n\tassert(t, Get(json, `a*.#[_%\"John*\"]#|#`).String() == \"0\")\n\tassert(t, Get(json, `a*.#[=\"123\"]#|#`).String() == \"1\")\n}\n\nfunc TestParenQueries(t *testing.T) {\n\tjson := `{\n\t\t\"friends\": [{\"a\":10},{\"a\":20},{\"a\":30},{\"a\":40}]\n\t}`\n\tassert(t, Get(json, \"friends.#(a>9)#|#\").Int() == 4)\n\tassert(t, Get(json, \"friends.#(a>10)#|#\").Int() == 3)\n\tassert(t, Get(json, \"friends.#(a>40)#|#\").Int() == 0)\n}\n\nfunc TestSubSelectors(t *testing.T) {\n\tjson := `{\n\t\t\"info\": {\n\t\t\t\"friends\": [\n\t\t\t\t{\n\t\t\t\t\t\"first\": \"Dale\", \"last\": \"Murphy\", \"kind\": \"Person\",\n\t\t\t\t\t\"cust1\": true,\n\t\t\t\t\t\"extra\": [10,20,30],\n\t\t\t\t\t\"details\": {\n\t\t\t\t\t\t\"city\": \"Tempe\",\n\t\t\t\t\t\t\"state\": \"Arizona\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"first\": \"Roger\", \"last\": \"Craig\", \"kind\": \"Person\",\n\t\t\t\t\t\"cust2\": false,\n\t\t\t\t\t\"extra\": [40,50,60],\n\t\t\t\t\t\"details\": {\n\t\t\t\t\t\t\"city\": \"Phoenix\",\n\t\t\t\t\t\t\"state\": \"Arizona\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t  }`\n\tassert(t, Get(json, \"[]\").String() == \"[]\")\n\tassert(t, Get(json, \"{}\").String() == \"{}\")\n\tres := Get(json, `{`+\n\t\t`abc:info.friends.0.first,`+\n\t\t`info.friends.1.last,`+\n\t\t`\"a`+\"\\r\"+`a\":info.friends.0.kind,`+\n\t\t`\"abc\":info.friends.1.kind,`+\n\t\t`{123:info.friends.1.cust2},`+\n\t\t`[info.friends.#[details.city=\"Phoenix\"]#|#]`+\n\t\t`}.@pretty.@ugly`).String()\n\t// println(res)\n\t// {\"abc\":\"Dale\",\"last\":\"Craig\",\"\\\"a\\ra\\\"\":\"Person\",\"_\":{\"123\":false},\"_\":[1]}\n\tassert(t, Get(res, \"abc\").String() == \"Dale\")\n\tassert(t, Get(res, \"last\").String() == \"Craig\")\n\tassert(t, Get(res, \"\\\"a\\ra\\\"\").String() == \"Person\")\n\tassert(t, Get(res, \"@reverse.abc\").String() == \"Person\")\n\tassert(t, Get(res, \"_.123\").String() == \"false\")\n\tassert(t, Get(res, \"@reverse._.0\").String() == \"1\")\n\tassert(t, Get(json, \"info.friends.[0.first,1.extra.0]\").String() ==\n\t\t`[\"Dale\",40]`)\n\tassert(t, Get(json, \"info.friends.#.[first,extra.0]\").String() ==\n\t\t`[[\"Dale\",10],[\"Roger\",40]]`)\n}\n\nfunc TestArrayCountRawOutput(t *testing.T) {\n\tassert(t, Get(`[1,2,3,4]`, \"#\").Raw == \"4\")\n}\n\nfunc TestParseQuery(t *testing.T) {\n\tvar path, op, value, remain string\n\tvar ok bool\n\n\tpath, op, value, remain, _, _, ok =\n\t\tparseQuery(`#(service_roles.#(==\"one\").()==asdf).cap`)\n\tassert(t, ok &&\n\t\tpath == `service_roles.#(==\"one\").()` &&\n\t\top == \"=\" &&\n\t\tvalue == `asdf` &&\n\t\tremain == `.cap`)\n\n\tpath, op, value, remain, _, _, ok = parseQuery(`#(first_name%\"Murphy\").last`)\n\tassert(t, ok &&\n\t\tpath == `first_name` &&\n\t\top == `%` &&\n\t\tvalue == `\"Murphy\"` &&\n\t\tremain == `.last`)\n\n\tpath, op, value, remain, _, _, ok = parseQuery(`#( first_name !% \"Murphy\" ).last`)\n\tassert(t, ok &&\n\t\tpath == `first_name` &&\n\t\top == `!%` &&\n\t\tvalue == `\"Murphy\"` &&\n\t\tremain == `.last`)\n\n\tpath, op, value, remain, _, _, ok = parseQuery(`#(service_roles.#(==\"one\"))`)\n\tassert(t, ok &&\n\t\tpath == `service_roles.#(==\"one\")` &&\n\t\top == `` &&\n\t\tvalue == `` &&\n\t\tremain == ``)\n\n\tpath, op, value, remain, _, _, ok =\n\t\tparseQuery(`#(a\\(\"\\\"(\".#(==\"o\\\"(ne\")%\"ab\\\")\").remain`)\n\tassert(t, ok &&\n\t\tpath == `a\\(\"\\\"(\".#(==\"o\\\"(ne\")` &&\n\t\top == \"%\" &&\n\t\tvalue == `\"ab\\\")\"` &&\n\t\tremain == `.remain`)\n}\n\nfunc TestParentSubQuery(t *testing.T) {\n\tvar json = `{\n\t\t\"topology\": {\n\t\t  \"instances\": [\n\t\t\t{\n\t\t\t  \"service_version\": \"1.2.3\",\n\t\t\t  \"service_locale\": {\"lang\": \"en\"},\n\t\t\t  \"service_roles\": [\"one\", \"two\"]\n\t\t\t},\n\t\t\t{\n\t\t\t  \"service_version\": \"1.2.4\",\n\t\t\t  \"service_locale\": {\"lang\": \"th\"},\n\t\t\t  \"service_roles\": [\"three\", \"four\"]\n\t\t\t},\n\t\t\t{\n\t\t\t  \"service_version\": \"1.2.2\",\n\t\t\t  \"service_locale\": {\"lang\": \"en\"},\n\t\t\t  \"service_roles\": [\"one\"]\n\t\t\t}\n\t\t  ]\n\t\t}\n\t  }`\n\tres := Get(json, `topology.instances.#( service_roles.#(==\"one\"))#.service_version`)\n\t// should return two instances\n\tassert(t, res.String() == `[\"1.2.3\",\"1.2.2\"]`)\n}\n\nfunc TestSingleModifier(t *testing.T) {\n\tvar data = `{\"@key\": \"value\"}`\n\tassert(t, Get(data, \"@key\").String() == \"value\")\n\tassert(t, Get(data, \"\\\\@key\").String() == \"value\")\n}\n\nfunc TestModifiersInMultipaths(t *testing.T) {\n\tAddModifier(\"case\", func(json, arg string) string {\n\t\tif arg == \"upper\" {\n\t\t\treturn strings.ToUpper(json)\n\t\t}\n\t\tif arg == \"lower\" {\n\t\t\treturn strings.ToLower(json)\n\t\t}\n\t\treturn json\n\t})\n\tjson := `{\"friends\": [\n\t\t{\"age\": 44, \"first\": \"Dale\", \"last\": \"Murphy\"},\n\t\t{\"age\": 68, \"first\": \"Roger\", \"last\": \"Craig\"},\n\t\t{\"age\": 47, \"first\": \"Jane\", \"last\": \"Murphy\"}\n\t]}`\n\n\tres := Get(json, `friends.#.{age,first|@case:upper}|@ugly`)\n\texp := `[{\"age\":44,\"@case:upper\":\"DALE\"},{\"age\":68,\"@case:upper\":\"ROGER\"},{\"age\":47,\"@case:upper\":\"JANE\"}]`\n\tassert(t, res.Raw == exp)\n\n\tres = Get(json, `{friends.#.{age,first:first|@case:upper}|0.first}`)\n\texp = `{\"first\":\"DALE\"}`\n\tassert(t, res.Raw == exp)\n\n\tres = Get(readmeJSON, `{\"children\":children|@case:upper,\"name\":name.first,\"age\":age}`)\n\texp = `{\"children\":[\"SARA\",\"ALEX\",\"JACK\"],\"name\":\"Tom\",\"age\":37}`\n\tassert(t, res.Raw == exp)\n}\n\nfunc TestIssue141(t *testing.T) {\n\tjson := `{\"data\": [{\"q\": 11, \"w\": 12}, {\"q\": 21, \"w\": 22}, {\"q\": 31, \"w\": 32} ], \"sql\": \"some stuff here\"}`\n\tassert(t, Get(json, \"data.#\").Int() == 3)\n\tassert(t, Get(json, \"data.#.{q}|@ugly\").Raw == `[{\"q\":11},{\"q\":21},{\"q\":31}]`)\n\tassert(t, Get(json, \"data.#.q|@ugly\").Raw == `[11,21,31]`)\n}\n\nfunc TestChainedModifierStringArgs(t *testing.T) {\n\t// issue #143\n\tAddModifier(\"push\", func(json, arg string) string {\n\t\tjson = strings.TrimSpace(json)\n\t\tif len(json) < 2 || !Parse(json).IsArray() {\n\t\t\treturn json\n\t\t}\n\t\tjson = strings.TrimSpace(json[1 : len(json)-1])\n\t\tif len(json) == 0 {\n\t\t\treturn \"[\" + arg + \"]\"\n\t\t}\n\t\treturn \"[\" + json + \",\" + arg + \"]\"\n\t})\n\tres := Get(\"[]\", `@push:\"2\"|@push:\"3\"|@push:{\"a\":\"b\",\"c\":[\"e\",\"f\"]}|@push:true|@push:10.23`)\n\tassert(t, res.String() == `[\"2\",\"3\",{\"a\":\"b\",\"c\":[\"e\",\"f\"]},true,10.23]`)\n}\n\nfunc TestFlatten(t *testing.T) {\n\tjson := `[1,[2],[3,4],[5,[6,[7]]],{\"hi\":\"there\"},8,[9]]`\n\tassert(t, Get(json, \"@flatten\").String() == `[1,2,3,4,5,[6,[7]],{\"hi\":\"there\"},8,9]`)\n\tassert(t, Get(json, `@flatten:{\"deep\":true}`).String() == `[1,2,3,4,5,6,7,{\"hi\":\"there\"},8,9]`)\n\tassert(t, Get(`{\"9999\":1234}`, \"@flatten\").String() == `{\"9999\":1234}`)\n}\n\nfunc TestJoin(t *testing.T) {\n\tassert(t, Get(`[{},{}]`, \"@join\").String() == `{}`)\n\tassert(t, Get(`[{\"a\":1},{\"b\":2}]`, \"@join\").String() == `{\"a\":1,\"b\":2}`)\n\tassert(t, Get(`[{\"a\":1,\"b\":1},{\"b\":2}]`, \"@join\").String() == `{\"a\":1,\"b\":2}`)\n\tassert(t, Get(`[{\"a\":1,\"b\":1},{\"b\":2},5,{\"c\":3}]`, \"@join\").String() == `{\"a\":1,\"b\":2,\"c\":3}`)\n\tassert(t, Get(`[{\"a\":1,\"b\":1},{\"b\":2},5,{\"c\":3}]`, `@join:{\"preserve\":true}`).String() == `{\"a\":1,\"b\":1,\"b\":2,\"c\":3}`)\n\tassert(t, Get(`[{\"a\":1,\"b\":1},{\"b\":2},5,{\"c\":3}]`, `@join:{\"preserve\":true}.b`).String() == `1`)\n\tassert(t, Get(`{\"9999\":1234}`, \"@join\").String() == `{\"9999\":1234}`)\n}\n\nfunc TestValid(t *testing.T) {\n\tassert(t, Get(\"[{}\", \"@valid\").Exists() == false)\n\tassert(t, Get(\"[{}]\", \"@valid\").Exists() == true)\n}\n\n// https://github.com/tidwall/gjson/issues/152\nfunc TestJoin152(t *testing.T) {\n\tvar json = `{\n\t\t\"distance\": 1374.0,\n\t\t\"validFrom\": \"2005-11-14\",\n\t\t\"historical\": {\n\t\t  \"type\": \"Day\",\n\t\t  \"name\": \"last25Hours\",\n\t\t  \"summary\": {\n\t\t\t\"units\": {\n\t\t\t  \"temperature\": \"C\",\n\t\t\t  \"wind\": \"m/s\",\n\t\t\t  \"snow\": \"cm\",\n\t\t\t  \"precipitation\": \"mm\"\n\t\t\t},\n\t\t\t\"days\": [\n\t\t\t  {\n\t\t\t\t\"time\": \"2020-02-08\",\n\t\t\t\t\"hours\": [\n\t\t\t\t  {\n\t\t\t\t\t\"temperature\": {\n\t\t\t\t\t  \"min\": -2.0,\n\t\t\t\t\t  \"max\": -1.6,\n\t\t\t\t\t  \"value\": -1.6\n\t\t\t\t\t},\n\t\t\t\t\t\"wind\": {},\n\t\t\t\t\t\"precipitation\": {},\n\t\t\t\t\t\"humidity\": {\n\t\t\t\t\t  \"value\": 92.0\n\t\t\t\t\t},\n\t\t\t\t\t\"snow\": {\n\t\t\t\t\t  \"depth\": 49.0\n\t\t\t\t\t},\n\t\t\t\t\t\"time\": \"2020-02-08T16:00:00+01:00\"\n\t\t\t\t  },\n\t\t\t\t  {\n\t\t\t\t\t\"temperature\": {\n\t\t\t\t\t  \"min\": -1.7,\n\t\t\t\t\t  \"max\": -1.3,\n\t\t\t\t\t  \"value\": -1.3\n\t\t\t\t\t},\n\t\t\t\t\t\"wind\": {},\n\t\t\t\t\t\"precipitation\": {},\n\t\t\t\t\t\"humidity\": {\n\t\t\t\t\t  \"value\": 92.0\n\t\t\t\t\t},\n\t\t\t\t\t\"snow\": {\n\t\t\t\t\t  \"depth\": 49.0\n\t\t\t\t\t},\n\t\t\t\t\t\"time\": \"2020-02-08T17:00:00+01:00\"\n\t\t\t\t  },\n\t\t\t\t  {\n\t\t\t\t\t\"temperature\": {\n\t\t\t\t\t  \"min\": -1.3,\n\t\t\t\t\t  \"max\": -0.9,\n\t\t\t\t\t  \"value\": -1.2\n\t\t\t\t\t},\n\t\t\t\t\t\"wind\": {},\n\t\t\t\t\t\"precipitation\": {},\n\t\t\t\t\t\"humidity\": {\n\t\t\t\t\t  \"value\": 91.0\n\t\t\t\t\t},\n\t\t\t\t\t\"snow\": {\n\t\t\t\t\t  \"depth\": 49.0\n\t\t\t\t\t},\n\t\t\t\t\t\"time\": \"2020-02-08T18:00:00+01:00\"\n\t\t\t\t  }\n\t\t\t\t]\n\t\t\t  },\n\t\t\t  {\n\t\t\t\t\"time\": \"2020-02-09\",\n\t\t\t\t\"hours\": [\n\t\t\t\t  {\n\t\t\t\t\t\"temperature\": {\n\t\t\t\t\t  \"min\": -1.7,\n\t\t\t\t\t  \"max\": -0.9,\n\t\t\t\t\t  \"value\": -1.5\n\t\t\t\t\t},\n\t\t\t\t\t\"wind\": {},\n\t\t\t\t\t\"precipitation\": {},\n\t\t\t\t\t\"humidity\": {\n\t\t\t\t\t  \"value\": 91.0\n\t\t\t\t\t},\n\t\t\t\t\t\"snow\": {\n\t\t\t\t\t  \"depth\": 49.0\n\t\t\t\t\t},\n\t\t\t\t\t\"time\": \"2020-02-09T00:00:00+01:00\"\n\t\t\t\t  },\n\t\t\t\t  {\n\t\t\t\t\t\"temperature\": {\n\t\t\t\t\t  \"min\": -1.5,\n\t\t\t\t\t  \"max\": 0.9,\n\t\t\t\t\t  \"value\": 0.2\n\t\t\t\t\t},\n\t\t\t\t\t\"wind\": {},\n\t\t\t\t\t\"precipitation\": {},\n\t\t\t\t\t\"humidity\": {\n\t\t\t\t\t  \"value\": 67.0\n\t\t\t\t\t},\n\t\t\t\t\t\"snow\": {\n\t\t\t\t\t  \"depth\": 49.0\n\t\t\t\t\t},\n\t\t\t\t\t\"time\": \"2020-02-09T01:00:00+01:00\"\n\t\t\t\t  }\n\t\t\t\t]\n\t\t\t  }\n\t\t\t]\n\t\t  }\n\t\t}\n\t  }`\n\n\tres := Get(json, \"historical.summary.days.#.hours|@flatten|#.humidity.value\")\n\tassert(t, res.Raw == `[92.0,92.0,91.0,91.0,67.0]`)\n}\n\nfunc TestVariousFuzz(t *testing.T) {\n\t// Issue #192\tassert(t, squash(`\"000\"hello`) == `\"000\"`)\n\tassert(t, squash(`\"000\"`) == `\"000\"`)\n\tassert(t, squash(`\"000`) == `\"000`)\n\tassert(t, squash(`\"`) == `\"`)\n\n\tassert(t, squash(`[000]hello`) == `[000]`)\n\tassert(t, squash(`[000]`) == `[000]`)\n\tassert(t, squash(`[000`) == `[000`)\n\tassert(t, squash(`[`) == `[`)\n\tassert(t, squash(`]`) == `]`)\n\n\ttestJSON := `0.#[[{}]].@valid:\"000`\n\tGet(testJSON, testJSON)\n\n\t// Issue #195\n\ttestJSON = `\\************************************` +\n\t\t`**********{**\",**,,**,**,**,**,\"\",**,**,**,**,**,**,**,**,**,**]`\n\tGet(testJSON, testJSON)\n\n\t// Issue #196\n\ttestJSON = `[#.@pretty.@join:{\"\"[]\"\"preserve\"3,\"][{]]]`\n\tGet(testJSON, testJSON)\n\n\t// Issue #237\n\ttestJSON1 := `[\"*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,,,,,,\"]`\n\ttestJSON2 := `#[%\"*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,,,,,,\"\"*,*\"]`\n\tGet(testJSON1, testJSON2)\n\n}\n\nfunc TestSubpathsWithMultipaths(t *testing.T) {\n\tconst json = `\n[\n  {\"a\": 1},\n  {\"a\": 2, \"values\": [\"a\", \"b\", \"c\", \"d\", \"e\"]},\n  true,\n  [\"a\", \"b\", \"c\", \"d\", \"e\"],\n  4\n]\n`\n\tassert(t, Get(json, `1.values.@ugly`).Raw == `[\"a\",\"b\",\"c\",\"d\",\"e\"]`)\n\tassert(t, Get(json, `1.values.[0,3]`).Raw == `[\"a\",\"d\"]`)\n\tassert(t, Get(json, `3.@ugly`).Raw == `[\"a\",\"b\",\"c\",\"d\",\"e\"]`)\n\tassert(t, Get(json, `3.[0,3]`).Raw == `[\"a\",\"d\"]`)\n\tassert(t, Get(json, `#.@ugly`).Raw == `[{\"a\":1},{\"a\":2,\"values\":[\"a\",\"b\",\"c\",\"d\",\"e\"]},true,[\"a\",\"b\",\"c\",\"d\",\"e\"],4]`)\n\tassert(t, Get(json, `#.[0,3]`).Raw == `[[],[],[],[\"a\",\"d\"],[]]`)\n}\n\nfunc TestFlattenRemoveNonExist(t *testing.T) {\n\traw := Get(\"[[1],[2,[[],[3]],[4,[5],[],[[[6]]]]]]\", `@flatten:{\"deep\":true}`).Raw\n\tassert(t, raw == \"[1,2,3,4,5,6]\")\n}\n\nfunc TestPipeEmptyArray(t *testing.T) {\n\traw := Get(\"[]\", `#(hello)#`).Raw\n\tassert(t, raw == \"[]\")\n}\n\nfunc TestEncodedQueryString(t *testing.T) {\n\tjson := `{\n\t\t\"friends\": [\n\t\t\t{\"first\": \"Dale\", \"last\": \"Mur\\nphy\", \"age\": 44},\n\t\t\t{\"first\": \"Roger\", \"last\": \"Craig\", \"age\": 68},\n\t\t\t{\"first\": \"Jane\", \"last\": \"Murphy\", \"age\": 47}\n\t\t]\n\t}`\n\tassert(t, Get(json, `friends.#(last==\"Mur\\nphy\").age`).Int() == 44)\n\tassert(t, Get(json, `friends.#(last==\"Murphy\").age`).Int() == 47)\n}\n\nfunc TestTildeQueries(t *testing.T) {\n\tjson := `{\n\t\t\"vals\": [\n\t\t\t{ \"a\": 1, \"b\": \"data\" },\n\t\t\t{ \"a\": 2, \"b\": true },\n\t\t\t{ \"a\": 3, \"b\": false },\n\t\t\t{ \"a\": 4, \"b\": \"0\" },\n\t\t\t{ \"a\": 5, \"b\": 0 },\n\t\t\t{ \"a\": 6, \"b\": \"1\" },\n\t\t\t{ \"a\": 7, \"b\": 1 },\n\t\t\t{ \"a\": 8, \"b\": \"true\" },\n\t\t\t{ \"a\": 9, \"b\": false },\n\t\t\t{ \"a\": 10, \"b\": null },\n\t\t\t{ \"a\": 11 }\n\t\t]\n\t}`\n\ttrues := Get(json, `vals.#(b==~true)#.a`).Raw\n\ttruesNOT := Get(json, `vals.#(b!=~true)#.a`).Raw\n\tfalses := Get(json, `vals.#(b==~false)#.a`).Raw\n\tfalsesNOT := Get(json, `vals.#(b!=~false)#.a`).Raw\n\tnulls := Get(json, `vals.#(b==~null)#.a`).Raw\n\tnullsNOT := Get(json, `vals.#(b!=~null)#.a`).Raw\n\texists := Get(json, `vals.#(b==~*)#.a`).Raw\n\texistsNOT := Get(json, `vals.#(b!=~*)#.a`).Raw\n\n\tassert(t, trues == \"[2,6,7,8]\")\n\tassert(t, truesNOT == \"[1,3,4,5,9,10,11]\")\n\tassert(t, falses == \"[3,4,5,9,10,11]\")\n\tassert(t, falsesNOT == \"[1,2,6,7,8]\")\n\tassert(t, nulls == \"[10,11]\")\n\tassert(t, nullsNOT == \"[1,2,3,4,5,6,7,8,9]\")\n\tassert(t, exists == \"[1,2,3,4,5,6,7,8,9,10]\")\n\tassert(t, existsNOT == \"[11]\")\n\tjson = `{\n\t\t\"vals\": [\n\t\t  { \"a\": 1, \"b\": \"something\" },\n\t\t  { \"a\": 2, \"b\": \"else\" },\n\t\t  { \"a\": 3, \"b\": false },\n\t\t  { \"a\": 4, \"b\": \"0\" },\n\t\t  { \"a\": 5, \"b\": 0 },\n\t\t  { \"a\": 6, \"b\": \"1\" },\n\t\t  { \"a\": 7, \"b\": 1 },\n\t\t  { \"a\": 8, \"b\": \"true\" },\n\t\t  { \"a\": 9, \"b\": false },\n\t\t  { \"a\": 10, \"b\": null },\n\t\t  { \"a\": 11 }\n\t\t],\n\t\t\"anything\": \"else\"\n\t}`\n\ttrues = Get(json, `vals.#(b==~true)#.a`).Raw\n\ttruesNOT = Get(json, `vals.#(b!=~true)#.a`).Raw\n\tfalses = Get(json, `vals.#(b==~false)#.a`).Raw\n\tfalsesNOT = Get(json, `vals.#(b!=~false)#.a`).Raw\n\tnulls = Get(json, `vals.#(b==~null)#.a`).Raw\n\tnullsNOT = Get(json, `vals.#(b!=~null)#.a`).Raw\n\texists = Get(json, `vals.#(b==~*)#.a`).Raw\n\texistsNOT = Get(json, `vals.#(b!=~*)#.a`).Raw\n\tassert(t, trues == \"[6,7,8]\")\n\tassert(t, truesNOT == \"[1,2,3,4,5,9,10,11]\")\n\tassert(t, falses == \"[3,4,5,9,10,11]\")\n\tassert(t, falsesNOT == \"[1,2,6,7,8]\")\n\tassert(t, nulls == \"[10,11]\")\n\tassert(t, nullsNOT == \"[1,2,3,4,5,6,7,8,9]\")\n\tassert(t, exists == \"[1,2,3,4,5,6,7,8,9,10]\")\n\tassert(t, existsNOT == \"[11]\")\n}\n\nfunc TestModifierDoubleQuotes(t *testing.T) {\n\tjosn := `{\n\t\t\"data\": [\n\t\t  {\n\t\t\t\"name\": \"Product P4\",\n\t\t\t\"productId\": \"1bb3\",\n\t\t\t\"vendorId\": \"10de\"\n\t\t  },\n\t\t  {\n\t\t\t\"name\": \"Product P4\",\n\t\t\t\"productId\": \"1cc3\",\n\t\t\t\"vendorId\": \"20de\"\n\t\t  },\n\t\t  {\n\t\t\t\"name\": \"Product P4\",\n\t\t\t\"productId\": \"1dd3\",\n\t\t\t\"vendorId\": \"30de\"\n\t\t  }\n\t\t]\n\t  }`\n\tAddModifier(\"string\", func(josn, arg string) string {\n\t\treturn strconv.Quote(josn)\n\t})\n\n\tres := Get(josn, \"data.#.{name,value:{productId,vendorId}.@string.@ugly}\")\n\n\tassert(t, res.Raw == `[`+\n\t\t`{\"name\":\"Product P4\",\"value\":\"{\\\"productId\\\":\\\"1bb3\\\",\\\"vendorId\\\":\\\"10de\\\"}\"},`+\n\t\t`{\"name\":\"Product P4\",\"value\":\"{\\\"productId\\\":\\\"1cc3\\\",\\\"vendorId\\\":\\\"20de\\\"}\"},`+\n\t\t`{\"name\":\"Product P4\",\"value\":\"{\\\"productId\\\":\\\"1dd3\\\",\\\"vendorId\\\":\\\"30de\\\"}\"}`+\n\t\t`]`)\n\n}\n\nfunc TestIndexes(t *testing.T) {\n\tvar exampleJSON = `{\n\t\t\"vals\": [\n\t\t\t[1,66,{test: 3}],\n\t\t\t[4,5,[6]]\n\t\t],\n\t\t\"objectArray\":[\n\t\t\t{\"first\": \"Dale\", \"age\": 44},\n\t\t\t{\"first\": \"Roger\", \"age\": 68},\n\t\t]\n\t}`\n\n\ttestCases := []struct {\n\t\tpath     string\n\t\texpected []string\n\t}{\n\t\t{\n\t\t\t`vals.#.1`,\n\t\t\t[]string{`6`, \"5\"},\n\t\t},\n\t\t{\n\t\t\t`vals.#.2`,\n\t\t\t[]string{\"{\", \"[\"},\n\t\t},\n\t\t{\n\t\t\t`objectArray.#(age>43)#.first`,\n\t\t\t[]string{`\"`, `\"`},\n\t\t},\n\t\t{\n\t\t\t`objectArray.@reverse.#.first`,\n\t\t\tnil,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tr := Get(exampleJSON, tc.path)\n\n\t\tassert(t, len(r.Indexes) == len(tc.expected))\n\n\t\tfor i, a := range r.Indexes {\n\t\t\tassert(t, string(exampleJSON[a]) == tc.expected[i])\n\t\t}\n\t}\n}\n\nfunc TestIndexesMatchesRaw(t *testing.T) {\n\tvar exampleJSON = `{\n\t\t\"objectArray\":[\n\t\t\t{\"first\": \"Jason\", \"age\": 41},\n\t\t\t{\"first\": \"Dale\", \"age\": 44},\n\t\t\t{\"first\": \"Roger\", \"age\": 68},\n\t\t\t{\"first\": \"Mandy\", \"age\": 32}\n\t\t]\n\t}`\n\tr := Get(exampleJSON, `objectArray.#(age>43)#.first`)\n\tassert(t, len(r.Indexes) == 2)\n\tassert(t, Parse(exampleJSON[r.Indexes[0]:]).String() == \"Dale\")\n\tassert(t, Parse(exampleJSON[r.Indexes[1]:]).String() == \"Roger\")\n\tr = Get(exampleJSON, `objectArray.#(age>43)#`)\n\tassert(t, Parse(exampleJSON[r.Indexes[0]:]).Get(\"first\").String() == \"Dale\")\n\tassert(t, Parse(exampleJSON[r.Indexes[1]:]).Get(\"first\").String() == \"Roger\")\n}\n\nfunc TestIssue240(t *testing.T) {\n\tnonArrayData := `{\"jsonrpc\":\"2.0\",\"method\":\"subscription\",\"params\":\n\t\t{\"channel\":\"funny_channel\",\"data\":\n\t\t\t{\"name\":\"Jason\",\"company\":\"good_company\",\"number\":12345}\n\t\t}\n\t}`\n\tparsed := Parse(nonArrayData)\n\tassert(t, len(parsed.Get(\"params.data\").Array()) == 1)\n\n\tarrayData := `{\"jsonrpc\":\"2.0\",\"method\":\"subscription\",\"params\":\n\t\t{\"channel\":\"funny_channel\",\"data\":[\n\t\t\t{\"name\":\"Jason\",\"company\":\"good_company\",\"number\":12345}\n\t\t]}\n\t}`\n\tparsed = Parse(arrayData)\n\tassert(t, len(parsed.Get(\"params.data\").Array()) == 1)\n}\n\nfunc TestKeysValuesModifier(t *testing.T) {\n\tvar json = `{\n\t\t\"1300014\": {\n\t\t  \"code\": \"1300014\",\n\t\t  \"price\": 59.18,\n\t\t  \"symbol\": \"300014\",\n\t\t  \"update\": \"2020/04/15 15:59:54\",\n\t\t},\n\t\t\"1300015\": {\n\t\t  \"code\": \"1300015\",\n\t\t  \"price\": 43.31,\n\t\t  \"symbol\": \"300015\",\n\t\t  \"update\": \"2020/04/15 15:59:54\",\n\t\t}\n\t  }`\n\tassert(t, Get(json, `@keys`).String() == `[\"1300014\",\"1300015\"]`)\n\tassert(t, Get(``, `@keys`).String() == `[]`)\n\tassert(t, Get(`\"hello\"`, `@keys`).String() == `[null]`)\n\tassert(t, Get(`[]`, `@keys`).String() == `[]`)\n\tassert(t, Get(`[1,2,3]`, `@keys`).String() == `[null,null,null]`)\n\n\tassert(t, Get(json, `@values.#.code`).String() == `[\"1300014\",\"1300015\"]`)\n\tassert(t, Get(``, `@values`).String() == `[]`)\n\tassert(t, Get(`\"hello\"`, `@values`).String() == `[\"hello\"]`)\n\tassert(t, Get(`[]`, `@values`).String() == `[]`)\n\tassert(t, Get(`[1,2,3]`, `@values`).String() == `[1,2,3]`)\n}\n\nfunc TestNaNInf(t *testing.T) {\n\tjson := `[+Inf,-Inf,Inf,iNF,-iNF,+iNF,NaN,nan,nAn,-0,+0]`\n\traws := []string{\"+Inf\", \"-Inf\", \"Inf\", \"iNF\", \"-iNF\", \"+iNF\", \"NaN\", \"nan\",\n\t\t\"nAn\", \"-0\", \"+0\"}\n\tnums := []float64{math.Inf(+1), math.Inf(-1), math.Inf(0), math.Inf(0),\n\t\tmath.Inf(-1), math.Inf(+1), math.NaN(), math.NaN(), math.NaN(),\n\t\tmath.Copysign(0, -1), 0}\n\n\tassert(t, int(Get(json, `#`).Int()) == len(raws))\n\tfor i := 0; i < len(raws); i++ {\n\t\tr := Get(json, fmt.Sprintf(\"%d\", i))\n\t\tassert(t, r.Raw == raws[i])\n\t\tassert(t, r.Num == nums[i] || (math.IsNaN(r.Num) && math.IsNaN(nums[i])))\n\t\tassert(t, r.Type == Number)\n\t}\n\n\tvar i int\n\tParse(json).ForEach(func(_, r Result) bool {\n\t\tassert(t, r.Raw == raws[i])\n\t\tassert(t, r.Num == nums[i] || (math.IsNaN(r.Num) && math.IsNaN(nums[i])))\n\t\tassert(t, r.Type == Number)\n\t\ti++\n\t\treturn true\n\t})\n\n\t// Parse should also return valid numbers\n\tassert(t, math.IsNaN(Parse(\"nan\").Float()))\n\tassert(t, math.IsNaN(Parse(\"NaN\").Float()))\n\tassert(t, math.IsNaN(Parse(\" NaN\").Float()))\n\tassert(t, math.IsInf(Parse(\"+inf\").Float(), +1))\n\tassert(t, math.IsInf(Parse(\"-inf\").Float(), -1))\n\tassert(t, math.IsInf(Parse(\"+INF\").Float(), +1))\n\tassert(t, math.IsInf(Parse(\"-INF\").Float(), -1))\n\tassert(t, math.IsInf(Parse(\" +INF\").Float(), +1))\n\tassert(t, math.IsInf(Parse(\" -INF\").Float(), -1))\n}\n\nfunc TestEmptyValueQuery(t *testing.T) {\n\t// issue: https://github.com/tidwall/gjson/issues/246\n\tassert(t, Get(\n\t\t`[\"ig\",\"\",\"tw\",\"fb\",\"tw\",\"ig\",\"tw\"]`,\n\t\t`#(!=\"\")#`).Raw ==\n\t\t`[\"ig\",\"tw\",\"fb\",\"tw\",\"ig\",\"tw\"]`)\n\tassert(t, Get(\n\t\t`[\"ig\",\"\",\"tw\",\"fb\",\"tw\",\"ig\",\"tw\"]`,\n\t\t`#(!=)#`).Raw ==\n\t\t`[\"ig\",\"tw\",\"fb\",\"tw\",\"ig\",\"tw\"]`)\n}\n\nfunc TestParseIndex(t *testing.T) {\n\tassert(t, Parse(`{}`).Index == 0)\n\tassert(t, Parse(` {}`).Index == 1)\n\tassert(t, Parse(` []`).Index == 1)\n\tassert(t, Parse(` true`).Index == 1)\n\tassert(t, Parse(` false`).Index == 1)\n\tassert(t, Parse(` null`).Index == 1)\n\tassert(t, Parse(` +inf`).Index == 1)\n\tassert(t, Parse(` -inf`).Index == 1)\n}\n\nfunc TestRevSquash(t *testing.T) {\n\tassert(t, revSquash(` {}`) == `{}`)\n\tassert(t, revSquash(` }`) == ` }`)\n\tassert(t, revSquash(` [123]`) == `[123]`)\n\tassert(t, revSquash(` ,123,123]`) == ` ,123,123]`)\n\tassert(t, revSquash(` hello,[[true,false],[0,1,2,3,5],[123]]`) == `[[true,false],[0,1,2,3,5],[123]]`)\n\tassert(t, revSquash(` \"hello\"`) == `\"hello\"`)\n\tassert(t, revSquash(` \"hel\\\\lo\"`) == `\"hel\\\\lo\"`)\n\tassert(t, revSquash(` \"hel\\\\\"lo\"`) == `\"lo\"`)\n\tassert(t, revSquash(` \"hel\\\\\\\"lo\"`) == `\"hel\\\\\\\"lo\"`)\n\tassert(t, revSquash(`hel\\\\\\\"lo\"`) == `hel\\\\\\\"lo\"`)\n\tassert(t, revSquash(`\\\"hel\\\\\\\"lo\"`) == `\\\"hel\\\\\\\"lo\"`)\n\tassert(t, revSquash(`\\\\\\\"hel\\\\\\\"lo\"`) == `\\\\\\\"hel\\\\\\\"lo\"`)\n\tassert(t, revSquash(`\\\\\\\\\"hel\\\\\\\"lo\"`) == `\"hel\\\\\\\"lo\"`)\n\tassert(t, revSquash(`hello\"`) == `hello\"`)\n\tjson := `true,[0,1,\"sadf\\\"asdf\",{\"hi\":[\"hello\",\"t\\\"\\\"u\",{\"a\":\"b\"}]},9]`\n\tassert(t, revSquash(json) == json[5:])\n\tassert(t, revSquash(json[:len(json)-3]) == `{\"hi\":[\"hello\",\"t\\\"\\\"u\",{\"a\":\"b\"}]}`)\n\tassert(t, revSquash(json[:len(json)-4]) == `[\"hello\",\"t\\\"\\\"u\",{\"a\":\"b\"}]`)\n\tassert(t, revSquash(json[:len(json)-5]) == `{\"a\":\"b\"}`)\n\tassert(t, revSquash(json[:len(json)-6]) == `\"b\"`)\n\tassert(t, revSquash(json[:len(json)-10]) == `\"a\"`)\n\tassert(t, revSquash(json[:len(json)-15]) == `\"t\\\"\\\"u\"`)\n\tassert(t, revSquash(json[:len(json)-24]) == `\"hello\"`)\n\tassert(t, revSquash(json[:len(json)-33]) == `\"hi\"`)\n\tassert(t, revSquash(json[:len(json)-39]) == `\"sadf\\\"asdf\"`)\n}\n\nconst readmeJSON = `\n{\n  \"name\": {\"first\": \"Tom\", \"last\": \"Anderson\"},\n  \"age\":37,\n  \"children\": [\"Sara\",\"Alex\",\"Jack\"],\n  \"fav.movie\": \"Deer Hunter\",\n  \"friends\": [\n    {\"first\": \"Dale\", \"last\": \"Murphy\", \"age\": 44, \"nets\": [\"ig\", \"fb\", \"tw\"]},\n    {\"first\": \"Roger\", \"last\": \"Craig\", \"age\": 68, \"nets\": [\"fb\", \"tw\"]},\n    {\"first\": \"Jane\", \"last\": \"Murphy\", \"age\": 47, \"nets\": [\"ig\", \"tw\"]}\n  ]\n}\n`\n\nfunc TestQueryGetPath(t *testing.T) {\n\tassert(t, strings.Join(\n\t\tGet(readmeJSON, \"friends.#.first\").Paths(readmeJSON), \" \") ==\n\t\t\"friends.0.first friends.1.first friends.2.first\")\n\tassert(t, strings.Join(\n\t\tGet(readmeJSON, \"friends.#(last=Murphy)\").Paths(readmeJSON), \" \") ==\n\t\t\"\")\n\tassert(t, Get(readmeJSON, \"friends.#(last=Murphy)\").Path(readmeJSON) ==\n\t\t\"friends.0\")\n\tassert(t, strings.Join(\n\t\tGet(readmeJSON, \"friends.#(last=Murphy)#\").Paths(readmeJSON), \" \") ==\n\t\t\"friends.0 friends.2\")\n\tarr := Get(readmeJSON, \"friends.#.first\").Array()\n\tfor i := 0; i < len(arr); i++ {\n\t\tassert(t, arr[i].Path(readmeJSON) == fmt.Sprintf(\"friends.%d.first\", i))\n\t}\n}\n\nfunc TestStaticJSON(t *testing.T) {\n\tjson := `{\n\t\t\"name\": {\"first\": \"Tom\", \"last\": \"Anderson\"}\n\t}`\n\tassert(t, Get(json,\n\t\t`\"bar\"`).Raw ==\n\t\t``)\n\tassert(t, Get(json,\n\t\t`!\"bar\"`).Raw ==\n\t\t`\"bar\"`)\n\tassert(t, Get(json,\n\t\t`!{\"name\":{\"first\":\"Tom\"}}.{name.first}.first`).Raw ==\n\t\t`\"Tom\"`)\n\tassert(t, Get(json,\n\t\t`{name.last,\"foo\":!\"bar\"}`).Raw ==\n\t\t`{\"last\":\"Anderson\",\"foo\":\"bar\"}`)\n\tassert(t, Get(json,\n\t\t`{name.last,\"foo\":!{\"a\":\"b\"},\"that\"}`).Raw ==\n\t\t`{\"last\":\"Anderson\",\"foo\":{\"a\":\"b\"}}`)\n\tassert(t, Get(json,\n\t\t`{name.last,\"foo\":!{\"c\":\"d\"},!\"that\"}`).Raw ==\n\t\t`{\"last\":\"Anderson\",\"foo\":{\"c\":\"d\"},\"_\":\"that\"}`)\n\tassert(t, Get(json,\n\t\t`[!true,!false,!null,!inf,!nan,!hello,{\"name\":!\"andy\",name.last},+inf,![\"any\",\"thing\"]]`).Raw ==\n\t\t`[true,false,null,inf,nan,{\"name\":\"andy\",\"last\":\"Anderson\"},[\"any\",\"thing\"]]`,\n\t)\n}\n\nfunc TestArrayKeys(t *testing.T) {\n\tN := 100\n\tjson := \"[\"\n\tfor i := 0; i < N; i++ {\n\t\tif i > 0 {\n\t\t\tjson += \",\"\n\t\t}\n\t\tjson += fmt.Sprint(i)\n\t}\n\tjson += \"]\"\n\tvar i int\n\tParse(json).ForEach(func(key, value Result) bool {\n\t\tassert(t, key.String() == fmt.Sprint(i))\n\t\tassert(t, key.Int() == int64(i))\n\t\ti++\n\t\treturn true\n\t})\n\tassert(t, i == N)\n}\n\nfunc TestToFromStr(t *testing.T) {\n\tjson := `{\"Message\":\"{\\\"Records\\\":[{\\\"eventVersion\\\":\\\"2.1\\\"}]\"}`\n\tres := Get(json, \"Message.@fromstr.Records.#.eventVersion.@tostr\").Raw\n\tassert(t, res == `[\"\\\"2.1\\\"\"]`)\n}\n\nfunc TestGroup(t *testing.T) {\n\tjson := `{\"id\":[\"123\",\"456\",\"789\"],\"val\":[2,1]}`\n\tres := Get(json, \"@group\").Raw\n\tassert(t, res == `[{\"id\":\"123\",\"val\":2},{\"id\":\"456\",\"val\":1},{\"id\":\"789\"}]`)\n\n\tjson = `\n{\n\t\"issues\": [\n\t  {\n\t\t\"fields\": {\n\t\t  \"labels\": [\n\t\t\t\"milestone_1\",\n\t\t\t\"group:foo\",\n\t\t\t\"plan:a\",\n\t\t\t\"plan:b\"\n\t\t  ]\n\t\t},\n\t\t\"id\": \"123\"\n\t  },{\n\t\t\"fields\": {\n\t\t  \"labels\": [\n\t\t\t\"milestone_1\",\n\t\t\t\"group:foo\",\n\t\t\t\"plan:a\",\n\t\t\t\"plan\"\n\t\t  ]\n\t\t},\n\t\t\"id\": \"456\"\n\t  }\n\t]\n  }\n  `\n\tres = Get(json, `{\"id\":issues.#.id,\"plans\":issues.#.fields.labels.#(%\"plan:*\")#|#.#}|@group|#(plans>=2)#.id`).Raw\n\tassert(t, res == `[\"123\"]`)\n}\n\nfunc goJSONMarshal(i interface{}) ([]byte, error) {\n\tbuffer := &bytes.Buffer{}\n\tencoder := json.NewEncoder(buffer)\n\tencoder.SetEscapeHTML(!DisableEscapeHTML)\n\terr := encoder.Encode(i)\n\treturn bytes.TrimRight(buffer.Bytes(), \"\\n\"), err\n}\n\nfunc testJSONString(t *testing.T, str string) {\n\tgjsonString := string(AppendJSONString(nil, str))\n\tdata, err := goJSONMarshal(str)\n\tif err != nil {\n\t\tpanic(123)\n\t}\n\tgoString := string(data)\n\tif gjsonString != goString {\n\t\tt.Fatal(strconv.Quote(str) + \"\\n\\t\" +\n\t\t\tgjsonString + \"\\n\\t\" +\n\t\t\tgoString + \"\\n\\t<<< MISMATCH >>>\")\n\t}\n}\n\nfunc TestJSONString(t *testing.T) {\n\ttestJSONString(t, \"hello\")\n\ttestJSONString(t, \"he\\\"llo\")\n\ttestJSONString(t, \"he\\\"l\\\\lo\")\n\tconst input = `{\"utf8\":\"Example emoji, KO: \\ud83d\\udd13, \\ud83c\\udfc3 ` +\n\t\t`OK: \\u2764\\ufe0f \"}`\n\tvalue := Get(input, \"utf8\")\n\tvar s string\n\tjson.Unmarshal([]byte(value.Raw), &s)\n\tif value.String() != s {\n\t\tt.Fatalf(\"expected '%v', got '%v'\", s, value.String())\n\t}\n\ttestJSONString(t, s)\n\ttestJSONString(t, \"R\\xfd\\xfc\\a!\\x82eO\\x16?_\\x0f\\x9ab\\x1dr\")\n\ttestJSONString(t, \"_\\xb9\\v\\xad\\xb3|X!\\xb6\\xd9U&\\xa4\\x1a\\x95\\x04\")\n\tdata, _ := json.Marshal(\"\\b\\f\")\n\tif string(data) == \"\\\"\\\\b\\\\f\\\"\" {\n\t\t// Go version 1.22+ encodes \"\\b\" and \"\\f\" correctly.\n\t\ttestJSONString(t, \"\\b\\f\")\n\t\trng := rand.New(rand.NewSource(time.Now().UnixNano()))\n\t\tstart := time.Now()\n\t\tvar buf [16]byte\n\t\tfor time.Since(start) < time.Second*2 {\n\t\t\tif _, err := rng.Read(buf[:]); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\ttestJSONString(t, string(buf[:]))\n\t\t}\n\t}\n}\n\nfunc TestIndexAtSymbol(t *testing.T) {\n\tjson := `{\n\t\t\"@context\": {\n\t\t  \"rdfs\": \"http://www.w3.org/2000/01/rdf-schema#\",\n\t\t  \"@vocab\": \"http://schema.org/\",\n\t\t  \"sh\": \"http://www.w3.org/ns/shacl#\"\n\t\t}\n\t}`\n\tassert(t, Get(json, \"@context.@vocab\").Index == 85)\n}\n\nfunc TestDeepModifierWithOptions(t *testing.T) {\n\trawJson := `{\"x\":[{\"y\":[{\"z\":{\"b\":1, \"c\": 2, \"a\": 3}}]}]}`\n\tjsonPathExpr := `x.#.y.#.z.@pretty:{\"sortKeys\":true}`\n\tresults := GetManyBytes([]byte(rawJson), jsonPathExpr)\n\tassert(t, len(results) == 1)\n\tactual := results[0].Raw\n\texpected := `[[{\n  \"a\": 3,\n  \"b\": 1,\n  \"c\": 2\n}\n]]`\n\tif expected != actual {\n\t\tt.Fatal(strconv.Quote(rawJson) + \"\\n\\t\" +\n\t\t\texpected + \"\\n\\t\" +\n\t\t\tactual + \"\\n\\t<<< MISMATCH >>>\")\n\t}\n}\n\nfunc TestIssue301(t *testing.T) {\n\tjson := `{\n\t\t\"children\": [\"Sara\",\"Alex\",\"Jack\"],\n\t\t\"fav.movie\": [\"Deer Hunter\"]\n\t}`\n\n\tassert(t, Get(json, `children.0`).String() == \"Sara\")\n\tassert(t, Get(json, `children.[0]`).String() == `[\"Sara\"]`)\n\tassert(t, Get(json, `children.1`).String() == \"Alex\")\n\tassert(t, Get(json, `children.[1]`).String() == `[\"Alex\"]`)\n\tassert(t, Get(json, `children.[10]`).String() == `[]`)\n\tassert(t, Get(json, `fav\\.movie.0`).String() == \"Deer Hunter\")\n\tassert(t, Get(json, `fav\\.movie.[0]`).String() == `[\"Deer Hunter\"]`)\n\tassert(t, Get(json, `fav\\.movie.1`).String() == \"\")\n\tassert(t, Get(json, `fav\\.movie.[1]`).String() == \"[]\")\n\n}\n\nfunc TestModDig(t *testing.T) {\n\tjson := `\n\t\t{\n\n\t\t\t\"group\": {\n\t\t\t\t\"issues\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"fields\": {\n\t\t\t\t\t\t\"labels\": [\n\t\t\t\t\t\t\t\"milestone_1\",\n\t\t\t\t\t\t\t\"group:foo\",\n\t\t\t\t\t\t\t\"plan:a\",\n\t\t\t\t\t\t\t\"plan:b\"\n\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"refid\": \"123\"\n\t\t\t\t\t},{\n\t\t\t\t\t\t\"fields\": {\n\t\t\t\t\t\t\"labels\": [\n\t\t\t\t\t\t\t\"milestone_2\",\n\t\t\t\t\t\t\t\"group:foo\",\n\t\t\t\t\t\t\t\"plan:a\",\n\t\t\t\t\t\t\t\"plan\"\n\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"refid\": \"456\"\n\t\t\t\t\t},[\n\t\t\t\t\t\t{\"extra_deep\":[{\n\t\t\t\t\t\t\t\"fields\": {\n\t\t\t\t\t\t\t\"labels\": [\n\t\t\t\t\t\t\t\t\"milestone_3\",\n\t\t\t\t\t\t\t\t\"group:foo\",\n\t\t\t\t\t\t\t\t\"plan:a\",\n\t\t\t\t\t\t\t\t\"plan\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"refid\": \"789\"\n\t\t\t\t\t\t}]\n\t\t\t\t\t}]\n\t\t\t\t]\n\t\t\t}\n\t\t}\n\t`\n\tassert(t, Get(json, \"group.@dig:#(refid=123)|0.fields.labels.0\").String() == \"milestone_1\")\n\tassert(t, Get(json, \"group.@dig:#(refid=456)|0.fields.labels.0\").String() == \"milestone_2\")\n\tassert(t, Get(json, \"group.@dig:#(refid=789)|0.fields.labels.0\").String() == \"milestone_3\")\n\tjson = `\n\t{ \"something\": {\n\t\t\"anything\": {\n\t\t  \"abcdefg\": {\n\t\t\t  \"finally\": {\n\t\t\t\t\"important\": {\n\t\t\t\t\t\"secret\": \"password\",\n\t\t\t\t\t\"name\": \"jake\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"name\": \"melinda\"\n\t\t  }\n\t\t}\n\t  }\n\t}`\n\tassert(t, Get(json, \"@dig:name\").String() == `[\"melinda\",\"jake\"]`)\n\tassert(t, Get(json, \"@dig:secret\").String() == `[\"password\"]`)\n}\n\nfunc TestEscape(t *testing.T) {\n\tjson := `{\n\t\t\"user\":{\n\t\t\t\"first.name\": \"Janet\",\n\t\t\t\"last.name\": \"Prichard\"\n\t\t  }\n\t  }`\n\tuser := Get(json, \"user\")\n\tassert(t, user.Get(Escape(\"first.name\")).String() == \"Janet\")\n\tassert(t, user.Get(Escape(\"last.name\")).String() == \"Prichard\")\n\tassert(t, user.Get(\"first.name\").String() == \"\")\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/tidwall/gjson\n\ngo 1.12\n\nrequire (\n\tgithub.com/tidwall/match v1.1.1\n\tgithub.com/tidwall/pretty v1.2.0\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=\ngithub.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=\ngithub.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=\ngithub.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\n"
  }
]