Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
080cd22816 | ||
|
|
182ad76050 | ||
|
|
67e2a63ac7 | ||
|
|
922b012d22 | ||
|
|
ac7b6ae6f2 |
48
README.md
48
README.md
@@ -234,53 +234,6 @@ 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
|
||||
|
||||
To unmarshal to a `map[string]interface{}`:
|
||||
@@ -338,7 +291,6 @@ and [json-iterator](https://github.com/json-iterator/go)
|
||||
BenchmarkGJSONGet-8 3000000 372 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGJSONUnmarshalMap-8 900000 4154 ns/op 1920 B/op 26 allocs/op
|
||||
BenchmarkJSONUnmarshalMap-8 600000 9019 ns/op 3048 B/op 69 allocs/op
|
||||
BenchmarkJSONUnmarshalStruct-8 600000 9268 ns/op 1832 B/op 69 allocs/op
|
||||
BenchmarkJSONDecoder-8 300000 14120 ns/op 4224 B/op 184 allocs/op
|
||||
BenchmarkFFJSONLexer-8 1500000 3111 ns/op 896 B/op 8 allocs/op
|
||||
BenchmarkEasyJSONLexer-8 3000000 887 ns/op 613 B/op 6 allocs/op
|
||||
|
||||
14
gjson.go
14
gjson.go
@@ -177,8 +177,8 @@ func (t Result) Time() time.Time {
|
||||
// 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.
|
||||
func (t Result) Array() []Result {
|
||||
if !t.Exists() {
|
||||
return nil
|
||||
if t.Type == Null {
|
||||
return []Result{}
|
||||
}
|
||||
if t.Type != JSON {
|
||||
return []Result{t}
|
||||
@@ -192,7 +192,7 @@ 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.
|
||||
// IsArray 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] == '['
|
||||
}
|
||||
@@ -511,7 +511,7 @@ func tonum(json string) (raw string, num float64) {
|
||||
|
||||
func tolit(json string) (raw string) {
|
||||
for i := 1; i < len(json); i++ {
|
||||
if json[i] <= 'a' || json[i] >= 'z' {
|
||||
if json[i] < 'a' || json[i] > 'z' {
|
||||
return json[:i]
|
||||
}
|
||||
}
|
||||
@@ -1880,7 +1880,7 @@ next_key:
|
||||
} else {
|
||||
// Since there was no matches we can just parse the value and
|
||||
// ignore the result.
|
||||
if i, _, ok = parseAny(json, i, false); !ok {
|
||||
if i, _, ok = parseAny(json, i, true); !ok {
|
||||
return i, false
|
||||
}
|
||||
}
|
||||
@@ -2099,6 +2099,8 @@ var validate uintptr = 1
|
||||
|
||||
// UnmarshalValidationEnabled provides the option to disable JSON validation
|
||||
// during the Unmarshal routine. Validation is enabled by default.
|
||||
//
|
||||
// Deprecated: Use encoder/json.Unmarshal instead
|
||||
func UnmarshalValidationEnabled(enabled bool) {
|
||||
if enabled {
|
||||
atomic.StoreUintptr(&validate, 1)
|
||||
@@ -2113,6 +2115,8 @@ func UnmarshalValidationEnabled(enabled bool) {
|
||||
// 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.
|
||||
//
|
||||
// Deprecated: Use encoder/json.Unmarshal instead
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
if atomic.LoadUintptr(&validate) == 1 {
|
||||
_, ok := validpayload(data, 0)
|
||||
|
||||
177
gjson_test.go
177
gjson_test.go
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -1101,3 +1102,179 @@ func TestGetMany48(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResultRawForLiteral(t *testing.T) {
|
||||
for _, lit := range []string{"null", "true", "false"} {
|
||||
result := Parse(lit)
|
||||
if result.Raw != lit {
|
||||
t.Fatalf("expected '%v', got '%v'", lit, result.Raw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNullArray(t *testing.T) {
|
||||
n := len(Get(`{"data":null}`, "data").Array())
|
||||
if n != 0 {
|
||||
t.Fatalf("expected '%v', got '%v'", 0, n)
|
||||
}
|
||||
n = len(Get(`{}`, "data").Array())
|
||||
if n != 0 {
|
||||
t.Fatalf("expected '%v', got '%v'", 0, n)
|
||||
}
|
||||
n = len(Get(`{"data":[]}`, "data").Array())
|
||||
if n != 0 {
|
||||
t.Fatalf("expected '%v', got '%v'", 0, n)
|
||||
}
|
||||
n = len(Get(`{"data":[null]}`, "data").Array())
|
||||
if n != 1 {
|
||||
t.Fatalf("expected '%v', got '%v'", 1, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomGetMany(t *testing.T) {
|
||||
start := time.Now()
|
||||
for time.Since(start) < time.Second*3 {
|
||||
testRandomGetMany(t)
|
||||
}
|
||||
}
|
||||
func testRandomGetMany(t *testing.T) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
json, keys := randomJSON()
|
||||
for _, key := range keys {
|
||||
r := Get(json, key)
|
||||
if !r.Exists() {
|
||||
t.Fatal("should exist")
|
||||
}
|
||||
}
|
||||
rkeysi := rand.Perm(len(keys))
|
||||
rkeysn := 1 + rand.Int()%32
|
||||
if len(rkeysi) > rkeysn {
|
||||
rkeysi = rkeysi[:rkeysn]
|
||||
}
|
||||
var rkeys []string
|
||||
for i := 0; i < len(rkeysi); i++ {
|
||||
rkeys = append(rkeys, keys[rkeysi[i]])
|
||||
}
|
||||
mres1 := GetMany(json, rkeys...)
|
||||
var mres2 []Result
|
||||
for _, rkey := range rkeys {
|
||||
mres2 = append(mres2, Get(json, rkey))
|
||||
}
|
||||
if len(mres1) != len(mres2) {
|
||||
t.Fatalf("expected %d, got %d", len(mres2), len(mres1))
|
||||
}
|
||||
for i := 0; i < len(mres1); i++ {
|
||||
mres1[i].Index = 0
|
||||
mres2[i].Index = 0
|
||||
v1 := fmt.Sprintf("%#v", mres1[i])
|
||||
v2 := fmt.Sprintf("%#v", mres2[i])
|
||||
if v1 != v2 {
|
||||
t.Fatalf("\nexpected %s\n"+
|
||||
" got %s", v2, v1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue54(t *testing.T) {
|
||||
var r []Result
|
||||
json := `{"MarketName":null,"Nounce":6115}`
|
||||
r = GetMany(json, "Nounce", "Buys", "Sells", "Fills")
|
||||
if strings.Replace(fmt.Sprintf("%v", r), " ", "", -1) != "[6115]" {
|
||||
t.Fatalf("expected '%v', got '%v'", "[6115]", strings.Replace(fmt.Sprintf("%v", r), " ", "", -1))
|
||||
}
|
||||
r = GetMany(json, "Nounce", "Buys", "Sells")
|
||||
if strings.Replace(fmt.Sprintf("%v", r), " ", "", -1) != "[6115]" {
|
||||
t.Fatalf("expected '%v', got '%v'", "[6115]", strings.Replace(fmt.Sprintf("%v", r), " ", "", -1))
|
||||
}
|
||||
r = GetMany(json, "Nounce")
|
||||
if strings.Replace(fmt.Sprintf("%v", r), " ", "", -1) != "[6115]" {
|
||||
t.Fatalf("expected '%v', got '%v'", "[6115]", strings.Replace(fmt.Sprintf("%v", r), " ", "", -1))
|
||||
}
|
||||
}
|
||||
|
||||
func randomString() string {
|
||||
var key string
|
||||
N := 1 + rand.Int()%16
|
||||
for i := 0; i < N; i++ {
|
||||
r := rand.Int() % 62
|
||||
if r < 10 {
|
||||
key += string(byte('0' + r))
|
||||
} else if r-10 < 26 {
|
||||
key += string(byte('a' + r - 10))
|
||||
} else {
|
||||
key += string(byte('A' + r - 10 - 26))
|
||||
}
|
||||
}
|
||||
return `"` + key + `"`
|
||||
}
|
||||
func randomBool() string {
|
||||
switch rand.Int() % 2 {
|
||||
default:
|
||||
return "false"
|
||||
case 1:
|
||||
return "true"
|
||||
}
|
||||
}
|
||||
func randomNumber() string {
|
||||
return strconv.FormatInt(int64(rand.Int()%1000000), 10)
|
||||
}
|
||||
|
||||
func randomObjectOrArray(keys []string, prefix string, array bool, depth int) (string, []string) {
|
||||
N := 5 + rand.Int()%5
|
||||
var json string
|
||||
if array {
|
||||
json = "["
|
||||
} else {
|
||||
json = "{"
|
||||
}
|
||||
for i := 0; i < N; i++ {
|
||||
if i > 0 {
|
||||
json += ","
|
||||
}
|
||||
var pkey string
|
||||
if array {
|
||||
pkey = prefix + "." + strconv.FormatInt(int64(i), 10)
|
||||
} else {
|
||||
key := randomString()
|
||||
pkey = prefix + "." + key[1:len(key)-1]
|
||||
json += key + `:`
|
||||
}
|
||||
keys = append(keys, pkey[1:])
|
||||
var kind int
|
||||
if depth == 5 {
|
||||
kind = rand.Int() % 4
|
||||
} else {
|
||||
kind = rand.Int() % 6
|
||||
}
|
||||
switch kind {
|
||||
case 0:
|
||||
json += randomString()
|
||||
case 1:
|
||||
json += randomBool()
|
||||
case 2:
|
||||
json += "null"
|
||||
case 3:
|
||||
json += randomNumber()
|
||||
case 4:
|
||||
var njson string
|
||||
njson, keys = randomObjectOrArray(keys, pkey, true, depth+1)
|
||||
json += njson
|
||||
case 5:
|
||||
var njson string
|
||||
njson, keys = randomObjectOrArray(keys, pkey, false, depth+1)
|
||||
json += njson
|
||||
}
|
||||
|
||||
}
|
||||
if array {
|
||||
json += "]"
|
||||
} else {
|
||||
json += "}"
|
||||
}
|
||||
return json, keys
|
||||
}
|
||||
|
||||
func randomJSON() (json string, keys []string) {
|
||||
//rand.Seed(time.Now().UnixNano())
|
||||
return randomObjectOrArray(nil, "", false, 0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user