From 0b9aa797740891a3d1751f826c4b4b321f47c639 Mon Sep 17 00:00:00 2001 From: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:35:22 +0200 Subject: [PATCH 1/4] Format files before working on them gjson_test.go was formatted with gofumpt --- README.md | 62 +++++++++++----------- SYNTAX.md | 44 ++++++++-------- gjson.go | 31 +++++++---- gjson_test.go | 140 +++++++++++++++++++++++++++++++------------------- 4 files changed, 159 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index 96b2e4d..0e8c299 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@

-GJSON
GoDoc GJSON Playground GJSON Syntax - +

get json values quickly

@@ -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: diff --git a/SYNTAX.md b/SYNTAX.md index a3f0fac..14c1df9 100644 --- a/SYNTAX.md +++ b/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} diff --git a/gjson.go b/gjson.go index eb8c707..d6d341b 100644 --- a/gjson.go +++ b/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' { diff --git a/gjson_test.go b/gjson_test.go index 61c21f9..c803f44 100644 --- a/gjson_test.go +++ b/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 Date: Tue, 23 Apr 2024 13:43:14 +0200 Subject: [PATCH 2/4] Fix JSON acronym An acronym is written in capital. --- README.md | 42 +++++++++++++++---------------- SYNTAX.md | 14 +++++------ gjson.go | 70 +++++++++++++++++++++++++-------------------------- gjson_test.go | 2 +- 4 files changed, 64 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 0e8c299..40cb763 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@

-

get json values quickly

+

get JSON values quickly

