updated gjson
This commit is contained in:
parent
d9ed059887
commit
d6936636c2
102
vendor/github.com/tidwall/gjson/README.md
generated
vendored
102
vendor/github.com/tidwall/gjson/README.md
generated
vendored
@ -3,17 +3,17 @@
|
|||||||
src="logo.png"
|
src="logo.png"
|
||||||
width="240" height="78" border="0" alt="GJSON">
|
width="240" height="78" border="0" alt="GJSON">
|
||||||
<br>
|
<br>
|
||||||
<a href="https://travis-ci.org/tidwall/gjson"><img src="https://img.shields.io/travis/tidwall/gjson.svg?style=flat-square" alt="Build Status"></a><!--
|
<a href="https://travis-ci.org/tidwall/gjson"><img src="https://img.shields.io/travis/tidwall/gjson.svg?style=flat-square" alt="Build Status"></a>
|
||||||
<a href="http://gocover.io/github.com/tidwall/gjson"><img src="https://img.shields.io/badge/coverage-97%25-brightgreen.svg?style=flat-square" alt="Code Coverage"></a>
|
|
||||||
-->
|
|
||||||
<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://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="http://tidwall.com/gjson-play"><img src="https://img.shields.io/badge/play-ground-orange.svg?style=flat-square" alt="GJSON Playground"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p align="center">get a json value quickly</a></p>
|
<p align="center">get a json value quickly</a></p>
|
||||||
|
|
||||||
GJSON is a Go package that provides a [very fast](#performance) and simple way to get a value from a json document. The purpose for this library it to give efficient json indexing for the [BuntDB](https://github.com/tidwall/buntdb) project.
|
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).
|
||||||
For a command line interface check out [JSONed](https://github.com/tidwall/jsoned).
|
|
||||||
|
|
||||||
Getting Started
|
Getting Started
|
||||||
===============
|
===============
|
||||||
@ -29,7 +29,7 @@ $ go get -u github.com/tidwall/gjson
|
|||||||
This will retrieve the library.
|
This will retrieve the library.
|
||||||
|
|
||||||
## Get a value
|
## Get a value
|
||||||
Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". This function expects that the json is well-formed and validates. Invalid json will not panic, but it may return back unexpected results. 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". This function expects that the json is well-formed. Bad json will not panic, but it may return back unexpected results. When the value is found it's returned immediately.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@ -57,7 +57,7 @@ A path is a series of keys separated by a dot.
|
|||||||
A key may contain special wildcard characters '\*' and '?'.
|
A key may contain special wildcard characters '\*' and '?'.
|
||||||
To access an array value use the index as the key.
|
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.
|
To get the number of elements in an array or to access a child path, use the '#' character.
|
||||||
The dot and wildcard characters can be escaped with '\'.
|
The dot and wildcard characters can be escaped with '\\'.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -122,12 +122,14 @@ result.Index // index of raw value in original json, zero means index unknown
|
|||||||
There are a variety of handy functions that work on a result:
|
There are a variety of handy functions that work on a result:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
result.Exists() bool
|
||||||
result.Value() interface{}
|
result.Value() interface{}
|
||||||
result.Int() int64
|
result.Int() int64
|
||||||
result.Uint() uint64
|
result.Uint() uint64
|
||||||
result.Float() float64
|
result.Float() float64
|
||||||
result.String() string
|
result.String() string
|
||||||
result.Bool() bool
|
result.Bool() bool
|
||||||
|
result.Time() time.Time
|
||||||
result.Array() []gjson.Result
|
result.Array() []gjson.Result
|
||||||
result.Map() map[string]gjson.Result
|
result.Map() map[string]gjson.Result
|
||||||
result.Get(path string) Result
|
result.Get(path string) Result
|
||||||
@ -137,8 +139,6 @@ 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:
|
The `result.Value()` function returns an `interface{}` which requires type assertion and is one of the following Go types:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The `result.Array()` function returns back an array of values.
|
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 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.
|
If the result is not a JSON array, the return value will be an array containing one result.
|
||||||
@ -170,7 +170,7 @@ Suppose you want all the last names from the following json:
|
|||||||
"lastName": "Harold",
|
"lastName": "Harold",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You would use the path "programmers.#.lastName" like such:
|
You would use the path "programmers.#.lastName" like such:
|
||||||
@ -198,7 +198,7 @@ Returning `false` from an iterator will stop iteration.
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
result := gjson.Get(json, "programmers")
|
result := gjson.Get(json, "programmers")
|
||||||
result.ForEach(func(key, value Result) bool{
|
result.ForEach(func(key, value gjson.Result) bool {
|
||||||
println(value.String())
|
println(value.String())
|
||||||
return true // keep iterating
|
return true // keep iterating
|
||||||
})
|
})
|
||||||
@ -234,6 +234,53 @@ if gjson.Get(json, "name.last").Exists(){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Unmarshalling
|
||||||
|
|
||||||
|
There's a `gjson.Unmarshal` function which loads json data into a value.
|
||||||
|
It's a general replacement for `json.Unmarshal` and you can typically
|
||||||
|
see a 2-3x boost in performance without the need for external generators.
|
||||||
|
|
||||||
|
This function works almost identically to `json.Unmarshal` except that
|
||||||
|
`gjson.Unmarshal` will automatically attempt to convert JSON values to any
|
||||||
|
Go type. For example, the JSON string "100" or the JSON number 100 can be
|
||||||
|
equally assigned to Go string, int, byte, uint64, etc. This rule applies to
|
||||||
|
all types.
|
||||||
|
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Animal struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Sound string `json:"sound"`
|
||||||
|
Age int `json:"age"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = `{
|
||||||
|
"type": "Dog",
|
||||||
|
"Sound": "Bark",
|
||||||
|
"Age": "11"
|
||||||
|
}`
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var dog Animal
|
||||||
|
gjson.Unmarshal([]byte(json), &dog)
|
||||||
|
fmt.Printf("type: %s, sound: %s, age: %d\n", dog.Type, dog.Sound, dog.Age)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will print:
|
||||||
|
|
||||||
|
```
|
||||||
|
type: Dog, sound: Bark, age: 11
|
||||||
|
```
|
||||||
|
|
||||||
## Unmarshal to a map
|
## Unmarshal to a map
|
||||||
|
|
||||||
To unmarshal to a `map[string]interface{}`:
|
To unmarshal to a `map[string]interface{}`:
|
||||||
@ -284,26 +331,28 @@ The return value is a `[]Result`, which will always contain exactly the same num
|
|||||||
Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/),
|
Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/),
|
||||||
[ffjson](https://github.com/pquerna/ffjson),
|
[ffjson](https://github.com/pquerna/ffjson),
|
||||||
[EasyJSON](https://github.com/mailru/easyjson),
|
[EasyJSON](https://github.com/mailru/easyjson),
|
||||||
and [jsonparser](https://github.com/buger/jsonparser)
|
[jsonparser](https://github.com/buger/jsonparser),
|
||||||
|
and [json-iterator](https://github.com/json-iterator/go)
|
||||||
|
|
||||||
```
|
```
|
||||||
BenchmarkGJSONGet-8 15000000 333 ns/op 0 B/op 0 allocs/op
|
BenchmarkGJSONGet-8 3000000 372 ns/op 0 B/op 0 allocs/op
|
||||||
BenchmarkGJSONUnmarshalMap-8 900000 4188 ns/op 1920 B/op 26 allocs/op
|
BenchmarkGJSONUnmarshalMap-8 900000 4154 ns/op 1920 B/op 26 allocs/op
|
||||||
BenchmarkJSONUnmarshalMap-8 600000 8908 ns/op 3048 B/op 69 allocs/op
|
BenchmarkJSONUnmarshalMap-8 600000 9019 ns/op 3048 B/op 69 allocs/op
|
||||||
BenchmarkJSONUnmarshalStruct-8 600000 9026 ns/op 1832 B/op 69 allocs/op
|
BenchmarkJSONUnmarshalStruct-8 600000 9268 ns/op 1832 B/op 69 allocs/op
|
||||||
BenchmarkJSONDecoder-8 300000 14339 ns/op 4224 B/op 184 allocs/op
|
BenchmarkJSONDecoder-8 300000 14120 ns/op 4224 B/op 184 allocs/op
|
||||||
BenchmarkFFJSONLexer-8 1500000 3156 ns/op 896 B/op 8 allocs/op
|
BenchmarkFFJSONLexer-8 1500000 3111 ns/op 896 B/op 8 allocs/op
|
||||||
BenchmarkEasyJSONLexer-8 3000000 938 ns/op 613 B/op 6 allocs/op
|
BenchmarkEasyJSONLexer-8 3000000 887 ns/op 613 B/op 6 allocs/op
|
||||||
BenchmarkJSONParserGet-8 3000000 442 ns/op 21 B/op 0 allocs/op
|
BenchmarkJSONParserGet-8 3000000 499 ns/op 21 B/op 0 allocs/op
|
||||||
|
BenchmarkJSONIterator-8 3000000 812 ns/op 544 B/op 9 allocs/op
|
||||||
```
|
```
|
||||||
|
|
||||||
Benchmarks for the `GetMany` function:
|
Benchmarks for the `GetMany` function:
|
||||||
|
|
||||||
```
|
```
|
||||||
BenchmarkGJSONGetMany4Paths-8 4000000 319 ns/op 112 B/op 0 allocs/op
|
BenchmarkGJSONGetMany4Paths-8 4000000 303 ns/op 112 B/op 0 allocs/op
|
||||||
BenchmarkGJSONGetMany8Paths-8 8000000 218 ns/op 56 B/op 0 allocs/op
|
BenchmarkGJSONGetMany8Paths-8 8000000 208 ns/op 56 B/op 0 allocs/op
|
||||||
BenchmarkGJSONGetMany16Paths-8 16000000 160 ns/op 56 B/op 0 allocs/op
|
BenchmarkGJSONGetMany16Paths-8 16000000 156 ns/op 56 B/op 0 allocs/op
|
||||||
BenchmarkGJSONGetMany32Paths-8 32000000 130 ns/op 64 B/op 0 allocs/op
|
BenchmarkGJSONGetMany32Paths-8 32000000 127 ns/op 64 B/op 0 allocs/op
|
||||||
BenchmarkGJSONGetMany64Paths-8 64000000 117 ns/op 64 B/op 0 allocs/op
|
BenchmarkGJSONGetMany64Paths-8 64000000 117 ns/op 64 B/op 0 allocs/op
|
||||||
BenchmarkGJSONGetMany128Paths-8 128000000 109 ns/op 64 B/op 0 allocs/op
|
BenchmarkGJSONGetMany128Paths-8 128000000 109 ns/op 64 B/op 0 allocs/op
|
||||||
```
|
```
|
||||||
@ -361,7 +410,8 @@ widget.text.data
|
|||||||
widget.text.size
|
widget.text.size
|
||||||
```
|
```
|
||||||
|
|
||||||
*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7.*
|
*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.8 and can be be found [here](https://github.com/tidwall/gjson-benchmarks).*
|
||||||
|
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
Josh Baker [@tidwall](http://twitter.com/tidwall)
|
Josh Baker [@tidwall](http://twitter.com/tidwall)
|
||||||
|
565
vendor/github.com/tidwall/gjson/gjson.go
generated
vendored
565
vendor/github.com/tidwall/gjson/gjson.go
generated
vendored
@ -2,8 +2,17 @@
|
|||||||
package gjson
|
package gjson
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unicode/utf8"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/tidwall/match"
|
"github.com/tidwall/match"
|
||||||
@ -65,7 +74,7 @@ type Result struct {
|
|||||||
func (t Result) String() string {
|
func (t Result) String() string {
|
||||||
switch t.Type {
|
switch t.Type {
|
||||||
default:
|
default:
|
||||||
return "null"
|
return ""
|
||||||
case False:
|
case False:
|
||||||
return "false"
|
return "false"
|
||||||
case Number:
|
case Number:
|
||||||
@ -101,12 +110,22 @@ func (t Result) Int() int64 {
|
|||||||
case True:
|
case True:
|
||||||
return 1
|
return 1
|
||||||
case String:
|
case String:
|
||||||
n, _ := strconv.ParseInt(t.Str, 10, 64)
|
n, _ := parseInt(t.Str)
|
||||||
return n
|
return n
|
||||||
case Number:
|
case Number:
|
||||||
|
// try to directly convert the float64 to int64
|
||||||
|
n, ok := floatToInt(t.Num)
|
||||||
|
if !ok {
|
||||||
|
// now try to parse the raw string
|
||||||
|
n, ok = parseInt(t.Raw)
|
||||||
|
if !ok {
|
||||||
|
// fallback to a standard conversion
|
||||||
return int64(t.Num)
|
return int64(t.Num)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Uint returns an unsigned integer representation.
|
// Uint returns an unsigned integer representation.
|
||||||
func (t Result) Uint() uint64 {
|
func (t Result) Uint() uint64 {
|
||||||
@ -116,12 +135,22 @@ func (t Result) Uint() uint64 {
|
|||||||
case True:
|
case True:
|
||||||
return 1
|
return 1
|
||||||
case String:
|
case String:
|
||||||
n, _ := strconv.ParseUint(t.Str, 10, 64)
|
n, _ := parseUint(t.Str)
|
||||||
return n
|
return n
|
||||||
case Number:
|
case Number:
|
||||||
|
// try to directly convert the float64 to uint64
|
||||||
|
n, ok := floatToUint(t.Num)
|
||||||
|
if !ok {
|
||||||
|
// now try to parse the raw string
|
||||||
|
n, ok = parseUint(t.Raw)
|
||||||
|
if !ok {
|
||||||
|
// fallback to a standard conversion
|
||||||
return uint64(t.Num)
|
return uint64(t.Num)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Float returns an float64 representation.
|
// Float returns an float64 representation.
|
||||||
func (t Result) Float() float64 {
|
func (t Result) Float() float64 {
|
||||||
@ -138,6 +167,12 @@ func (t Result) Float() float64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Time returns a time.Time representation.
|
||||||
|
func (t Result) Time() time.Time {
|
||||||
|
res, _ := time.Parse(time.RFC3339, t.String())
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// Array returns back an array of values.
|
// Array returns back an array of values.
|
||||||
// If the result represents a non-existent value, then an empty array will be returned.
|
// 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.
|
// If the result is not a JSON array, the return value will be an array containing one result.
|
||||||
@ -152,6 +187,16 @@ func (t Result) Array() []Result {
|
|||||||
return r.a
|
return r.a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsObject returns true if the result value is a JSON object.
|
||||||
|
func (t Result) IsObject() bool {
|
||||||
|
return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '{'
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsObject returns true if the result value is a JSON array.
|
||||||
|
func (t Result) IsArray() bool {
|
||||||
|
return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '['
|
||||||
|
}
|
||||||
|
|
||||||
// ForEach iterates through values.
|
// ForEach iterates through values.
|
||||||
// If the result represents a non-existent value, then no values will be iterated.
|
// If the result represents a non-existent value, then no values will be iterated.
|
||||||
// If the result is an Object, the iterator will pass the key and value of each item.
|
// If the result is an Object, the iterator will pass the key and value of each item.
|
||||||
@ -903,7 +948,7 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i, key, kesc, ok = i, c.json[s:], false, false
|
key, kesc, ok = c.json[s:], false, false
|
||||||
parse_key_string_done:
|
parse_key_string_done:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1076,8 +1121,8 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
|
|||||||
var multires []byte
|
var multires []byte
|
||||||
rp := parseArrayPath(path)
|
rp := parseArrayPath(path)
|
||||||
if !rp.arrch {
|
if !rp.arrch {
|
||||||
n, err := strconv.ParseUint(rp.part, 10, 64)
|
n, ok := parseUint(rp.part)
|
||||||
if err != nil {
|
if !ok {
|
||||||
partidx = -1
|
partidx = -1
|
||||||
} else {
|
} else {
|
||||||
partidx = int(n)
|
partidx = int(n)
|
||||||
@ -1221,7 +1266,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
|
|||||||
c.value.Type = JSON
|
c.value.Type = JSON
|
||||||
c.value.Raw = string(jsons)
|
c.value.Raw = string(jsons)
|
||||||
return i + 1, true
|
return i + 1, true
|
||||||
} else {
|
}
|
||||||
if rp.alogok {
|
if rp.alogok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1231,7 +1276,6 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
|
|||||||
c.calcd = true
|
c.calcd = true
|
||||||
return i + 1, true
|
return i + 1, true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if len(multires) > 0 && !c.value.Exists() {
|
if len(multires) > 0 && !c.value.Exists() {
|
||||||
c.value = Result{
|
c.value = Result{
|
||||||
Raw: string(append(multires, ']')),
|
Raw: string(append(multires, ']')),
|
||||||
@ -1357,6 +1401,12 @@ func GetBytes(json []byte, path string) Result {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runeit returns the rune from the the \uXXXX
|
||||||
|
func runeit(json string) rune {
|
||||||
|
n, _ := strconv.ParseUint(json[:4], 16, 64)
|
||||||
|
return rune(n)
|
||||||
|
}
|
||||||
|
|
||||||
// unescape unescapes a string
|
// unescape unescapes a string
|
||||||
func unescape(json string) string { //, error) {
|
func unescape(json string) string { //, error) {
|
||||||
var str = make([]byte, 0, len(json))
|
var str = make([]byte, 0, len(json))
|
||||||
@ -1365,15 +1415,15 @@ func unescape(json string) string { //, error) {
|
|||||||
default:
|
default:
|
||||||
str = append(str, json[i])
|
str = append(str, json[i])
|
||||||
case json[i] < ' ':
|
case json[i] < ' ':
|
||||||
return "" //, errors.New("invalid character in string")
|
return string(str)
|
||||||
case json[i] == '\\':
|
case json[i] == '\\':
|
||||||
i++
|
i++
|
||||||
if i >= len(json) {
|
if i >= len(json) {
|
||||||
return "" //, errors.New("invalid escape sequence")
|
return string(str)
|
||||||
}
|
}
|
||||||
switch json[i] {
|
switch json[i] {
|
||||||
default:
|
default:
|
||||||
return "" //, errors.New("invalid escape sequence")
|
return string(str)
|
||||||
case '\\':
|
case '\\':
|
||||||
str = append(str, '\\')
|
str = append(str, '\\')
|
||||||
case '/':
|
case '/':
|
||||||
@ -1392,29 +1442,27 @@ func unescape(json string) string { //, error) {
|
|||||||
str = append(str, '"')
|
str = append(str, '"')
|
||||||
case 'u':
|
case 'u':
|
||||||
if i+5 > len(json) {
|
if i+5 > len(json) {
|
||||||
return "" //, errors.New("invalid escape sequence")
|
return string(str)
|
||||||
}
|
}
|
||||||
i++
|
r := runeit(json[i+1:])
|
||||||
// extract the codepoint
|
i += 5
|
||||||
var code int
|
if utf16.IsSurrogate(r) {
|
||||||
for j := i; j < i+4; j++ {
|
// need another code
|
||||||
switch {
|
if len(json[i:]) >= 6 && json[i] == '\\' && json[i+1] == 'u' {
|
||||||
default:
|
// we expect it to be correct so just consume it
|
||||||
return "" //, errors.New("invalid escape sequence")
|
r = utf16.DecodeRune(r, runeit(json[i+2:]))
|
||||||
case json[j] >= '0' && json[j] <= '9':
|
i += 6
|
||||||
code += (int(json[j]) - '0') << uint(12-(j-i)*4)
|
|
||||||
case json[j] >= 'a' && json[j] <= 'f':
|
|
||||||
code += (int(json[j]) - 'a' + 10) << uint(12-(j-i)*4)
|
|
||||||
case json[j] >= 'a' && json[j] <= 'f':
|
|
||||||
code += (int(json[j]) - 'a' + 10) << uint(12-(j-i)*4)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
str = append(str, []byte(string(code))...)
|
// provide enough space to encode the largest utf8 possible
|
||||||
i += 3 // only 3 because we will increment on the for-loop
|
str = append(str, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
n := utf8.EncodeRune(str[len(str)-8:], r)
|
||||||
|
str = str[:len(str)-8+n]
|
||||||
|
i-- // backtrack index by one
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return string(str) //, nil
|
return string(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Less return true if a token is less than another token.
|
// Less return true if a token is less than another token.
|
||||||
@ -1742,7 +1790,6 @@ next_key:
|
|||||||
usedPaths++
|
usedPaths++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to match the key to the path
|
// try to match the key to the path
|
||||||
// this is spaghetti code but the idea is to minimize
|
// this is spaghetti code but the idea is to minimize
|
||||||
// calls and variable assignments when comparing the
|
// calls and variable assignments when comparing the
|
||||||
@ -1757,13 +1804,18 @@ next_key:
|
|||||||
}
|
}
|
||||||
if i < len(paths[j]) {
|
if i < len(paths[j]) {
|
||||||
if paths[j][i] == '.' {
|
if paths[j][i] == '.' {
|
||||||
// matched, but there still more keys in the path
|
// matched, but there are still more keys in path
|
||||||
goto match_not_atend
|
goto match_not_atend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(paths[j]) <= len(key) || kplen != 0 {
|
||||||
|
if len(paths[j]) != i {
|
||||||
|
goto nomatch
|
||||||
|
}
|
||||||
// matched and at the end of the path
|
// matched and at the end of the path
|
||||||
goto match_atend
|
goto match_atend
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// no match, jump to the nomatch label
|
// no match, jump to the nomatch label
|
||||||
goto nomatch
|
goto nomatch
|
||||||
match_atend:
|
match_atend:
|
||||||
@ -1798,6 +1850,9 @@ next_key:
|
|||||||
nomatch: // noop label
|
nomatch: // noop label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !hasMatch && i < len(json) && json[i] == '}' {
|
||||||
|
return i + 1, true
|
||||||
|
}
|
||||||
if !parsedVal {
|
if !parsedVal {
|
||||||
if hasMatch {
|
if hasMatch {
|
||||||
// we found a match and the value has not been parsed yet.
|
// we found a match and the value has not been parsed yet.
|
||||||
@ -1940,3 +1995,453 @@ func getMany512(json string, i int, paths []string) ([]Result, bool) {
|
|||||||
_, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results)
|
_, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results)
|
||||||
return results, ok
|
return results, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fieldsmu sync.RWMutex
|
||||||
|
var fields = make(map[string]map[string]int)
|
||||||
|
|
||||||
|
func assign(jsval Result, goval reflect.Value) {
|
||||||
|
if jsval.Type == Null {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch goval.Kind() {
|
||||||
|
default:
|
||||||
|
case reflect.Ptr:
|
||||||
|
if !goval.IsNil() {
|
||||||
|
newval := reflect.New(goval.Elem().Type())
|
||||||
|
assign(jsval, newval.Elem())
|
||||||
|
goval.Elem().Set(newval.Elem())
|
||||||
|
} else {
|
||||||
|
newval := reflect.New(goval.Type().Elem())
|
||||||
|
assign(jsval, newval.Elem())
|
||||||
|
goval.Set(newval)
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
fieldsmu.RLock()
|
||||||
|
sf := fields[goval.Type().String()]
|
||||||
|
fieldsmu.RUnlock()
|
||||||
|
if sf == nil {
|
||||||
|
fieldsmu.Lock()
|
||||||
|
sf = make(map[string]int)
|
||||||
|
for i := 0; i < goval.Type().NumField(); i++ {
|
||||||
|
f := goval.Type().Field(i)
|
||||||
|
tag := strings.Split(f.Tag.Get("json"), ",")[0]
|
||||||
|
if tag != "-" {
|
||||||
|
if tag != "" {
|
||||||
|
sf[tag] = i
|
||||||
|
sf[f.Name] = i
|
||||||
|
} else {
|
||||||
|
sf[f.Name] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields[goval.Type().String()] = sf
|
||||||
|
fieldsmu.Unlock()
|
||||||
|
}
|
||||||
|
jsval.ForEach(func(key, value Result) bool {
|
||||||
|
if idx, ok := sf[key.Str]; ok {
|
||||||
|
f := goval.Field(idx)
|
||||||
|
if f.CanSet() {
|
||||||
|
assign(value, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
case reflect.Slice:
|
||||||
|
if goval.Type().Elem().Kind() == reflect.Uint8 && jsval.Type == String {
|
||||||
|
data, _ := base64.StdEncoding.DecodeString(jsval.String())
|
||||||
|
goval.Set(reflect.ValueOf(data))
|
||||||
|
} else {
|
||||||
|
jsvals := jsval.Array()
|
||||||
|
slice := reflect.MakeSlice(goval.Type(), len(jsvals), len(jsvals))
|
||||||
|
for i := 0; i < len(jsvals); i++ {
|
||||||
|
assign(jsvals[i], slice.Index(i))
|
||||||
|
}
|
||||||
|
goval.Set(slice)
|
||||||
|
}
|
||||||
|
case reflect.Array:
|
||||||
|
i, n := 0, goval.Len()
|
||||||
|
jsval.ForEach(func(_, value Result) bool {
|
||||||
|
if i == n {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
assign(value, goval.Index(i))
|
||||||
|
i++
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
case reflect.Map:
|
||||||
|
if goval.Type().Key().Kind() == reflect.String && goval.Type().Elem().Kind() == reflect.Interface {
|
||||||
|
goval.Set(reflect.ValueOf(jsval.Value()))
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
goval.Set(reflect.ValueOf(jsval.Value()))
|
||||||
|
case reflect.Bool:
|
||||||
|
goval.SetBool(jsval.Bool())
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
goval.SetFloat(jsval.Float())
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
goval.SetInt(jsval.Int())
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
goval.SetUint(jsval.Uint())
|
||||||
|
case reflect.String:
|
||||||
|
goval.SetString(jsval.String())
|
||||||
|
}
|
||||||
|
if len(goval.Type().PkgPath()) > 0 {
|
||||||
|
v := goval.Addr()
|
||||||
|
if v.Type().NumMethod() > 0 {
|
||||||
|
if u, ok := v.Interface().(json.Unmarshaler); ok {
|
||||||
|
u.UnmarshalJSON([]byte(jsval.Raw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var validate uintptr = 1
|
||||||
|
|
||||||
|
// UnmarshalValidationEnabled provides the option to disable JSON validation
|
||||||
|
// during the Unmarshal routine. Validation is enabled by default.
|
||||||
|
func UnmarshalValidationEnabled(enabled bool) {
|
||||||
|
if enabled {
|
||||||
|
atomic.StoreUintptr(&validate, 1)
|
||||||
|
} else {
|
||||||
|
atomic.StoreUintptr(&validate, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal loads the JSON data into the value pointed to by v.
|
||||||
|
//
|
||||||
|
// This function works almost identically to json.Unmarshal except that
|
||||||
|
// gjson.Unmarshal will automatically attempt to convert JSON values to any Go
|
||||||
|
// type. For example, the JSON string "100" or the JSON number 100 can be equally
|
||||||
|
// assigned to Go string, int, byte, uint64, etc. This rule applies to all types.
|
||||||
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
|
if atomic.LoadUintptr(&validate) == 1 {
|
||||||
|
_, ok := validpayload(data, 0)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v := reflect.ValueOf(v); v.Kind() == reflect.Ptr {
|
||||||
|
assign(ParseBytes(data), v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validpayload(data []byte, i int) (outi int, ok bool) {
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
switch data[i] {
|
||||||
|
default:
|
||||||
|
i, ok = validany(data, i)
|
||||||
|
if !ok {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
switch data[i] {
|
||||||
|
default:
|
||||||
|
return i, false
|
||||||
|
case ' ', '\t', '\n', '\r':
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, true
|
||||||
|
case ' ', '\t', '\n', '\r':
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
func validany(data []byte, i int) (outi int, ok bool) {
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
switch data[i] {
|
||||||
|
default:
|
||||||
|
return i, false
|
||||||
|
case ' ', '\t', '\n', '\r':
|
||||||
|
continue
|
||||||
|
case '{':
|
||||||
|
return validobject(data, i+1)
|
||||||
|
case '[':
|
||||||
|
return validarray(data, i+1)
|
||||||
|
case '"':
|
||||||
|
return validstring(data, i+1)
|
||||||
|
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||||
|
return validnumber(data, i+1)
|
||||||
|
case 't':
|
||||||
|
return validtrue(data, i+1)
|
||||||
|
case 'f':
|
||||||
|
return validfalse(data, i+1)
|
||||||
|
case 'n':
|
||||||
|
return validnull(data, i+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
func validobject(data []byte, i int) (outi int, ok bool) {
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
switch data[i] {
|
||||||
|
default:
|
||||||
|
return i, false
|
||||||
|
case ' ', '\t', '\n', '\r':
|
||||||
|
continue
|
||||||
|
case '}':
|
||||||
|
return i + 1, true
|
||||||
|
case '"':
|
||||||
|
key:
|
||||||
|
if i, ok = validstring(data, i+1); !ok {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if i, ok = validcolon(data, i); !ok {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if i, ok = validany(data, i); !ok {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if i, ok = validcomma(data, i, '}'); !ok {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if data[i] == '}' {
|
||||||
|
return i + 1, true
|
||||||
|
}
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
if data[i] == '"' {
|
||||||
|
goto key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
func validcolon(data []byte, i int) (outi int, ok bool) {
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
switch data[i] {
|
||||||
|
default:
|
||||||
|
return i, false
|
||||||
|
case ' ', '\t', '\n', '\r':
|
||||||
|
continue
|
||||||
|
case ':':
|
||||||
|
return i + 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
func validcomma(data []byte, i int, end byte) (outi int, ok bool) {
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
switch data[i] {
|
||||||
|
default:
|
||||||
|
return i, false
|
||||||
|
case ' ', '\t', '\n', '\r':
|
||||||
|
continue
|
||||||
|
case ',':
|
||||||
|
return i, true
|
||||||
|
case end:
|
||||||
|
return i, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
func validarray(data []byte, i int) (outi int, ok bool) {
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
switch data[i] {
|
||||||
|
default:
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
if i, ok = validany(data, i); !ok {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if i, ok = validcomma(data, i, ']'); !ok {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if data[i] == ']' {
|
||||||
|
return i + 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ' ', '\t', '\n', '\r':
|
||||||
|
continue
|
||||||
|
case ']':
|
||||||
|
return i + 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
func validstring(data []byte, i int) (outi int, ok bool) {
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
if data[i] < ' ' {
|
||||||
|
return i, false
|
||||||
|
} else if data[i] == '\\' {
|
||||||
|
i++
|
||||||
|
if i == len(data) {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
switch data[i] {
|
||||||
|
default:
|
||||||
|
return i, false
|
||||||
|
case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
|
||||||
|
case 'u':
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
i++
|
||||||
|
if i >= len(data) {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if !((data[i] >= '0' && data[i] <= '9') ||
|
||||||
|
(data[i] >= 'a' && data[i] <= 'f') ||
|
||||||
|
(data[i] >= 'A' && data[i] <= 'F')) {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if data[i] == '"' {
|
||||||
|
return i + 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
func validnumber(data []byte, i int) (outi int, ok bool) {
|
||||||
|
i--
|
||||||
|
// sign
|
||||||
|
if data[i] == '-' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// int
|
||||||
|
if i == len(data) {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if data[i] == '0' {
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
if data[i] >= '0' && data[i] <= '9' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// frac
|
||||||
|
if i == len(data) {
|
||||||
|
return i, true
|
||||||
|
}
|
||||||
|
if data[i] == '.' {
|
||||||
|
i++
|
||||||
|
if i == len(data) {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if data[i] < '0' || data[i] > '9' {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
if data[i] >= '0' && data[i] <= '9' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// exp
|
||||||
|
if i == len(data) {
|
||||||
|
return i, true
|
||||||
|
}
|
||||||
|
if data[i] == 'e' || data[i] == 'E' {
|
||||||
|
i++
|
||||||
|
if i == len(data) {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if data[i] == '+' || data[i] == '-' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == len(data) {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
if data[i] < '0' || data[i] > '9' {
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
if data[i] >= '0' && data[i] <= '9' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func validtrue(data []byte, i int) (outi int, ok bool) {
|
||||||
|
if i+3 <= len(data) && data[i] == 'r' && data[i+1] == 'u' && data[i+2] == 'e' {
|
||||||
|
return i + 3, true
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
func validfalse(data []byte, i int) (outi int, ok bool) {
|
||||||
|
if i+4 <= len(data) && data[i] == 'a' && data[i+1] == 'l' && data[i+2] == 's' && data[i+3] == 'e' {
|
||||||
|
return i + 4, true
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
func validnull(data []byte, i int) (outi int, ok bool) {
|
||||||
|
if i+3 <= len(data) && data[i] == 'u' && data[i+1] == 'l' && data[i+2] == 'l' {
|
||||||
|
return i + 3, true
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid returns true if the input is valid json.
|
||||||
|
func Valid(json string) bool {
|
||||||
|
_, ok := validpayload([]byte(json), 0)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUint(s string) (n uint64, ok bool) {
|
||||||
|
var i int
|
||||||
|
if i == len(s) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
for ; i < len(s); i++ {
|
||||||
|
if s[i] >= '0' && s[i] <= '9' {
|
||||||
|
n = n*10 + uint64(s[i]-'0')
|
||||||
|
} else {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInt(s string) (n int64, ok bool) {
|
||||||
|
var i int
|
||||||
|
var sign bool
|
||||||
|
if len(s) > 0 && s[0] == '-' {
|
||||||
|
sign = true
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == len(s) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
for ; i < len(s); i++ {
|
||||||
|
if s[i] >= '0' && s[i] <= '9' {
|
||||||
|
n = n*10 + int64(s[i]-'0')
|
||||||
|
} else {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sign {
|
||||||
|
return n * -1, true
|
||||||
|
}
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
|
|
||||||
|
const minUint53 = 0
|
||||||
|
const maxUint53 = 4503599627370495
|
||||||
|
const minInt53 = -2251799813685248
|
||||||
|
const maxInt53 = 2251799813685247
|
||||||
|
|
||||||
|
func floatToUint(f float64) (n uint64, ok bool) {
|
||||||
|
n = uint64(f)
|
||||||
|
if float64(n) == f && n >= minUint53 && n <= maxUint53 {
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func floatToInt(f float64) (n int64, ok bool) {
|
||||||
|
n = int64(f)
|
||||||
|
if float64(n) == f && n >= minInt53 && n <= maxInt53 {
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
908
vendor/github.com/tidwall/gjson/gjson_test.go
generated
vendored
908
vendor/github.com/tidwall/gjson/gjson_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user