gjson/README.md

489 lines
14 KiB
Markdown
Raw Normal View History

2016-08-11 06:07:45 +03:00
<p align="center">
<img
src="logo.png"
2016-08-11 06:07:45 +03:00
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>
2021-11-30 20:22:50 +03:00
<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>
2016-08-11 06:07:45 +03:00
</p>
<p align="center">get JSON values quickly</a></p>
2016-08-11 06:07:45 +03:00
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).
2018-02-10 19:10:40 +03:00
2024-04-23 14:59:52 +03:00
Also check out [SJSON](https://github.com/tidwall/sjson) for modifying JSON, and the [JJ](https://github.com/tidwall/jj) command-line tool.
2016-08-11 06:07:45 +03:00
This README is a quick overview of how to use GJSON, for more information check out [GJSON Syntax](SYNTAX.md).
2022-06-13 16:38:27 +03:00
GJSON is also available for [Python](https://github.com/volans-/gjson-py) and [Rust](https://github.com/tidwall/gjson.rs)
2016-08-11 06:07:45 +03:00
Getting Started
===============
## Installing
To start using GJSON, install Go and run `go get`:
```sh
$ go get -u github.com/tidwall/gjson
```
This will retrieve the library.
## Get a value
Get searches JSON for the specified path. A path is in dot syntax, such as `name.last` or `age`. When the value is found it's returned immediately.
2016-08-11 06:07:45 +03:00
```go
package main
import "github.com/tidwall/gjson"
const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
func main() {
value := gjson.Get(json, "name.last")
println(value.String())
}
```
This will print:
```
Prichard
```
2016-11-29 05:04:18 +03:00
*There's also the [GetMany](#get-multiple-values-at-once) function to get multiple values at once, and [GetBytes](#working-with-bytes) for working with JSON byte slices.*
2016-10-28 19:07:48 +03:00
2016-08-21 19:56:42 +03:00
## Path Syntax
2024-04-23 14:59:52 +03:00
Below is a quick overview of the path syntax, for further information please
2019-06-29 03:17:38 +03:00
check out [GJSON Syntax](SYNTAX.md).
2019-06-29 03:16:29 +03:00
2016-08-21 19:56:42 +03:00
A path is a series of keys separated by a dot.
A key may contain special wildcard characters '\*' and '?'.
To access an array value use the index as the key.
To get the number of elements in an array or to access a child path, use the '#' character.
2017-04-10 22:26:09 +03:00
The dot and wildcard characters can be escaped with '\\'.
2016-08-21 19:56:42 +03:00
```json
2016-08-11 06:07:45 +03:00
{
"name": {"first": "Tom", "last": "Anderson"},
"age":37,
2016-08-12 05:16:21 +03:00
"children": ["Sara","Alex","Jack"],
"fav.movie": "Deer Hunter",
"friends": [
2019-07-12 16:46:38 +03:00
{"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
{"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},
{"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}
]
2016-08-11 06:07:45 +03:00
}
2016-08-22 16:11:47 +03:00
```
```
2016-08-11 06:07:45 +03:00
"name.last" >> "Anderson"
"age" >> 37
"children" >> ["Sara","Alex","Jack"]
2016-08-11 06:07:45 +03:00
"children.#" >> 3
"children.1" >> "Alex"
"child*.2" >> "Jack"
"c?ildren.0" >> "Sara"
2016-08-12 05:16:21 +03:00
"fav\.movie" >> "Deer Hunter"
"friends.#.first" >> ["Dale","Roger","Jane"]
2016-08-21 19:56:42 +03:00
"friends.1.last" >> "Craig"
2016-08-11 06:07:45 +03:00
```
2016-12-02 21:53:14 +03:00
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 `!%`
2019-06-30 22:59:56 +03:00
(not like) operators.
```
2019-06-30 22:59:56 +03:00
friends.#(last=="Murphy").first >> "Dale"
friends.#(last=="Murphy")#.first >> ["Dale","Jane"]
friends.#(age>45)#.last >> ["Craig","Murphy"]
friends.#(first%"D*").last >> "Murphy"
friends.#(first!%"D*").last >> "Craig"
2019-07-12 16:46:38 +03:00
friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"]
2016-08-31 23:23:20 +03:00
```
2016-08-11 06:07:45 +03:00
2024-04-23 14:59:52 +03:00
*Please note that before v1.3.0, queries used the `#[...]` brackets. This was
2019-06-30 22:59:56 +03:00
changed in v1.3.0 as to avoid confusion with the new
2024-04-23 14:59:52 +03:00
[multi-paths](SYNTAX.md#multi-paths) syntax. For backwards compatibility,
2019-06-30 22:59:56 +03:00
`#[...]` will continue to work until the next major release.*
2016-08-11 06:07:45 +03:00
## Result Type
GJSON supports the JSON types `string`, `number`, `bool`, and `null`.
Arrays and Objects are returned as their raw JSON types.
2016-08-11 06:07:45 +03:00
2016-08-13 02:26:06 +03:00
The `Result` type holds one of these:
2016-08-11 06:07:45 +03:00
```
bool, for JSON booleans
float64, for JSON numbers
string, for JSON string literals
nil, for JSON null
```
2016-08-13 02:26:06 +03:00
To directly access the value:
2016-08-11 06:07:45 +03:00
```go
2021-06-30 08:38:05 +03:00
result.Type // can be String, Number, True, False, Null, or JSON
result.Str // holds the string
result.Num // holds the float64 number
result.Raw // holds the raw json
result.Index // index of raw value in original json, zero means index unknown
2021-09-01 17:33:46 +03:00
result.Indexes // indexes of all the elements that match on a path containing the '#' query character.
2016-08-21 17:30:33 +03:00
```
2024-04-23 14:59:52 +03:00
These are a variety of handy functions that work on a result:
```go
result.Exists() bool
result.Value() interface{}
result.Int() int64
result.Uint() uint64
result.Float() float64
result.String() string
result.Bool() bool
2017-04-15 03:58:25 +03:00
result.Time() time.Time
result.Array() []gjson.Result
result.Map() map[string]gjson.Result
result.Get(path string) Result
2016-12-02 21:53:14 +03:00
result.ForEach(iterator func(key, value Result) bool)
result.Less(token Result, caseSensitive bool) bool
```
The `result.Value()` function returns an `interface{}` which requires type assertion and is one of the following Go types:
```go
boolean >> bool
number >> float64
string >> string
null >> nil
array >> []interface{}
object >> map[string]interface{}
```
2021-04-02 05:03:29 +03:00
The `result.Array()` function returns back an array of values.
If the result represents a non-existent value, then an empty array will be returned.
If the result is not a JSON array, the return value will be an array containing one result.
2018-02-08 22:21:08 +03:00
### 64-bit integers
The `result.Int()` and `result.Uint()` calls are capable of reading all 64 bits, allowing for large JSON integers.
```go
result.Int() int64 // -9223372036854775808 to 9223372036854775807
2022-10-31 17:56:38 +03:00
result.Uint() uint64 // 0 to 18446744073709551615
2018-02-08 22:21:08 +03:00
```
## Modifiers and path chaining
New in version 1.2 is support for modifier functions and path chaining.
A modifier is a path component that performs custom processing on the
JSON.
Multiple paths can be "chained" together using the pipe character.
This is useful for getting results from a modified query.
For example, using the built-in `@reverse` modifier on the above JSON document,
we'll get `children` array and reverse the order:
```
"children|@reverse" >> ["Jack","Alex","Sara"]
2019-02-17 05:21:12 +03:00
"children|@reverse|0" >> "Jack"
```
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.
2024-04-23 14:59:52 +03:00
- `@pretty`: Make the JSON document more human-readable.
- `@this`: Returns the current element. It can be used to retrieve the root element.
- `@valid`: Ensure the JSON document is valid.
- `@flatten`: Flattens an array.
- `@join`: Joins multiple objects into a single object.
- `@keys`: Returns an array of keys for an object.
- `@values`: Returns an array of values for an object.
- `@tostr`: Converts JSON to a string. Wraps a JSON string.
- `@fromstr`: Converts a string from JSON. Unwraps a JSON string.
2022-02-02 14:58:31 +03:00
- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db).
2023-08-10 01:08:32 +03:00
- `@dig`: Search for a value without providing its entire path. See [e8e87f2](https://github.com/tidwall/gjson/commit/e8e87f2a00dc41f3aba5631094e21f59a8cf8cbf).
### Modifier arguments
A modifier may accept an optional argument. The argument can be a valid JSON
document or just characters.
For example, the `@pretty` modifier takes a JSON object as its argument.
```
@pretty:{"sortKeys":true}
```
Which makes the JSON pretty and orders all of its keys.
```json
{
"age":37,
"children": ["Sara","Alex","Jack"],
"fav.movie": "Deer Hunter",
"friends": [
{"age": 44, "first": "Dale", "last": "Murphy"},
{"age": 68, "first": "Roger", "last": "Craig"},
{"age": 47, "first": "Jane", "last": "Murphy"}
],
"name": {"first": "Tom", "last": "Anderson"}
}
```
*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`.
Please see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.*
### Custom modifiers
You can also add custom modifiers.
For example, here we create a modifier that makes the entire JSON document upper
or lower case.
```go
gjson.AddModifier("case", func(json, arg string) string {
if arg == "upper" {
return strings.ToUpper(json)
}
if arg == "lower" {
return strings.ToLower(json)
}
return json
})
```
```
"children|@case:upper" >> ["SARA","ALEX","JACK"]
"children|@case:lower|@reverse" >> ["jack","alex","sara"]
```
## JSON Lines
2024-04-23 14:59:52 +03:00
There's support for [JSON Lines](http://jsonlines.org/) using the `..` prefix, which treats a multi-lined document as an array.
For example:
```
{"name": "Gilbert", "age": 61}
{"name": "Alexa", "age": 34}
{"name": "May", "age": 57}
{"name": "Deloise", "age": 44}
```
```
..# >> 4
..1 >> {"name": "Alexa", "age": 34}
..3 >> {"name": "Deloise", "age": 44}
..#.name >> ["Gilbert","Alexa","May","Deloise"]
2019-06-30 22:59:56 +03:00
..#(name="May").age >> 57
```
The `ForEachLines` function will iterate through JSON lines.
```go
gjson.ForEachLine(json, func(line gjson.Result) bool{
println(line.String())
return true
})
```
2016-08-21 17:30:33 +03:00
## Get nested array values
Suppose you want all the last names from the following JSON:
2016-08-21 17:30:33 +03:00
```json
{
"programmers": [
{
"firstName": "Janet",
"lastName": "McLaughlin",
2016-08-21 17:30:33 +03:00
}, {
"firstName": "Elliotte",
"lastName": "Hunter",
2016-08-21 17:30:33 +03:00
}, {
"firstName": "Jason",
"lastName": "Harold",
2016-08-21 17:30:33 +03:00
}
]
2017-05-11 06:42:56 +03:00
}
2016-08-21 17:30:33 +03:00
```
You would use the path `programmers.#.lastName` like such:
2016-08-21 17:30:33 +03:00
```go
result := gjson.Get(json, "programmers.#.lastName")
2017-05-11 06:42:56 +03:00
for _, name := range result.Array() {
2016-08-21 17:30:33 +03:00
println(name.String())
}
2016-08-11 06:07:45 +03:00
```
2016-08-31 23:23:20 +03:00
You can also query an object inside an array:
```go
2019-06-30 22:59:56 +03:00
name := gjson.Get(json, `programmers.#(lastName="Hunter").firstName`)
2016-08-31 23:23:20 +03:00
println(name.String()) // prints "Elliotte"
```
2016-11-30 20:50:59 +03:00
## Iterate through an object or array
The `ForEach` function allows for quickly iterating through an object or array.
2016-11-30 20:50:59 +03:00
The key and value are passed to the iterator function for objects.
Only the value is passed for arrays.
Returning `false` from an iterator will stop iteration.
```go
result := gjson.Get(json, "programmers")
2017-05-11 06:42:56 +03:00
result.ForEach(func(key, value gjson.Result) bool {
println(value.String())
2016-11-30 20:50:59 +03:00
return true // keep iterating
})
```
2016-08-31 23:23:20 +03:00
## Simple Parse and Get
There's a `Parse(json)` function that will do a simple parse, and `result.Get(path)` that will search a result.
2024-04-23 14:59:52 +03:00
For example, these will return the same result:
```go
2016-08-22 16:13:09 +03:00
gjson.Parse(json).Get("name").Get("last")
2016-08-24 18:06:20 +03:00
gjson.Get(json, "name").Get("last")
gjson.Get(json, "name.last")
```
2016-08-13 02:26:06 +03:00
## Check for the existence of a value
Sometimes you just want to know if a value exists.
2016-08-13 02:26:06 +03:00
2016-08-21 20:56:31 +03:00
```go
2016-08-13 02:26:06 +03:00
value := gjson.Get(json, "name.last")
if !value.Exists() {
println("no last name")
} else {
println(value.String())
}
// Or as one step
2017-05-11 06:42:56 +03:00
if gjson.Get(json, "name.last").Exists() {
2016-08-13 02:26:06 +03:00
println("has a last name")
}
```
2018-02-18 19:49:01 +03:00
## Validate JSON
2024-04-23 14:59:52 +03:00
The `Get*` and `Parse*` functions expects that the JSON is well-formed. Bad JSON will not panic, but it may return unexpected results.
2018-02-18 19:49:01 +03:00
2024-04-23 14:59:52 +03:00
If you are consuming JSON from an unpredictable source then you may want to validate before using GJSON.
2018-02-18 19:49:01 +03:00
```go
if !gjson.Valid(json) {
return errors.New("invalid json")
}
value := gjson.Get(json, "name.last")
```
2016-08-27 16:20:43 +03:00
## Unmarshal to a map
To unmarshal to a `map[string]interface{}`:
```go
m, ok := gjson.Parse(json).Value().(map[string]interface{})
2017-05-11 06:42:56 +03:00
if !ok {
2016-08-27 16:20:43 +03:00
// not a map
}
```
2016-08-13 02:26:06 +03:00
2016-10-28 19:07:48 +03:00
## Working with Bytes
2016-10-28 20:56:10 +03:00
If your JSON is contained in a `[]byte` slice, there's the [GetBytes](https://godoc.org/github.com/tidwall/gjson#GetBytes) function. This is preferred over `Get(string(data), path)`.
2016-10-28 19:07:48 +03:00
```go
2016-11-05 05:41:23 +03:00
var json []byte = ...
result := gjson.GetBytes(json, path)
2016-10-28 19:07:48 +03:00
```
2024-04-23 14:59:52 +03:00
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:
2016-11-05 05:41:23 +03:00
```go
var json []byte = ...
result := gjson.GetBytes(json, path)
var raw []byte
if result.Index > 0 {
raw = json[result.Index:result.Index+len(result.Raw)]
} else {
raw = []byte(result.Raw)
}
```
This is a best-effort no allocation sub slice of the original JSON. This method utilizes the `result.Index` field, which is the position of the raw data in the original JSON. It's possible that the value of `result.Index` equals zero, in which case the `result.Raw` is converted to a `[]byte`.
2016-10-28 19:07:48 +03:00
2016-08-11 06:07:45 +03:00
## Performance
Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/),
[ffjson](https://github.com/pquerna/ffjson),
2016-08-11 20:53:50 +03:00
[EasyJSON](https://github.com/mailru/easyjson),
[jsonparser](https://github.com/buger/jsonparser),
and [json-iterator](https://github.com/json-iterator/go)
2016-08-11 06:07:45 +03:00
```
2021-10-22 15:10:12 +03:00
BenchmarkGJSONGet-16 11644512 311 ns/op 0 B/op 0 allocs/op
BenchmarkGJSONUnmarshalMap-16 1122678 3094 ns/op 1920 B/op 26 allocs/op
BenchmarkJSONUnmarshalMap-16 516681 6810 ns/op 2944 B/op 69 allocs/op
BenchmarkJSONUnmarshalStruct-16 697053 5400 ns/op 928 B/op 13 allocs/op
BenchmarkJSONDecoder-16 330450 10217 ns/op 3845 B/op 160 allocs/op
BenchmarkFFJSONLexer-16 1424979 2585 ns/op 880 B/op 8 allocs/op
BenchmarkEasyJSONLexer-16 3000000 729 ns/op 501 B/op 5 allocs/op
BenchmarkJSONParserGet-16 3000000 366 ns/op 21 B/op 0 allocs/op
BenchmarkJSONIterator-16 3000000 869 ns/op 693 B/op 14 allocs/op
2016-08-11 06:07:45 +03:00
```
2016-08-11 20:53:50 +03:00
JSON document used:
2016-08-11 06:07:45 +03:00
```json
{
"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
2016-08-11 06:07:45 +03:00
"src": "Images/Sun.png",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}
}
2016-08-11 06:07:45 +03:00
```
2020-07-27 18:31:55 +03:00
Each operation was rotated through one of the following search paths:
2016-08-11 06:07:45 +03:00
```
widget.window.name
widget.image.hOffset
widget.text.onMouseUp
```
2021-10-22 15:10:12 +03:00
*These benchmarks were run on a MacBook Pro 16" 2.4 GHz Intel Core i9 using Go 1.17 and can be found [here](https://github.com/tidwall/gjson-benchmarks).*