-GJSON is a Go package that provides a [fast](#performance) and [simple](#get-a-value) way to get values from a json document. -It has features such as [one line retrieval](#get-a-value), [dot notation paths](#path-syntax), [iteration](#iterate-through-an-object-or-array), and [parsing json lines](#json-lines). +GJSON is a Go package that provides a [fast](#performance) and [simple](#get-a-value) way to get values from a JSON document. +It has features such as [one line retrieval](#get-a-value), [dot notation paths](#path-syntax), [iteration](#iterate-through-an-object-or-array), and [parsing JSON lines](#json-lines). -Also check out [SJSON](https://github.com/tidwall/sjson) for modifying json, and the [JJ](https://github.com/tidwall/jj) command line tool. +Also check out [SJSON](https://github.com/tidwall/sjson) for modifying JSON, and the [JJ](https://github.com/tidwall/jj) command line tool. This README is a quick overview of how to use GJSON, for more information check out [GJSON Syntax](SYNTAX.md). @@ -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 @@ -114,8 +114,8 @@ changed in v1.3.0 as to avoid confusion with the new ## 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: @@ -184,12 +184,12 @@ result.Uint() uint64 // 0 to 18446744073709551615 New in version 1.2 is support for modifier functions and path chaining. A modifier is a path component that performs custom processing on the -json. +JSON. Multiple paths can be "chained" together using the pipe character. This is useful for getting results from a modified query. -For example, using the built-in `@reverse` modifier on the above json document, +For example, using the built-in `@reverse` modifier on the above JSON document, we'll get `children` array and reverse the order: ``` @@ -197,19 +197,19 @@ we'll get `children` array and reverse the order: "children|@reverse|0" >> "Jack" ``` -There are currently the following built-in modifiers: +These are currently the following built-in modifiers: - `@reverse`: Reverse an array or the members of an object. -- `@ugly`: Remove all whitespace from a json document. -- `@pretty`: Make the json document more human readable. +- `@ugly`: Remove all whitespace from a JSON document. +- `@pretty`: Make the JSON document more human readable. - `@this`: Returns the current element. It can be used to retrieve the root element. -- `@valid`: Ensure the json document is valid. +- `@valid`: Ensure the JSON document is valid. - `@flatten`: Flattens an array. - `@join`: Joins multiple objects into a single object. - `@keys`: Returns an array of keys for an object. - `@values`: Returns an array of values for an object. -- `@tostr`: Converts json to a string. Wraps a json string. -- `@fromstr`: Converts a string from json. Unwraps a json string. +- `@tostr`: Converts JSON to a string. Wraps a JSON string. +- `@fromstr`: Converts a string from JSON. Unwraps a JSON string. - `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db). - `@dig`: Search for a value without providing its entire path. See [e8e87f2](https://github.com/tidwall/gjson/commit/e8e87f2a00dc41f3aba5631094e21f59a8cf8cbf). @@ -218,13 +218,13 @@ There are currently the following built-in modifiers: 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} ``` -Which makes the json pretty and orders all of its keys. +Which makes the JSON pretty and orders all of its keys. ```json { @@ -247,7 +247,7 @@ Please see [Pretty Options](https://github.com/tidwall/pretty#customized-output) You can also add custom modifiers. -For example, here we create a modifier that makes the entire json document upper +For example, here we create a modifier that makes the entire JSON document upper or lower case. ```go @@ -299,7 +299,7 @@ gjson.ForEachLine(json, func(line gjson.Result) bool{ ## Get nested array values -Suppose you want all the last names from the following json: +Suppose you want all the last names from the following JSON: ```json { @@ -381,7 +381,7 @@ if gjson.Get(json, "name.last").Exists() { ## Validate JSON -The `Get*` and `Parse*` functions expects that the json is well-formed. Bad json will not panic, but it may return back unexpected results. +The `Get*` and `Parse*` functions expects that the JSON is well-formed. Bad JSON will not panic, but it may return back unexpected results. If you are consuming JSON from an unpredictable source then you may want to validate prior to using GJSON. @@ -425,7 +425,7 @@ if result.Index > 0 { } ``` -This is a best-effort no allocation sub slice of the original json. This method utilizes the `result.Index` field, which is the position of the raw data in the original json. It's possible that the value of `result.Index` equals zero, in which case the `result.Raw` is converted to a `[]byte`. +This is a best-effort no allocation sub slice of the original JSON. This method utilizes the `result.Index` field, which is the position of the raw data in the original JSON. It's possible that the value of `result.Index` equals zero, in which case the `result.Raw` is converted to a `[]byte`. ## Performance diff --git a/SYNTAX.md b/SYNTAX.md index 14c1df9..0460770 100644 --- a/SYNTAX.md +++ b/SYNTAX.md @@ -250,13 +250,13 @@ There are currently the following built-in modifiers: - `@ugly`: Remove all whitespace from JSON. - `@pretty`: Make the JSON more human readable. - `@this`: Returns the current element. It can be used to retrieve the root element. -- `@valid`: Ensure the json document is valid. +- `@valid`: Ensure the JSON document is valid. - `@flatten`: Flattens an array. - `@join`: Joins multiple objects into a single object. - `@keys`: Returns an array of keys for an object. - `@values`: Returns an array of values for an object. -- `@tostr`: Converts json to a string. Wraps a json string. -- `@fromstr`: Converts a string from json. Unwraps a json string. +- `@tostr`: Converts JSON to a string. Wraps a JSON string. +- `@fromstr`: Converts a string from JSON. Unwraps a JSON string. - `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db). - `@dig`: Search for a value without providing its entire path. See [e8e87f2](https://github.com/tidwall/gjson/commit/e8e87f2a00dc41f3aba5631094e21f59a8cf8cbf). @@ -264,13 +264,13 @@ There are currently the following built-in modifiers: A modifier may accept an optional argument. The argument can be a valid JSON payload or just characters. -For example, the `@pretty` modifier takes a json object as its argument. +For example, the `@pretty` modifier takes a JSON object as its argument. ``` @pretty:{"sortKeys":true} ``` -Which makes the json pretty and orders all of its keys. +Which makes the JSON pretty and orders all of its keys. ```json { @@ -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: diff --git a/gjson.go b/gjson.go index d6d341b..4c50909 100644 --- a/gjson.go +++ b/gjson.go @@ -1,4 +1,4 @@ -// Package gjson provides searching for json strings. +// Package gjson provides searching for JSON strings. package gjson import ( @@ -17,15 +17,15 @@ import ( type Type int const ( - // Null is a null json value + // Null is a null JSON value Null Type = iota - // False is a json false boolean + // False is a JSON false boolean False - // Number is json number + // Number is JSON number Number - // String is a json string + // String is a JSON string String - // True is a json true boolean + // True is a JSON true boolean True // JSON is a raw block of JSON JSON @@ -51,17 +51,17 @@ func (t Type) String() string { } } -// Result represents a json value that is returned from Get(). +// Result represents a JSON value that is returned from Get(). type Result struct { - // Type is the json type + // Type is the JSON type Type Type - // Raw is the raw json + // Raw is the raw JSON Raw string - // Str is the json string + // Str is the JSON string Str string - // Num is the json number + // Num is the JSON number Num float64 - // Index of raw value in original json, zero means index unknown + // Index of raw value in original JSON, zero means index unknown Index int // Indexes of all the elements that match on a path containing the '#' // query character. @@ -455,10 +455,10 @@ end: return } -// Parse parses the json and returns a result. +// Parse parses the JSON and returns a result. // -// This function expects that the json is well-formed, and does not validate. -// Invalid json will not panic, but it may return back unexpected results. +// This function expects that the JSON is well-formed, and does not validate. +// Invalid JSON will not panic, but it may return back unexpected results. // If you are consuming JSON from an unpredictable source then you may want to // use the Valid function first. func Parse(json string) Result { @@ -508,7 +508,7 @@ func Parse(json string) Result { return value } -// ParseBytes parses the json and returns a result. +// ParseBytes parses the JSON and returns a result. // If working with bytes, this method preferred over Parse(string(data)) func ParseBytes(json []byte) Result { return Parse(string(json)) @@ -1978,7 +1978,7 @@ type parseContext struct { lines bool } -// Get searches json for the specified path. +// 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. // @@ -2007,8 +2007,8 @@ type parseContext struct { // "c?ildren.0" >> "Sara" // "friends.#.first" >> ["James","Roger"] // -// This function expects that the json is well-formed, and does not validate. -// Invalid json will not panic, but it may return back unexpected results. +// This function expects that the JSON is well-formed, and does not validate. +// Invalid JSON will not panic, but it may return back unexpected results. // If you are consuming JSON from an unpredictable source then you may want to // use the Valid function first. func Get(json, path string) Result { @@ -2122,7 +2122,7 @@ func Get(json, path string) Result { return c.value } -// GetBytes searches json for the specified path. +// GetBytes searches JSON for the specified path. // If working with bytes, this method preferred over Get(string(data), path) func GetBytes(json []byte, path string) Result { return getBytes(json, path) @@ -2254,7 +2254,7 @@ func stringLessInsensitive(a, b string) bool { return len(a) < len(b) } -// parseAny parses the next value from a json string. +// parseAny parses the next value from a JSON string. // A Result is returned when the hit param is set. // The return values are (i int, res Result, ok bool) func parseAny(json string, i int, hit bool) (int, Result, bool) { @@ -2332,7 +2332,7 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) { return i, res, false } -// GetMany searches json for the multiple paths. +// GetMany searches JSON for the multiple paths. // The return value is a Result array where the number of items // will be equal to the number of input paths. func GetMany(json string, path ...string) []Result { @@ -2343,7 +2343,7 @@ func GetMany(json string, path ...string) []Result { return res } -// GetManyBytes searches json for the multiple paths. +// GetManyBytes searches JSON for the multiple paths. // The return value is a Result array where the number of items // will be equal to the number of input paths. func GetManyBytes(json []byte, path ...string) []Result { @@ -2633,7 +2633,7 @@ func validnull(data []byte, i int) (outi int, ok bool) { return i, false } -// Valid returns true if the input is valid json. +// Valid returns true if the input is valid JSON. // // if !gjson.Valid(json) { // return errors.New("invalid json") @@ -2644,7 +2644,7 @@ func Valid(json string) bool { return ok } -// ValidBytes returns true if the input is valid json. +// ValidBytes returns true if the input is valid JSON. // // if !gjson.Valid(json) { // return errors.New("invalid json") @@ -2767,7 +2767,7 @@ func execModifier(json, path string) (pathOut, res string, ok bool) { var parsedArgs bool switch pathOut[0] { case '{', '[', '"': - // json arg + // JSON arg res := Parse(pathOut) if res.Exists() { args = squash(pathOut) @@ -2797,7 +2797,7 @@ func execModifier(json, path string) (pathOut, res string, ok bool) { return pathOut, res, false } -// unwrap removes the '[]' or '{}' characters around json +// unwrap removes the '[]' or '{}' characters around JSON func unwrap(json string) string { json = trim(json) if len(json) >= 2 && (json[0] == '[' || json[0] == '{') { @@ -2862,7 +2862,7 @@ func cleanWS(s string) string { return s } -// @pretty modifier makes the json look nice. +// @pretty modifier makes the JSON look nice. func modPretty(json, arg string) string { if len(arg) > 0 { opts := *pretty.DefaultOptions @@ -2944,7 +2944,7 @@ func modReverse(json, arg string) string { // // [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,6,7] // -// The original json is returned when the json is not an array. +// The original JSON is returned when the JSON is not an array. func modFlatten(json, arg string) string { res := Parse(json) if !res.IsArray() { @@ -3053,7 +3053,7 @@ func modValues(json, arg string) string { // // [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":41} // -// The original json is returned when the json is not an object. +// The original JSON is returned when the JSON is not an object. func modJoin(json, arg string) string { res := Parse(json) if !res.IsArray() { @@ -3115,9 +3115,9 @@ func modJoin(json, arg string) string { return bytesString(out) } -// @valid ensures that the json is valid before moving on. An empty string is -// returned when the json is not valid, otherwise it returns the original json. -func modValid(json, arg string) string { +// @valid ensures that the JSON is valid before moving on. An empty string is +// returned when the JSON is not valid, otherwise it returns the original JSON. +func modValid(json, _ string) string { if !Valid(json) { return "" } @@ -3189,9 +3189,9 @@ type sliceHeader struct { cap int } -// getBytes casts the input json bytes to a string and safely returns the +// getBytes casts the input JSON bytes to a string and safely returns the // results as uniquely allocated data. This operation is intended to minimize -// copies and allocations for the large json string->[]byte. +// copies and allocations for the large JSON string->[]byte. func getBytes(json []byte, path string) Result { var result Result if json != nil { diff --git a/gjson_test.go b/gjson_test.go index c803f44..33f15d6 100644 --- a/gjson_test.go +++ b/gjson_test.go @@ -107,7 +107,7 @@ func TestEscapePath(t *testing.T) { testEscapePath(t, json, "test.keyk\\*.key\\?", "val7") } -// this json block is poorly formed on purpose. +// this JSON block is poorly formed on purpose. var basicJSON = ` {"age":100, "name":{"here":"B\\\"R"}, "noop":{"what is a wren?":"a bird"}, "happy":true,"immortal":false, From c12548f2007147f26a07fb1065e3f7f818a814cb Mon Sep 17 00:00:00 2001 From: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:59:52 +0200 Subject: [PATCH 3/4] Fix typos and style --- README.md | 22 +++++++++++----------- SYNTAX.md | 36 ++++++++++++++++++------------------ gjson.go | 38 +++++++++++++++++++------------------- gjson_test.go | 10 +++++----- 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 40cb763..73a60e6 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ GJSON is a Go package that provides a [fast](#performance) and [simple](#get-a-value) way to get values from a JSON document. It has features such as [one line retrieval](#get-a-value), [dot notation paths](#path-syntax), [iteration](#iterate-through-an-object-or-array), and [parsing JSON lines](#json-lines). -Also check out [SJSON](https://github.com/tidwall/sjson) for modifying JSON, and the [JJ](https://github.com/tidwall/jj) command line tool. +Also check out [SJSON](https://github.com/tidwall/sjson) for modifying JSON, and the [JJ](https://github.com/tidwall/jj) command-line tool. This README is a quick overview of how to use GJSON, for more information check out [GJSON Syntax](SYNTAX.md). @@ -58,7 +58,7 @@ Prichard ## Path Syntax -Below is a quick overview of the path syntax, for more complete information please +Below is a quick overview of the path syntax, for further information please check out [GJSON Syntax](SYNTAX.md). A path is a series of keys separated by a dot. @@ -107,9 +107,9 @@ friends.#(first!%"D*").last >> "Craig" friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"] ``` -*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was +*Please note that before 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, +[multi-paths](SYNTAX.md#multi-paths) syntax. For backwards compatibility, `#[...]` will continue to work until the next major release.* ## Result Type @@ -137,7 +137,7 @@ result.Index // index of raw value in original json, zero means index u result.Indexes // indexes of all the elements that match on a path containing the '#' query character. ``` -There are a variety of handy functions that work on a result: +These are a variety of handy functions that work on a result: ```go result.Exists() bool @@ -201,7 +201,7 @@ These are currently the following built-in modifiers: - `@reverse`: Reverse an array or the members of an object. - `@ugly`: Remove all whitespace from a JSON document. -- `@pretty`: Make the JSON document more human readable. +- `@pretty`: Make the JSON document more human-readable. - `@this`: Returns the current element. It can be used to retrieve the root element. - `@valid`: Ensure the JSON document is valid. - `@flatten`: Flattens an array. @@ -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 multi-lined document as an array. For example: @@ -353,7 +353,7 @@ result.ForEach(func(key, value gjson.Result) bool { There's a `Parse(json)` function that will do a simple parse, and `result.Get(path)` that will search a result. -For example, all of these will return the same result: +For example, these will return the same result: ```go gjson.Parse(json).Get("name").Get("last") @@ -381,9 +381,9 @@ if gjson.Get(json, "name.last").Exists() { ## Validate JSON -The `Get*` and `Parse*` functions expects that the JSON is well-formed. Bad JSON will not panic, but it may return back unexpected results. +The `Get*` and `Parse*` functions expects that the JSON is well-formed. Bad JSON will not panic, but it may return unexpected results. -If you are consuming JSON from an unpredictable source then you may want to validate prior to using GJSON. +If you are consuming JSON from an unpredictable source then you may want to validate before using GJSON. ```go if !gjson.Valid(json) { @@ -412,7 +412,7 @@ var json []byte = ... result := gjson.GetBytes(json, path) ``` -If you are using the `gjson.GetBytes(json, path)` function and you want to avoid converting `result.Raw` to a `[]byte`, then you can use this pattern: +If you are using the `gjson.GetBytes(json, path)` function, and you want to avoid converting `result.Raw` to a `[]byte`, then you can use this pattern: ```go var json []byte = ... diff --git a/SYNTAX.md b/SYNTAX.md index 0460770..b4cd1b2 100644 --- a/SYNTAX.md +++ b/SYNTAX.md @@ -10,9 +10,9 @@ This document is designed to explain the structure of a GJSON Path through examp - [Escape Character](#escape-character) - [Arrays](#arrays) - [Queries](#queries) -- [Dot vs Pipe](#dot-vs-pipe) +- [Dot vs. Pipe](#dot-vs-pipe) - [Modifiers](#modifiers) -- [Multipaths](#multipaths) +- [Multi-paths](#multi-paths) - [Literals](#literals) The definitive implementation is [github.com/tidwall/gjson](https://github.com/tidwall/gjson). @@ -46,7 +46,7 @@ The following GJSON Paths evaluate to the accompanying values. ### Basic -In many cases you'll just want to retrieve values by object name or array index. +Often, you'll just want to retrieve values by object name or array index. ```go name.last "Anderson" @@ -77,7 +77,7 @@ Special purpose characters, such as `.`, `*`, and `?` can be escaped with `\`. fav\.movie "Deer Hunter" ``` -You'll also need to make sure that the `\` character is correctly escaped when hardcoding a path in your source code. +You'll also need to make sure that the `\` character is correctly escaped when hard-coding a path in your source code. ```go // Go @@ -105,7 +105,7 @@ friends.#.age [44,68,47] ### Queries -You can also query an array for the first match by using `#(...)`, or find all matches with `#(...)#`. +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. @@ -130,8 +130,8 @@ Nested queries are allowed. 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) +*Please note that before v1.3.0, queries used the `#[...]` brackets. This was +changed in v1.3.0 as to avoid confusion with the new [multi-paths](#multi-paths) syntax. For backwards compatibility, `#[...]` will continue to work until the next major release.* @@ -183,10 +183,10 @@ vals.#(b==~*)#.a >> [1,2,3,4,5,6,7,8,9,10] vals.#(b!=~*)#.a >> [11] ``` -### Dot vs Pipe +### Dot vs. Pipe The `.` is standard separator, but it's also possible to use a `|`. -In most cases they both end up returning the same results. +Usually, they both end up returning the same results. The cases where`|` differs from `.` is when it's used after the `#` for [Arrays](#arrays) and [Queries](#queries). Here are some examples @@ -215,14 +215,14 @@ The path `friends.#(last="Murphy")#` all by itself results in [{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}] ``` -The `.first` suffix will process the `first` path on each array element *before* returning the results. Which becomes +The `.first` suffix will process the `first` path on each array element *before* returning the results. Which becomes: ```json ["Dale","Jane"] ``` But the `|first` suffix actually processes the `first` path *after* the previous result. -Since the previous result is an array, not an object, it's not possible to process +Since the previous result is an array, not an object, it's impossible to process because `first` does not exist. Yet, `|0` suffix returns @@ -244,11 +244,11 @@ children.@reverse ["Jack","Alex","Sara"] children.@reverse.0 "Jack" ``` -There are currently the following built-in modifiers: +These are currently the following built-in modifiers: - `@reverse`: Reverse an array or the members of an object. - `@ugly`: Remove all whitespace from JSON. -- `@pretty`: Make the JSON more human readable. +- `@pretty`: Make the JSON more human-readable. - `@this`: Returns the current element. It can be used to retrieve the root element. - `@valid`: Ensure the JSON document is valid. - `@flatten`: Flattens an array. @@ -270,7 +270,7 @@ For example, the `@pretty` modifier takes a JSON object as its argument. @pretty:{"sortKeys":true} ``` -Which makes the JSON pretty and orders all of its keys. +Which makes the JSON pretty and orders all its keys. ```json { @@ -311,13 +311,13 @@ gjson.AddModifier("case", func(json, arg string) string { *Note: Custom modifiers are not yet available in the Rust version* -### Multipaths +### Multi-paths Starting with v1.3.0, GJSON added the ability to join multiple paths together to form new documents. Wrapping comma-separated paths between `[...]` or `{...}` will result in a new array or object, respectively. -For example, using the given multipath: +For example, using the given multi-path: ``` {name.first,age,"the_murphys":friends.#(last="Murphy")#.first} @@ -339,11 +339,11 @@ 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 can be particularly useful when constructing a new JSON document using [multi-paths](#multi-paths). A JSON literal begins with the '!' declaration character. -For example, using the given multipath: +For example, using the given multi-path: ``` {name.first,age,"company":!"Happysoft","employed":!true} diff --git a/gjson.go b/gjson.go index 4c50909..f9711fa 100644 --- a/gjson.go +++ b/gjson.go @@ -99,7 +99,7 @@ func (t Result) String() string { } } -// Bool returns an boolean representation. +// Bool returns a boolean representation. func (t Result) Bool() bool { switch t.Type { default: @@ -166,7 +166,7 @@ func (t Result) Uint() uint64 { } } -// Float returns an float64 representation. +// Float returns a float64 representation. func (t Result) Float() float64 { switch t.Type { default: @@ -458,7 +458,7 @@ end: // Parse parses the JSON and returns a result. // // This function expects that the JSON is well-formed, and does not validate. -// Invalid JSON will not panic, but it may return back unexpected results. +// Invalid JSON will not panic, but it may return unexpected results. // If you are consuming JSON from an unpredictable source then you may want to // use the Valid function first. func Parse(json string) Result { @@ -480,7 +480,7 @@ func Parse(json string) Result { value.Raw, value.Num = tonum(json[i:]) case 'n': if i+1 < len(json) && json[i+1] != 'u' { - // nan + // NaN value.Type = Number value.Raw, value.Num = tonum(json[i:]) } else { @@ -1813,10 +1813,10 @@ type subSelector struct { path string } -// parseSubSelectors returns the subselectors belonging to a '[path1,path2]' or +// parseSubSelectors returns the sub-selectors belonging to a '[path1,path2]' or // '{"field1":path1,"field2":path2}' type subSelection. It's expected that the // first character in path is either '[' or '{', and has already been checked -// prior to calling this function. +// before calling this function. func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) { modifier := 0 depth := 1 @@ -2008,7 +2008,7 @@ type parseContext struct { // "friends.#.first" >> ["James","Roger"] // // This function expects that the JSON is well-formed, and does not validate. -// Invalid JSON will not panic, but it may return back unexpected results. +// Invalid JSON will not panic, but it may return unexpected results. // If you are consuming JSON from an unpredictable source then you may want to // use the Valid function first. func Get(json, path string) Result { @@ -2035,7 +2035,7 @@ func Get(json, path string) Result { } } if path[0] == '[' || path[0] == '{' { - // using a subselector path + // using a sub-selector path kind := path[0] var ok bool var subs []subSelector @@ -2128,7 +2128,7 @@ func GetBytes(json []byte, path string) Result { return getBytes(json, path) } -// runeit returns the rune from the the \uXXXX +// runeit returns the rune from the \uXXXX func runeit(json string) rune { n, _ := strconv.ParseUint(json[:4], 16, 64) return rune(n) @@ -2255,7 +2255,7 @@ func stringLessInsensitive(a, b string) bool { } // parseAny parses the next value from a JSON string. -// A Result is returned when the hit param is set. +// A Result is returned when the hit parameter is set. // The return values are (i int, res Result, ok bool) func parseAny(json string, i int, hit bool) (int, Result, bool) { var res Result @@ -2767,7 +2767,7 @@ func execModifier(json, path string) (pathOut, res string, ok bool) { var parsedArgs bool switch pathOut[0] { case '{', '[', '"': - // JSON arg + // JSON argument res := Parse(pathOut) if res.Exists() { args = squash(pathOut) @@ -2776,7 +2776,7 @@ func execModifier(json, path string) (pathOut, res string, ok bool) { } } if !parsedArgs { - // simple arg + // simple argument i := 0 for ; i < len(pathOut); i++ { if pathOut[i] == '|' { @@ -2940,7 +2940,7 @@ func modReverse(json, arg string) string { // // [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,[6,7]] // -// The {"deep":true} arg can be provide for deep flattening. +// The {"deep":true} argument can be provided for deep flattening. // // [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,6,7] // @@ -3221,11 +3221,11 @@ func getBytes(json []byte, path string) Result { } else if uintptr(strh.data) >= uintptr(rawh.data) && uintptr(strh.data)+uintptr(strh.len) <= uintptr(rawh.data)+uintptr(rawh.len) { - // Str is a substring of Raw. + // Str is a sub-string of Raw. start := uintptr(strh.data) - uintptr(rawh.data) // safely copy the raw slice header result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) - // substring the raw + // sub-string the raw result.Str = result.Raw[start : start+uintptr(strh.len)] } else { // safely copy both the raw and str slice headers to strings @@ -3319,10 +3319,10 @@ func revSquash(json string) string { // // ["friends.0.first","friends.1.first","friends.2.first"] // -// The param 'json' must be the original JSON used when calling Get. +// The parameter 'json' must be the original JSON used when calling Get. // // Returns an empty string if the paths cannot be determined, which can happen -// when the Result came from a path that contained a multipath, modifier, +// when the Result came from a path that contained a multi-path, modifier, // or a nested query. func (t Result) Paths(json string) []string { if t.Indexes == nil { @@ -3348,10 +3348,10 @@ func (t Result) Paths(json string) []string { // // "friends.0" // -// The param 'json' must be the original JSON used when calling Get. +// The parameter 'json' must be the original JSON used when calling Get. // // Returns an empty string if the paths cannot be determined, which can happen -// when the Result came from a path that contained a multipath, modifier, +// when the Result came from a path that contained a multi-path, modifier, // or a nested query. func (t Result) Path(json string) string { var path []byte diff --git a/gjson_test.go b/gjson_test.go index 33f15d6..8e68755 100644 --- a/gjson_test.go +++ b/gjson_test.go @@ -353,7 +353,7 @@ func TestPlus53BitInts(t *testing.T) { } func TestIssue38(t *testing.T) { - // These should not fail, even though the unicode is invalid. + // These should not fail, even though the Unicode is invalid. Get(`["S3O PEDRO DO BUTI\udf93"]`, "0") Get(`["S3O PEDRO DO BUTI\udf93asdf"]`, "0") Get(`["S3O PEDRO DO BUTI\udf93\u"]`, "0") @@ -2227,7 +2227,7 @@ func TestTildeQueries(t *testing.T) { } func TestModifierDoubleQuotes(t *testing.T) { - josn := `{ + json := `{ "data": [ { "name": "Product P4", @@ -2246,11 +2246,11 @@ func TestModifierDoubleQuotes(t *testing.T) { } ] }` - AddModifier("string", func(josn, arg string) string { - return strconv.Quote(josn) + AddModifier("string", func(json, arg string) string { + return strconv.Quote(json) }) - res := Get(josn, "data.#.{name,value:{productId,vendorId}.@string.@ugly}") + res := Get(json, "data.#.{name,value:{productId,vendorId}.@string.@ugly}") assert(t, res.Raw == `[`+ `{"name":"Product P4","value":"{\"productId\":\"1bb3\",\"vendorId\":\"10de\"}"},`+ From 29588afd67568cc4e005dd9a03654099f6bac37c Mon Sep 17 00:00:00 2001 From: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:08:27 +0200 Subject: [PATCH 4/4] Use backtick for JSON node name, not quotes --- README.md | 4 ++-- SYNTAX.md | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 73a60e6..4807517 100644 --- a/README.md +++ b/README.md @@ -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 @@ -318,7 +318,7 @@ Suppose you want all the last names from the following JSON: } ``` -You would use the path "programmers.#.lastName" like such: +You would use the path `programmers.#.lastName` like such: ```go result := gjson.Get(json, "programmers.#.lastName") diff --git a/SYNTAX.md b/SYNTAX.md index b4cd1b2..39b77d4 100644 --- a/SYNTAX.md +++ b/SYNTAX.md @@ -324,12 +324,12 @@ For example, using the given multi-path: ``` Here we selected the first name, age, and the first name for friends with the -last name "Murphy". +last name `Murphy`. You'll notice that an optional key can be provided, in this case -"the_murphys", to force assign a key to a value. Otherwise, the name of the -actual field will be used, in this case "first". If a name cannot be -determined, then "_" is used. +`the_murphys`, to force assign a key to a value. Otherwise, the name of the +actual field will be used, in this case `first`. If a name cannot be +determined, then `_` is used. This results in @@ -349,7 +349,7 @@ For example, using the given multi-path: {name.first,age,"company":!"Happysoft","employed":!true} ``` -Here we selected the first name and age. Then add two new fields, "company" and "employed". +Here we selected the first name and age. Then add two new fields, `company` and `employed`. This results in