mirror of https://github.com/tidwall/gjson.git
Format files before working on them
gjson_test.go was formatted with gofumpt
This commit is contained in:
parent
2ba56cc086
commit
0b9aa79774
62
README.md
62
README.md
|
@ -1,12 +1,12 @@
|
|||
<p align="center">
|
||||
<img
|
||||
src="logo.png"
|
||||
<img
|
||||
src="logo.png"
|
||||
width="240" height="78" border="0" alt="GJSON">
|
||||
<br>
|
||||
<a href="https://godoc.org/github.com/tidwall/gjson"><img src="https://img.shields.io/badge/api-reference-blue.svg?style=flat-square" alt="GoDoc"></a>
|
||||
<a href="https://tidwall.com/gjson-play"><img src="https://img.shields.io/badge/%F0%9F%8F%90-playground-9900cc.svg?style=flat-square" alt="GJSON Playground"></a>
|
||||
<a href="SYNTAX.md"><img src="https://img.shields.io/badge/{}-syntax-33aa33.svg?style=flat-square" alt="GJSON Syntax"></a>
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
<p align="center">get json values quickly</a></p>
|
||||
|
@ -34,7 +34,7 @@ $ go get -u github.com/tidwall/gjson
|
|||
This will retrieve the library.
|
||||
|
||||
## Get a value
|
||||
Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". When the value is found it's returned immediately.
|
||||
Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". When the value is found it's returned immediately.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
@ -93,9 +93,9 @@ The dot and wildcard characters can be escaped with '\\'.
|
|||
"friends.1.last" >> "Craig"
|
||||
```
|
||||
|
||||
You can also query an array for the first match by using `#(...)`, or find all
|
||||
matches with `#(...)#`. Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=`
|
||||
comparison operators and the simple pattern matching `%` (like) and `!%`
|
||||
You can also query an array for the first match by using `#(...)`, or find all
|
||||
matches with `#(...)#`. Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=`
|
||||
comparison operators and the simple pattern matching `%` (like) and `!%`
|
||||
(not like) operators.
|
||||
|
||||
```
|
||||
|
@ -109,13 +109,13 @@ friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"]
|
|||
|
||||
*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was
|
||||
changed in v1.3.0 as to avoid confusion with the new
|
||||
[multipath](SYNTAX.md#multipaths) syntax. For backwards compatibility,
|
||||
[multipath](SYNTAX.md#multipaths) syntax. For backwards compatibility,
|
||||
`#[...]` will continue to work until the next major release.*
|
||||
|
||||
## Result Type
|
||||
|
||||
GJSON supports the json types `string`, `number`, `bool`, and `null`.
|
||||
Arrays and Objects are returned as their raw json types.
|
||||
GJSON supports the json types `string`, `number`, `bool`, and `null`.
|
||||
Arrays and Objects are returned as their raw json types.
|
||||
|
||||
The `Result` type holds one of these:
|
||||
|
||||
|
@ -179,14 +179,14 @@ result.Int() int64 // -9223372036854775808 to 9223372036854775807
|
|||
result.Uint() uint64 // 0 to 18446744073709551615
|
||||
```
|
||||
|
||||
## Modifiers and path chaining
|
||||
## Modifiers and path chaining
|
||||
|
||||
New in version 1.2 is support for modifier functions and path chaining.
|
||||
|
||||
A modifier is a path component that performs custom processing on the
|
||||
A modifier is a path component that performs custom processing on the
|
||||
json.
|
||||
|
||||
Multiple paths can be "chained" together using the pipe character.
|
||||
Multiple paths can be "chained" together using the pipe character.
|
||||
This is useful for getting results from a modified query.
|
||||
|
||||
For example, using the built-in `@reverse` modifier on the above json document,
|
||||
|
@ -215,13 +215,13 @@ There are currently the following built-in modifiers:
|
|||
|
||||
### Modifier arguments
|
||||
|
||||
A modifier may accept an optional argument. The argument can be a valid JSON
|
||||
A modifier may accept an optional argument. The argument can be a valid JSON
|
||||
document or just characters.
|
||||
|
||||
For example, the `@pretty` modifier takes a json object as its argument.
|
||||
For example, the `@pretty` modifier takes a json object as its argument.
|
||||
|
||||
```
|
||||
@pretty:{"sortKeys":true}
|
||||
@pretty:{"sortKeys":true}
|
||||
```
|
||||
|
||||
Which makes the json pretty and orders all of its keys.
|
||||
|
@ -240,7 +240,7 @@ Which makes the json pretty and orders all of its keys.
|
|||
}
|
||||
```
|
||||
|
||||
*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`.
|
||||
*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`.
|
||||
Please see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.*
|
||||
|
||||
### Custom modifiers
|
||||
|
@ -269,7 +269,7 @@ gjson.AddModifier("case", func(json, arg string) string {
|
|||
|
||||
## JSON Lines
|
||||
|
||||
There's support for [JSON Lines](http://jsonlines.org/) using the `..` prefix, which treats a multilined document as an array.
|
||||
There's support for [JSON Lines](http://jsonlines.org/) using the `..` prefix, which treats a multilined document as an array.
|
||||
|
||||
For example:
|
||||
|
||||
|
@ -305,14 +305,14 @@ Suppose you want all the last names from the following json:
|
|||
{
|
||||
"programmers": [
|
||||
{
|
||||
"firstName": "Janet",
|
||||
"lastName": "McLaughlin",
|
||||
"firstName": "Janet",
|
||||
"lastName": "McLaughlin",
|
||||
}, {
|
||||
"firstName": "Elliotte",
|
||||
"lastName": "Hunter",
|
||||
"firstName": "Elliotte",
|
||||
"lastName": "Hunter",
|
||||
}, {
|
||||
"firstName": "Jason",
|
||||
"lastName": "Harold",
|
||||
"firstName": "Jason",
|
||||
"lastName": "Harold",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -336,7 +336,7 @@ println(name.String()) // prints "Elliotte"
|
|||
|
||||
## Iterate through an object or array
|
||||
|
||||
The `ForEach` function allows for quickly iterating through an object or array.
|
||||
The `ForEach` function allows for quickly iterating through an object or array.
|
||||
The key and value are passed to the iterator function for objects.
|
||||
Only the value is passed for arrays.
|
||||
Returning `false` from an iterator will stop iteration.
|
||||
|
@ -344,7 +344,7 @@ Returning `false` from an iterator will stop iteration.
|
|||
```go
|
||||
result := gjson.Get(json, "programmers")
|
||||
result.ForEach(func(key, value gjson.Result) bool {
|
||||
println(value.String())
|
||||
println(value.String())
|
||||
return true // keep iterating
|
||||
})
|
||||
```
|
||||
|
@ -363,7 +363,7 @@ gjson.Get(json, "name.last")
|
|||
|
||||
## Check for the existence of a value
|
||||
|
||||
Sometimes you just want to know if a value exists.
|
||||
Sometimes you just want to know if a value exists.
|
||||
|
||||
```go
|
||||
value := gjson.Get(json, "name.last")
|
||||
|
@ -429,8 +429,8 @@ This is a best-effort no allocation sub slice of the original json. This method
|
|||
|
||||
## Performance
|
||||
|
||||
Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/),
|
||||
[ffjson](https://github.com/pquerna/ffjson),
|
||||
Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/),
|
||||
[ffjson](https://github.com/pquerna/ffjson),
|
||||
[EasyJSON](https://github.com/mailru/easyjson),
|
||||
[jsonparser](https://github.com/buger/jsonparser),
|
||||
and [json-iterator](https://github.com/json-iterator/go)
|
||||
|
@ -459,7 +459,7 @@ JSON document used:
|
|||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
|
@ -474,7 +474,7 @@ JSON document used:
|
|||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Each operation was rotated through one of the following search paths:
|
||||
|
|
44
SYNTAX.md
44
SYNTAX.md
|
@ -15,12 +15,12 @@ This document is designed to explain the structure of a GJSON Path through examp
|
|||
- [Multipaths](#multipaths)
|
||||
- [Literals](#literals)
|
||||
|
||||
The definitive implementation is [github.com/tidwall/gjson](https://github.com/tidwall/gjson).
|
||||
The definitive implementation is [github.com/tidwall/gjson](https://github.com/tidwall/gjson).
|
||||
Use the [GJSON Playground](https://gjson.dev) to experiment with the syntax online.
|
||||
|
||||
## Path structure
|
||||
|
||||
A GJSON Path is intended to be easily expressed as a series of components separated by a `.` character.
|
||||
A GJSON Path is intended to be easily expressed as a series of components separated by a `.` character.
|
||||
|
||||
Along with `.` character, there are a few more that have special meaning, including `|`, `#`, `@`, `\`, `*`, `!`, and `?`.
|
||||
|
||||
|
@ -44,7 +44,7 @@ Given this JSON
|
|||
|
||||
The following GJSON Paths evaluate to the accompanying values.
|
||||
|
||||
### Basic
|
||||
### Basic
|
||||
|
||||
In many cases you'll just want to retrieve values by object name or array index.
|
||||
|
||||
|
@ -61,7 +61,7 @@ friends.1.first "Roger"
|
|||
|
||||
### Wildcards
|
||||
|
||||
A key may contain the special wildcard characters `*` and `?`.
|
||||
A key may contain the special wildcard characters `*` and `?`.
|
||||
The `*` will match on any zero+ characters, and `?` matches on any one character.
|
||||
|
||||
```go
|
||||
|
@ -71,7 +71,7 @@ c?ildren.0 "Sara"
|
|||
|
||||
### Escape character
|
||||
|
||||
Special purpose characters, such as `.`, `*`, and `?` can be escaped with `\`.
|
||||
Special purpose characters, such as `.`, `*`, and `?` can be escaped with `\`.
|
||||
|
||||
```go
|
||||
fav\.movie "Deer Hunter"
|
||||
|
@ -82,13 +82,13 @@ You'll also need to make sure that the `\` character is correctly escaped when h
|
|||
```go
|
||||
// Go
|
||||
val := gjson.Get(json, "fav\\.movie") // must escape the slash
|
||||
val := gjson.Get(json, `fav\.movie`) // no need to escape the slash
|
||||
val := gjson.Get(json, `fav\.movie`) // no need to escape the slash
|
||||
```
|
||||
|
||||
```rust
|
||||
// Rust
|
||||
let val = gjson::get(json, "fav\\.movie") // must escape the slash
|
||||
let val = gjson::get(json, r#"fav\.movie"#) // no need to escape the slash
|
||||
let val = gjson::get(json, r#"fav\.movie"#) // no need to escape the slash
|
||||
```
|
||||
|
||||
|
||||
|
@ -105,8 +105,8 @@ friends.#.age [44,68,47]
|
|||
|
||||
### Queries
|
||||
|
||||
You can also query an array for the first match by using `#(...)`, or find all matches with `#(...)#`.
|
||||
Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` comparison operators,
|
||||
You can also query an array for the first match by using `#(...)`, or find all matches with `#(...)#`.
|
||||
Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` comparison operators,
|
||||
and the simple pattern matching `%` (like) and `!%` (not like) operators.
|
||||
|
||||
```go
|
||||
|
@ -131,7 +131,7 @@ friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"]
|
|||
```
|
||||
|
||||
*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was
|
||||
changed in v1.3.0 as to avoid confusion with the new [multipath](#multipaths)
|
||||
changed in v1.3.0 as to avoid confusion with the new [multipath](#multipaths)
|
||||
syntax. For backwards compatibility, `#[...]` will continue to work until the
|
||||
next major release.*
|
||||
|
||||
|
@ -185,9 +185,9 @@ vals.#(b!=~*)#.a >> [11]
|
|||
|
||||
### Dot vs Pipe
|
||||
|
||||
The `.` is standard separator, but it's also possible to use a `|`.
|
||||
The `.` is standard separator, but it's also possible to use a `|`.
|
||||
In most cases they both end up returning the same results.
|
||||
The cases where`|` differs from `.` is when it's used after the `#` for [Arrays](#arrays) and [Queries](#queries).
|
||||
The cases where`|` differs from `.` is when it's used after the `#` for [Arrays](#arrays) and [Queries](#queries).
|
||||
|
||||
Here are some examples
|
||||
|
||||
|
@ -221,8 +221,8 @@ The `.first` suffix will process the `first` path on each array element *before*
|
|||
["Dale","Jane"]
|
||||
```
|
||||
|
||||
But the `|first` suffix actually processes the `first` path *after* the previous result.
|
||||
Since the previous result is an array, not an object, it's not possible to process
|
||||
But the `|first` suffix actually processes the `first` path *after* the previous result.
|
||||
Since the previous result is an array, not an object, it's not possible to process
|
||||
because `first` does not exist.
|
||||
|
||||
Yet, `|0` suffix returns
|
||||
|
@ -286,12 +286,12 @@ Which makes the json pretty and orders all of its keys.
|
|||
}
|
||||
```
|
||||
|
||||
*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`.
|
||||
*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`.
|
||||
Please see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.*
|
||||
|
||||
#### Custom modifiers
|
||||
|
||||
You can also add custom modifiers.
|
||||
You can also add custom modifiers.
|
||||
|
||||
For example, here we create a modifier which makes the entire JSON payload upper or lower case.
|
||||
|
||||
|
@ -323,11 +323,11 @@ For example, using the given multipath:
|
|||
{name.first,age,"the_murphys":friends.#(last="Murphy")#.first}
|
||||
```
|
||||
|
||||
Here we selected the first name, age, and the first name for friends with the
|
||||
Here we selected the first name, age, and the first name for friends with the
|
||||
last name "Murphy".
|
||||
|
||||
You'll notice that an optional key can be provided, in this case
|
||||
"the_murphys", to force assign a key to a value. Otherwise, the name of the
|
||||
You'll notice that an optional key can be provided, in this case
|
||||
"the_murphys", to force assign a key to a value. Otherwise, the name of the
|
||||
actual field will be used, in this case "first". If a name cannot be
|
||||
determined, then "_" is used.
|
||||
|
||||
|
@ -339,9 +339,9 @@ This results in
|
|||
|
||||
### Literals
|
||||
|
||||
Starting with v1.12.0, GJSON added support of json literals, which provides a way for constructing static blocks of json. This is can be particularly useful when constructing a new json document using [multipaths](#multipaths).
|
||||
Starting with v1.12.0, GJSON added support of json literals, which provides a way for constructing static blocks of json. This is can be particularly useful when constructing a new json document using [multipaths](#multipaths).
|
||||
|
||||
A json literal begins with the '!' declaration character.
|
||||
A json literal begins with the '!' declaration character.
|
||||
|
||||
For example, using the given multipath:
|
||||
|
||||
|
@ -351,7 +351,7 @@ For example, using the given multipath:
|
|||
|
||||
Here we selected the first name and age. Then add two new fields, "company" and "employed".
|
||||
|
||||
This results in
|
||||
This results in
|
||||
|
||||
```json
|
||||
{"first":"Tom","age":37,"company":"Happysoft","employed":true}
|
||||
|
|
31
gjson.go
31
gjson.go
|
@ -334,7 +334,7 @@ type arrayOrMapResult struct {
|
|||
}
|
||||
|
||||
func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) {
|
||||
var json = t.Raw
|
||||
json := t.Raw
|
||||
var i int
|
||||
var value Result
|
||||
var count int
|
||||
|
@ -686,7 +686,7 @@ func (t Result) Value() interface{} {
|
|||
}
|
||||
|
||||
func parseString(json string, i int) (int, string, bool, bool) {
|
||||
var s = i
|
||||
s := i
|
||||
for ; i < len(json); i++ {
|
||||
if json[i] > '\\' {
|
||||
continue
|
||||
|
@ -724,7 +724,7 @@ func parseString(json string, i int) (int, string, bool, bool) {
|
|||
}
|
||||
|
||||
func parseNumber(json string, i int) (int, string) {
|
||||
var s = i
|
||||
s := i
|
||||
i++
|
||||
for ; i < len(json); i++ {
|
||||
if json[i] <= ' ' || json[i] == ',' || json[i] == ']' ||
|
||||
|
@ -736,7 +736,7 @@ func parseNumber(json string, i int) (int, string) {
|
|||
}
|
||||
|
||||
func parseLiteral(json string, i int) (int, string) {
|
||||
var s = i
|
||||
s := i
|
||||
i++
|
||||
for ; i < len(json); i++ {
|
||||
if json[i] < 'a' || json[i] > 'z' {
|
||||
|
@ -793,8 +793,7 @@ func parseArrayPath(path string) (r arrayPathResult) {
|
|||
} else if path[1] == '[' || path[1] == '(' {
|
||||
// query
|
||||
r.query.on = true
|
||||
qpath, op, value, _, fi, vesc, ok :=
|
||||
parseQuery(path[i:])
|
||||
qpath, op, value, _, fi, vesc, ok := parseQuery(path[i:])
|
||||
if !ok {
|
||||
// bad query, end now
|
||||
break
|
||||
|
@ -1103,7 +1102,7 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
|
|||
// this is slightly different from getting s string value
|
||||
// because we don't need the outer quotes.
|
||||
i++
|
||||
var s = i
|
||||
s := i
|
||||
for ; i < len(c.json); i++ {
|
||||
if c.json[i] > '\\' {
|
||||
continue
|
||||
|
@ -1400,6 +1399,7 @@ func queryMatches(rp *arrayPathResult, value Result) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func parseArray(c *parseContext, i int, path string) (int, bool) {
|
||||
var pmatch, vesc, ok, hit bool
|
||||
var val string
|
||||
|
@ -1619,8 +1619,8 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
|
|||
c.pipe = right
|
||||
c.piped = true
|
||||
}
|
||||
var indexes = make([]int, 0, 64)
|
||||
var jsons = make([]byte, 0, 64)
|
||||
indexes := make([]int, 0, 64)
|
||||
jsons := make([]byte, 0, 64)
|
||||
jsons = append(jsons, '[')
|
||||
for j, k := 0, 0; j < len(alog); j++ {
|
||||
idx := alog[j]
|
||||
|
@ -2095,7 +2095,7 @@ func Get(json, path string) Result {
|
|||
}
|
||||
}
|
||||
var i int
|
||||
var c = &parseContext{json: json}
|
||||
c := &parseContext{json: json}
|
||||
if len(path) >= 2 && path[0] == '.' && path[1] == '.' {
|
||||
c.lines = true
|
||||
parseArray(c, 0, path[2:])
|
||||
|
@ -2136,7 +2136,7 @@ func runeit(json string) rune {
|
|||
|
||||
// unescape unescapes a string
|
||||
func unescape(json string) string {
|
||||
var str = make([]byte, 0, len(json))
|
||||
str := make([]byte, 0, len(json))
|
||||
for i := 0; i < len(json); i++ {
|
||||
switch {
|
||||
default:
|
||||
|
@ -2377,6 +2377,7 @@ func validpayload(data []byte, i int) (outi int, ok bool) {
|
|||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
func validany(data []byte, i int) (outi int, ok bool) {
|
||||
for ; i < len(data); i++ {
|
||||
switch data[i] {
|
||||
|
@ -2402,6 +2403,7 @@ func validany(data []byte, i int) (outi int, ok bool) {
|
|||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
func validobject(data []byte, i int) (outi int, ok bool) {
|
||||
for ; i < len(data); i++ {
|
||||
switch data[i] {
|
||||
|
@ -2444,6 +2446,7 @@ func validobject(data []byte, i int) (outi int, ok bool) {
|
|||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
func validcolon(data []byte, i int) (outi int, ok bool) {
|
||||
for ; i < len(data); i++ {
|
||||
switch data[i] {
|
||||
|
@ -2457,6 +2460,7 @@ func validcolon(data []byte, i int) (outi int, ok bool) {
|
|||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
func validcomma(data []byte, i int, end byte) (outi int, ok bool) {
|
||||
for ; i < len(data); i++ {
|
||||
switch data[i] {
|
||||
|
@ -2472,6 +2476,7 @@ func validcomma(data []byte, i int, end byte) (outi int, ok bool) {
|
|||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
func validarray(data []byte, i int) (outi int, ok bool) {
|
||||
for ; i < len(data); i++ {
|
||||
switch data[i] {
|
||||
|
@ -2495,6 +2500,7 @@ func validarray(data []byte, i int) (outi int, ok bool) {
|
|||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
func validstring(data []byte, i int) (outi int, ok bool) {
|
||||
for ; i < len(data); i++ {
|
||||
if data[i] < ' ' {
|
||||
|
@ -2527,6 +2533,7 @@ func validstring(data []byte, i int) (outi int, ok bool) {
|
|||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
func validnumber(data []byte, i int) (outi int, ok bool) {
|
||||
i--
|
||||
// sign
|
||||
|
@ -2609,6 +2616,7 @@ func validtrue(data []byte, i int) (outi int, ok bool) {
|
|||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
func validfalse(data []byte, i int) (outi int, ok bool) {
|
||||
if i+4 <= len(data) && data[i] == 'a' && data[i+1] == 'l' &&
|
||||
data[i+2] == 's' && data[i+3] == 'e' {
|
||||
|
@ -2616,6 +2624,7 @@ func validfalse(data []byte, i int) (outi int, ok bool) {
|
|||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
func validnull(data []byte, i int) (outi int, ok bool) {
|
||||
if i+3 <= len(data) && data[i] == 'u' && data[i+1] == 'l' &&
|
||||
data[i+2] == 'l' {
|
||||
|
|
140
gjson_test.go
140
gjson_test.go
|
@ -199,7 +199,6 @@ func TestPath(t *testing.T) {
|
|||
get("loggy.programmers.2.email")
|
||||
get("lastly.end\\.\\.\\.ing")
|
||||
get("lastly.yay")
|
||||
|
||||
}
|
||||
|
||||
func TestTimeResult(t *testing.T) {
|
||||
|
@ -216,8 +215,10 @@ func TestParseAny(t *testing.T) {
|
|||
|
||||
func TestManyVariousPathCounts(t *testing.T) {
|
||||
json := `{"a":"a","b":"b","c":"c"}`
|
||||
counts := []int{3, 4, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127,
|
||||
128, 129, 255, 256, 257, 511, 512, 513}
|
||||
counts := []int{
|
||||
3, 4, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127,
|
||||
128, 129, 255, 256, 257, 511, 512, 513,
|
||||
}
|
||||
paths := []string{"a", "b", "c"}
|
||||
expects := []string{"a", "b", "c"}
|
||||
for _, count := range counts {
|
||||
|
@ -238,6 +239,7 @@ func TestManyVariousPathCounts(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestManyRecursion(t *testing.T) {
|
||||
var json string
|
||||
var path string
|
||||
|
@ -252,6 +254,7 @@ func TestManyRecursion(t *testing.T) {
|
|||
path = path[1:]
|
||||
assert(t, GetMany(json, path)[0].String() == "b")
|
||||
}
|
||||
|
||||
func TestByteSafety(t *testing.T) {
|
||||
jsonb := []byte(`{"name":"Janet","age":38}`)
|
||||
mtok := GetBytes(jsonb, "name")
|
||||
|
@ -348,6 +351,7 @@ func TestPlus53BitInts(t *testing.T) {
|
|||
// flip the number to the negative sign.
|
||||
assert(t, Get(json, "overflow_int64").Int() == -9223372036854775808)
|
||||
}
|
||||
|
||||
func TestIssue38(t *testing.T) {
|
||||
// These should not fail, even though the unicode is invalid.
|
||||
Get(`["S3O PEDRO DO BUTI\udf93"]`, "0")
|
||||
|
@ -359,6 +363,7 @@ func TestIssue38(t *testing.T) {
|
|||
Get(`["S3O PEDRO DO BUTI\udf93\u1345"]`, "0")
|
||||
Get(`["S3O PEDRO DO BUTI\udf93\u1345asd"]`, "0")
|
||||
}
|
||||
|
||||
func TestTypes(t *testing.T) {
|
||||
assert(t, (Result{Type: String}).Type.String() == "String")
|
||||
assert(t, (Result{Type: Number}).Type.String() == "Number")
|
||||
|
@ -404,6 +409,7 @@ func TestTypes(t *testing.T) {
|
|||
assert(t, (Result{Type: False}).Float() == 0)
|
||||
assert(t, (Result{Type: Number, Num: 1}).Float() == 1)
|
||||
}
|
||||
|
||||
func TestForEach(t *testing.T) {
|
||||
Result{}.ForEach(nil)
|
||||
Result{Type: String, Str: "Hello"}.ForEach(func(_, value Result) bool {
|
||||
|
@ -423,6 +429,7 @@ func TestForEach(t *testing.T) {
|
|||
ParseBytes([]byte(`{"bad`)).ForEach(nil)
|
||||
ParseBytes([]byte(`{"ok":"bad`)).ForEach(nil)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
assert(t, len(ParseBytes([]byte(`"asdf"`)).Map()) == 0)
|
||||
assert(t, ParseBytes([]byte(`{"asdf":"ghjk"`)).Map()["asdf"].String() ==
|
||||
|
@ -431,6 +438,7 @@ func TestMap(t *testing.T) {
|
|||
assert(t, Result{Type: JSON, Raw: "**invalid**"}.Value() == nil)
|
||||
assert(t, Result{Type: JSON, Raw: "{"}.Map() != nil)
|
||||
}
|
||||
|
||||
func TestBasic1(t *testing.T) {
|
||||
mtok := get(basicJSON, `loggy.programmers`)
|
||||
var count int
|
||||
|
@ -474,6 +482,7 @@ func TestBasic1(t *testing.T) {
|
|||
t.Fatalf("expected %v, got %v", 3, count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic2(t *testing.T) {
|
||||
mtok := get(basicJSON, `loggy.programmers.#[age=101].firstName`)
|
||||
if mtok.String() != "1002.3" {
|
||||
|
@ -509,6 +518,7 @@ func TestBasic2(t *testing.T) {
|
|||
mtok.Map()["programmers"].Array()[1].Map()["firstName"].Str)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic3(t *testing.T) {
|
||||
var mtok Result
|
||||
if Parse(basicJSON).Get("loggy.programmers").Get("1").
|
||||
|
@ -549,6 +559,7 @@ func TestBasic3(t *testing.T) {
|
|||
t.Fatalf("expected 0, got %v", len(mtok.Array()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic4(t *testing.T) {
|
||||
if get(basicJSON, "items.3.tags.#").Num != 3 {
|
||||
t.Fatalf("expected 3, got %v", get(basicJSON, "items.3.tags.#").Num)
|
||||
|
@ -593,6 +604,7 @@ func TestBasic4(t *testing.T) {
|
|||
t.Fatal("should be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic5(t *testing.T) {
|
||||
token := get(basicJSON, "age")
|
||||
if token.String() != "100" {
|
||||
|
@ -630,8 +642,9 @@ func TestBasic5(t *testing.T) {
|
|||
t.Fatalf("expecting %v, got %v", "Jason", fn)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnicode(t *testing.T) {
|
||||
var json = `{"key":0,"的情况下解":{"key":1,"的情况":2}}`
|
||||
json := `{"key":0,"的情况下解":{"key":1,"的情况":2}}`
|
||||
if Get(json, "的情况下解.key").Num != 1 {
|
||||
t.Fatal("fail")
|
||||
}
|
||||
|
@ -659,11 +672,13 @@ func TestUnescape(t *testing.T) {
|
|||
unescape(string([]byte{'\\', '\\', 0}))
|
||||
unescape(string([]byte{'\\', '/', '\\', 'b', '\\', 'f'}))
|
||||
}
|
||||
|
||||
func assert(t testing.TB, cond bool) {
|
||||
if !cond {
|
||||
panic("assert failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLess(t *testing.T) {
|
||||
assert(t, !Result{Type: Null}.Less(Result{Type: Null}, true))
|
||||
assert(t, Result{Type: Null}.Less(Result{Type: False}, true))
|
||||
|
@ -673,18 +688,30 @@ func TestLess(t *testing.T) {
|
|||
assert(t, Result{Type: Null}.Less(Result{Type: String}, true))
|
||||
assert(t, !Result{Type: False}.Less(Result{Type: Null}, true))
|
||||
assert(t, Result{Type: False}.Less(Result{Type: True}, true))
|
||||
assert(t, Result{Type: String, Str: "abc"}.Less(Result{Type: String,
|
||||
Str: "bcd"}, true))
|
||||
assert(t, Result{Type: String, Str: "ABC"}.Less(Result{Type: String,
|
||||
Str: "abc"}, true))
|
||||
assert(t, !Result{Type: String, Str: "ABC"}.Less(Result{Type: String,
|
||||
Str: "abc"}, false))
|
||||
assert(t, Result{Type: Number, Num: 123}.Less(Result{Type: Number,
|
||||
Num: 456}, true))
|
||||
assert(t, !Result{Type: Number, Num: 456}.Less(Result{Type: Number,
|
||||
Num: 123}, true))
|
||||
assert(t, !Result{Type: Number, Num: 456}.Less(Result{Type: Number,
|
||||
Num: 456}, true))
|
||||
assert(t, Result{Type: String, Str: "abc"}.Less(Result{
|
||||
Type: String,
|
||||
Str: "bcd",
|
||||
}, true))
|
||||
assert(t, Result{Type: String, Str: "ABC"}.Less(Result{
|
||||
Type: String,
|
||||
Str: "abc",
|
||||
}, true))
|
||||
assert(t, !Result{Type: String, Str: "ABC"}.Less(Result{
|
||||
Type: String,
|
||||
Str: "abc",
|
||||
}, false))
|
||||
assert(t, Result{Type: Number, Num: 123}.Less(Result{
|
||||
Type: Number,
|
||||
Num: 456,
|
||||
}, true))
|
||||
assert(t, !Result{Type: Number, Num: 456}.Less(Result{
|
||||
Type: Number,
|
||||
Num: 123,
|
||||
}, true))
|
||||
assert(t, !Result{Type: Number, Num: 456}.Less(Result{
|
||||
Type: Number,
|
||||
Num: 456,
|
||||
}, true))
|
||||
assert(t, stringLessInsensitive("abcde", "BBCDE"))
|
||||
assert(t, stringLessInsensitive("abcde", "bBCDE"))
|
||||
assert(t, stringLessInsensitive("Abcde", "BBCDE"))
|
||||
|
@ -776,7 +803,7 @@ var exampleJSON = `{
|
|||
}`
|
||||
|
||||
func TestUnmarshalMap(t *testing.T) {
|
||||
var m1 = Parse(exampleJSON).Value().(map[string]interface{})
|
||||
m1 := Parse(exampleJSON).Value().(map[string]interface{})
|
||||
var m2 map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(exampleJSON), &m2); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -795,9 +822,9 @@ func TestUnmarshalMap(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSingleArrayValue(t *testing.T) {
|
||||
var json = `{"key": "value","key2":[1,2,3,4,"A"]}`
|
||||
var result = Get(json, "key")
|
||||
var array = result.Array()
|
||||
json := `{"key": "value","key2":[1,2,3,4,"A"]}`
|
||||
result := Get(json, "key")
|
||||
array := result.Array()
|
||||
if len(array) != 1 {
|
||||
t.Fatal("array is empty")
|
||||
}
|
||||
|
@ -814,7 +841,6 @@ func TestSingleArrayValue(t *testing.T) {
|
|||
if len(array) != 0 {
|
||||
t.Fatalf("got '%v', expected '%v'", len(array), 0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var manyJSON = ` {
|
||||
|
@ -867,12 +893,15 @@ func TestManyBasic(t *testing.T) {
|
|||
testMany(true, `[Cat Nancy]`, "name\\.first", "name.first")
|
||||
testMany(true, `[world]`, strings.Repeat("a.", 70)+"hello")
|
||||
}
|
||||
|
||||
func testMany(t *testing.T, json string, paths, expected []string) {
|
||||
testManyAny(t, json, paths, expected, true)
|
||||
testManyAny(t, json, paths, expected, false)
|
||||
}
|
||||
|
||||
func testManyAny(t *testing.T, json string, paths, expected []string,
|
||||
bytes bool) {
|
||||
bytes bool,
|
||||
) {
|
||||
var result []Result
|
||||
for i := 0; i < 2; i++ {
|
||||
var which string
|
||||
|
@ -902,6 +931,7 @@ func testManyAny(t *testing.T, json string, paths, expected []string,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue20(t *testing.T) {
|
||||
json := `{ "name": "FirstName", "name1": "FirstName1", ` +
|
||||
`"address": "address1", "addressDetails": "address2", }`
|
||||
|
@ -918,10 +948,14 @@ func TestIssue21(t *testing.T) {
|
|||
"Level1Field4":4,
|
||||
"Level1Field2":{ "Level2Field1":[ "value1", "value2" ],
|
||||
"Level2Field2":{ "Level3Field1":[ { "key1":"value1" } ] } } }`
|
||||
paths := []string{"Level1Field1", "Level1Field2.Level2Field1",
|
||||
"Level1Field2.Level2Field2.Level3Field1", "Level1Field4"}
|
||||
expected := []string{"3", `[ "value1", "value2" ]`,
|
||||
`[ { "key1":"value1" } ]`, "4"}
|
||||
paths := []string{
|
||||
"Level1Field1", "Level1Field2.Level2Field1",
|
||||
"Level1Field2.Level2Field2.Level3Field1", "Level1Field4",
|
||||
}
|
||||
expected := []string{
|
||||
"3", `[ "value1", "value2" ]`,
|
||||
`[ { "key1":"value1" } ]`, "4",
|
||||
}
|
||||
t.Run("SingleMany", func(t *testing.T) {
|
||||
testMany(t, json, paths,
|
||||
expected)
|
||||
|
@ -1097,8 +1131,10 @@ func TestValidBasic(t *testing.T) {
|
|||
testvalid(t, "[-.123]", false)
|
||||
}
|
||||
|
||||
var jsonchars = []string{"{", "[", ",", ":", "}", "]", "1", "0", "true",
|
||||
"false", "null", `""`, `"\""`, `"a"`}
|
||||
var jsonchars = []string{
|
||||
"{", "[", ",", ":", "}", "]", "1", "0", "true",
|
||||
"false", "null", `""`, `"\""`, `"a"`,
|
||||
}
|
||||
|
||||
func makeRandomJSONChars(b []byte) {
|
||||
var bb []byte
|
||||
|
@ -1217,6 +1253,7 @@ func TestIssue55(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue58(t *testing.T) {
|
||||
json := `{"data":[{"uid": 1},{"uid": 2}]}`
|
||||
res := Get(json, `data.#[uid!=1]`).Raw
|
||||
|
@ -1279,11 +1316,10 @@ null
|
|||
if i != 4 {
|
||||
t.Fatalf("expected '%v', got '%v'", 4, i)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNumUint64String(t *testing.T) {
|
||||
var i int64 = 9007199254740993 //2^53 + 1
|
||||
var i int64 = 9007199254740993 // 2^53 + 1
|
||||
j := fmt.Sprintf(`{"data": [ %d, "hello" ] }`, i)
|
||||
res := Get(j, "data.0")
|
||||
if res.String() != "9007199254740993" {
|
||||
|
@ -1312,7 +1348,7 @@ func TestNumBigString(t *testing.T) {
|
|||
|
||||
func TestNumFloatString(t *testing.T) {
|
||||
var i int64 = -9007199254740993
|
||||
j := fmt.Sprintf(`{"data":[ "hello", %d ]}`, i) //No quotes around value!!
|
||||
j := fmt.Sprintf(`{"data":[ "hello", %d ]}`, i) // No quotes around value!!
|
||||
res := Get(j, "data.1")
|
||||
if res.String() != "-9007199254740993" {
|
||||
t.Fatalf("expected '%v', got '%v'", "-9007199254740993", res.String())
|
||||
|
@ -1321,7 +1357,7 @@ func TestNumFloatString(t *testing.T) {
|
|||
|
||||
func TestDuplicateKeys(t *testing.T) {
|
||||
// this is valid json according to the JSON spec
|
||||
var json = `{"name": "Alex","name": "Peter"}`
|
||||
json := `{"name": "Alex","name": "Peter"}`
|
||||
if Parse(json).Get("name").String() !=
|
||||
Parse(json).Map()["name"].String() {
|
||||
t.Fatalf("expected '%v', got '%v'",
|
||||
|
@ -1335,7 +1371,7 @@ func TestDuplicateKeys(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestArrayValues(t *testing.T) {
|
||||
var json = `{"array": ["PERSON1","PERSON2",0],}`
|
||||
json := `{"array": ["PERSON1","PERSON2",0],}`
|
||||
values := Get(json, "array").Array()
|
||||
var output string
|
||||
for i, val := range values {
|
||||
|
@ -1354,7 +1390,6 @@ func TestArrayValues(t *testing.T) {
|
|||
if output != expect {
|
||||
t.Fatalf("expected '%v', got '%v'", expect, output)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkValid(b *testing.B) {
|
||||
|
@ -1459,7 +1494,6 @@ func TestSplitPipe(t *testing.T) {
|
|||
split(t, `hello.#[a|1="asdf\"|1324"]#|that.more|yikes`,
|
||||
`hello.#[a|1="asdf\"|1324"]#`, "that.more|yikes", true)
|
||||
split(t, `a.#[]#\|b`, "", "", false)
|
||||
|
||||
}
|
||||
|
||||
func TestArrayEx(t *testing.T) {
|
||||
|
@ -1705,7 +1739,6 @@ func TestQueries(t *testing.T) {
|
|||
assert(t, Get(json, `i*.f*.#[cust1>=true].first`).Exists())
|
||||
assert(t, !Get(json, `i*.f*.#[cust2<false].first`).Exists())
|
||||
assert(t, Get(json, `i*.f*.#[cust2<=false].first`).Exists())
|
||||
|
||||
}
|
||||
|
||||
func TestQueryArrayValues(t *testing.T) {
|
||||
|
@ -1797,8 +1830,7 @@ func TestParseQuery(t *testing.T) {
|
|||
var path, op, value, remain string
|
||||
var ok bool
|
||||
|
||||
path, op, value, remain, _, _, ok =
|
||||
parseQuery(`#(service_roles.#(=="one").()==asdf).cap`)
|
||||
path, op, value, remain, _, _, ok = parseQuery(`#(service_roles.#(=="one").()==asdf).cap`)
|
||||
assert(t, ok &&
|
||||
path == `service_roles.#(=="one").()` &&
|
||||
op == "=" &&
|
||||
|
@ -1826,8 +1858,7 @@ func TestParseQuery(t *testing.T) {
|
|||
value == `` &&
|
||||
remain == ``)
|
||||
|
||||
path, op, value, remain, _, _, ok =
|
||||
parseQuery(`#(a\("\"(".#(=="o\"(ne")%"ab\")").remain`)
|
||||
path, op, value, remain, _, _, ok = parseQuery(`#(a\("\"(".#(=="o\"(ne")%"ab\")").remain`)
|
||||
assert(t, ok &&
|
||||
path == `a\("\"(".#(=="o\"(ne")` &&
|
||||
op == "%" &&
|
||||
|
@ -1836,7 +1867,7 @@ func TestParseQuery(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestParentSubQuery(t *testing.T) {
|
||||
var json = `{
|
||||
json := `{
|
||||
"topology": {
|
||||
"instances": [
|
||||
{
|
||||
|
@ -1863,7 +1894,7 @@ func TestParentSubQuery(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSingleModifier(t *testing.T) {
|
||||
var data = `{"@key": "value"}`
|
||||
data := `{"@key": "value"}`
|
||||
assert(t, Get(data, "@key").String() == "value")
|
||||
assert(t, Get(data, "\\@key").String() == "value")
|
||||
}
|
||||
|
@ -1945,7 +1976,7 @@ func TestValid(t *testing.T) {
|
|||
|
||||
// https://github.com/tidwall/gjson/issues/152
|
||||
func TestJoin152(t *testing.T) {
|
||||
var json = `{
|
||||
json := `{
|
||||
"distance": 1374.0,
|
||||
"validFrom": "2005-11-14",
|
||||
"historical": {
|
||||
|
@ -2086,7 +2117,6 @@ func TestVariousFuzz(t *testing.T) {
|
|||
testJSON1 := `["*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,,,,,,"]`
|
||||
testJSON2 := `#[%"*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,,,,,,""*,*"]`
|
||||
Get(testJSON1, testJSON2)
|
||||
|
||||
}
|
||||
|
||||
func TestSubpathsWithMultipaths(t *testing.T) {
|
||||
|
@ -2227,11 +2257,10 @@ func TestModifierDoubleQuotes(t *testing.T) {
|
|||
`{"name":"Product P4","value":"{\"productId\":\"1cc3\",\"vendorId\":\"20de\"}"},`+
|
||||
`{"name":"Product P4","value":"{\"productId\":\"1dd3\",\"vendorId\":\"30de\"}"}`+
|
||||
`]`)
|
||||
|
||||
}
|
||||
|
||||
func TestIndexes(t *testing.T) {
|
||||
var exampleJSON = `{
|
||||
exampleJSON := `{
|
||||
"vals": [
|
||||
[1,66,{test: 3}],
|
||||
[4,5,[6]]
|
||||
|
@ -2276,7 +2305,7 @@ func TestIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIndexesMatchesRaw(t *testing.T) {
|
||||
var exampleJSON = `{
|
||||
exampleJSON := `{
|
||||
"objectArray":[
|
||||
{"first": "Jason", "age": 41},
|
||||
{"first": "Dale", "age": 44},
|
||||
|
@ -2312,7 +2341,7 @@ func TestIssue240(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestKeysValuesModifier(t *testing.T) {
|
||||
var json = `{
|
||||
json := `{
|
||||
"1300014": {
|
||||
"code": "1300014",
|
||||
"price": 59.18,
|
||||
|
@ -2341,11 +2370,15 @@ func TestKeysValuesModifier(t *testing.T) {
|
|||
|
||||
func TestNaNInf(t *testing.T) {
|
||||
json := `[+Inf,-Inf,Inf,iNF,-iNF,+iNF,NaN,nan,nAn,-0,+0]`
|
||||
raws := []string{"+Inf", "-Inf", "Inf", "iNF", "-iNF", "+iNF", "NaN", "nan",
|
||||
"nAn", "-0", "+0"}
|
||||
nums := []float64{math.Inf(+1), math.Inf(-1), math.Inf(0), math.Inf(0),
|
||||
raws := []string{
|
||||
"+Inf", "-Inf", "Inf", "iNF", "-iNF", "+iNF", "NaN", "nan",
|
||||
"nAn", "-0", "+0",
|
||||
}
|
||||
nums := []float64{
|
||||
math.Inf(+1), math.Inf(-1), math.Inf(0), math.Inf(0),
|
||||
math.Inf(-1), math.Inf(+1), math.NaN(), math.NaN(), math.NaN(),
|
||||
math.Copysign(0, -1), 0}
|
||||
math.Copysign(0, -1), 0,
|
||||
}
|
||||
|
||||
assert(t, int(Get(json, `#`).Int()) == len(raws))
|
||||
for i := 0; i < len(raws); i++ {
|
||||
|
@ -2579,7 +2612,7 @@ func TestJSONString(t *testing.T) {
|
|||
testJSONString(t, "R\xfd\xfc\a!\x82eO\x16?_\x0f\x9ab\x1dr")
|
||||
testJSONString(t, "_\xb9\v\xad\xb3|X!\xb6\xd9U&\xa4\x1a\x95\x04")
|
||||
data, _ := json.Marshal("\b\f")
|
||||
if (string(data) == "\"\\b\\f\"") {
|
||||
if string(data) == "\"\\b\\f\"" {
|
||||
// Go version 1.22+ encodes "\b" and "\f" correctly.
|
||||
testJSONString(t, "\b\f")
|
||||
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
@ -2639,7 +2672,6 @@ func TestIssue301(t *testing.T) {
|
|||
assert(t, Get(json, `fav\.movie.[0]`).String() == `["Deer Hunter"]`)
|
||||
assert(t, Get(json, `fav\.movie.1`).String() == "")
|
||||
assert(t, Get(json, `fav\.movie.[1]`).String() == "[]")
|
||||
|
||||
}
|
||||
|
||||
func TestModDig(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue