6 Commits

Author SHA1 Message Date
Josh Baker
f123b34087 Add appengine support 2018-06-21 11:09:58 -07:00
Josh Baker
afaeb95620 Fix false validation
closes #73
2018-06-13 11:46:59 -07:00
Josh Baker
3cd3a11923 Merge pull request #71 from speier/master
valid bytes method
2018-04-29 08:13:28 -07:00
speier
3a977634eb valid bytes method 2018-04-27 15:54:16 +02:00
Josh Baker
9ed3f8e1a5 Merge pull request #67 from bcho/patch-1
Add `arrayOrMap` result description
2018-03-01 11:15:57 -07:00
hbc
93d61e6369 Add arrayOrMap result description
Explain `Result.Value` will return array and map when possible.
2018-02-28 11:21:22 +08:00
4 changed files with 178 additions and 122 deletions

View File

@@ -13,7 +13,6 @@ import (
"time" "time"
"unicode/utf16" "unicode/utf16"
"unicode/utf8" "unicode/utf8"
"unsafe"
"github.com/tidwall/match" "github.com/tidwall/match"
) )
@@ -583,6 +582,8 @@ func (t Result) Exists() bool {
// Number, for JSON numbers // Number, for JSON numbers
// string, for JSON string literals // string, for JSON string literals
// nil, for JSON null // nil, for JSON null
// map[string]interface{}, for JSON objects
// []interface{}, for JSON arrays
// //
func (t Result) Value() interface{} { func (t Result) Value() interface{} {
if t.Type == String { if t.Type == String {
@@ -1383,64 +1384,14 @@ func Get(json, path string) Result {
} }
} }
} }
if len(c.value.Raw) > 0 && !c.calcd { fillIndex(json, c)
jhdr := *(*reflect.StringHeader)(unsafe.Pointer(&json))
rhdr := *(*reflect.StringHeader)(unsafe.Pointer(&(c.value.Raw)))
c.value.Index = int(rhdr.Data - jhdr.Data)
if c.value.Index < 0 || c.value.Index >= len(json) {
c.value.Index = 0
}
}
return c.value return c.value
} }
func fromBytesGet(result Result) Result {
// safely get the string headers
rawhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Raw))
strhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Str))
// create byte slice headers
rawh := reflect.SliceHeader{Data: rawhi.Data, Len: rawhi.Len}
strh := reflect.SliceHeader{Data: strhi.Data, Len: strhi.Len}
if strh.Data == 0 {
// str is nil
if rawh.Data == 0 {
// raw is nil
result.Raw = ""
} else {
// raw has data, safely copy the slice header to a string
result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
}
result.Str = ""
} else if rawh.Data == 0 {
// raw is nil
result.Raw = ""
// str has data, safely copy the slice header to a string
result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
} else if strh.Data >= rawh.Data &&
int(strh.Data)+strh.Len <= int(rawh.Data)+rawh.Len {
// Str is a substring of Raw.
start := int(strh.Data - rawh.Data)
// safely copy the raw slice header
result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
// substring the raw
result.Str = result.Raw[start : start+strh.Len]
} else {
// safely copy both the raw and str slice headers to strings
result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
}
return result
}
// 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) // If working with bytes, this method preferred over Get(string(data), path)
func GetBytes(json []byte, path string) Result { func GetBytes(json []byte, path string) Result {
var result Result return getBytes(json, path)
if json != nil {
// unsafe cast to string
result = Get(*(*string)(unsafe.Pointer(&json)), path)
result = fromBytesGet(result)
}
return result
} }
// runeit returns the rune from the the \uXXXX // runeit returns the rune from the the \uXXXX
@@ -1863,8 +1814,14 @@ func validobject(data []byte, i int) (outi int, ok bool) {
if data[i] == '}' { if data[i] == '}' {
return i + 1, true return i + 1, true
} }
i++
for ; i < len(data); i++ { for ; i < len(data); i++ {
if data[i] == '"' { switch data[i] {
default:
return i, false
case ' ', '\t', '\n', '\r':
continue
case '"':
goto key goto key
} }
} }
@@ -2056,6 +2013,20 @@ func Valid(json string) bool {
return ok return ok
} }
// ValidBytes returns true if the input is valid json.
//
// if !gjson.Valid(json) {
// return errors.New("invalid json")
// }
// value := gjson.Get(json, "name.last")
//
// If working with bytes, this method preferred over Valid(string(data))
//
func ValidBytes(json []byte) bool {
_, ok := validpayload(json, 0)
return ok
}
func parseUint(s string) (n uint64, ok bool) { func parseUint(s string) (n uint64, ok bool) {
var i int var i int
if i == len(s) { if i == len(s) {

10
gjson_gae.go Normal file
View File

@@ -0,0 +1,10 @@
//+build appengine
package gjson
func getBytes(json []byte, path string) Result {
return Get(string(json), path)
}
func fillIndex(json string, c *parseContext) {
// noop. Use zero for the Index value.
}

73
gjson_ngae.go Normal file
View File

@@ -0,0 +1,73 @@
//+build !appengine
package gjson
import (
"reflect"
"unsafe"
)
// 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.
func getBytes(json []byte, path string) Result {
var result Result
if json != nil {
// unsafe cast to string
result = Get(*(*string)(unsafe.Pointer(&json)), path)
result = fromBytesGet(result)
}
return result
}
func fromBytesGet(result Result) Result {
// safely get the string headers
rawhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Raw))
strhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Str))
// create byte slice headers
rawh := reflect.SliceHeader{Data: rawhi.Data, Len: rawhi.Len}
strh := reflect.SliceHeader{Data: strhi.Data, Len: strhi.Len}
if strh.Data == 0 {
// str is nil
if rawh.Data == 0 {
// raw is nil
result.Raw = ""
} else {
// raw has data, safely copy the slice header to a string
result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
}
result.Str = ""
} else if rawh.Data == 0 {
// raw is nil
result.Raw = ""
// str has data, safely copy the slice header to a string
result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
} else if strh.Data >= rawh.Data &&
int(strh.Data)+strh.Len <= int(rawh.Data)+rawh.Len {
// Str is a substring of Raw.
start := int(strh.Data - rawh.Data)
// safely copy the raw slice header
result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
// substring the raw
result.Str = result.Raw[start : start+strh.Len]
} else {
// safely copy both the raw and str slice headers to strings
result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
}
return result
}
// fillIndex finds the position of Raw data and assigns it to the Index field
// of the resulting value. If the position cannot be found then Index zero is
// used instead.
func fillIndex(json string, c *parseContext) {
if len(c.value.Raw) > 0 && !c.calcd {
jhdr := *(*reflect.StringHeader)(unsafe.Pointer(&json))
rhdr := *(*reflect.StringHeader)(unsafe.Pointer(&(c.value.Raw)))
c.value.Index = int(rhdr.Data - jhdr.Data)
if c.value.Index < 0 || c.value.Index >= len(json) {
c.value.Index = 0
}
}
}

View File

@@ -970,80 +970,82 @@ func TestUnmarshal(t *testing.T) {
assert(t, str == Get(complicatedJSON, "LeftOut").String()) assert(t, str == Get(complicatedJSON, "LeftOut").String())
} }
func testvalid(json string, expect bool) { func testvalid(t *testing.T, json string, expect bool) {
t.Helper()
_, ok := validpayload([]byte(json), 0) _, ok := validpayload([]byte(json), 0)
if ok != expect { if ok != expect {
panic("mismatch") t.Fatal("mismatch")
} }
} }
func TestValidBasic(t *testing.T) { func TestValidBasic(t *testing.T) {
testvalid("0", true) testvalid(t, "0", true)
testvalid("00", false) testvalid(t, "00", false)
testvalid("-00", false) testvalid(t, "-00", false)
testvalid("-.", false) testvalid(t, "-.", false)
testvalid("0.0", true) testvalid(t, "0.0", true)
testvalid("10.0", true) testvalid(t, "10.0", true)
testvalid("10e1", true) testvalid(t, "10e1", true)
testvalid("10EE", false) testvalid(t, "10EE", false)
testvalid("10E-", false) testvalid(t, "10E-", false)
testvalid("10E+", false) testvalid(t, "10E+", false)
testvalid("10E123", true) testvalid(t, "10E123", true)
testvalid("10E-123", true) testvalid(t, "10E-123", true)
testvalid("10E-0123", true) testvalid(t, "10E-0123", true)
testvalid("", false) testvalid(t, "", false)
testvalid(" ", false) testvalid(t, " ", false)
testvalid("{}", true) testvalid(t, "{}", true)
testvalid("{", false) testvalid(t, "{", false)
testvalid("-", false) testvalid(t, "-", false)
testvalid("-1", true) testvalid(t, "-1", true)
testvalid("-1.", false) testvalid(t, "-1.", false)
testvalid("-1.0", true) testvalid(t, "-1.0", true)
testvalid(" -1.0", true) testvalid(t, " -1.0", true)
testvalid(" -1.0 ", true) testvalid(t, " -1.0 ", true)
testvalid("-1.0 ", true) testvalid(t, "-1.0 ", true)
testvalid("-1.0 i", false) testvalid(t, "-1.0 i", false)
testvalid("-1.0 i", false) testvalid(t, "-1.0 i", false)
testvalid("true", true) testvalid(t, "true", true)
testvalid(" true", true) testvalid(t, " true", true)
testvalid(" true ", true) testvalid(t, " true ", true)
testvalid(" True ", false) testvalid(t, " True ", false)
testvalid(" tru", false) testvalid(t, " tru", false)
testvalid("false", true) testvalid(t, "false", true)
testvalid(" false", true) testvalid(t, " false", true)
testvalid(" false ", true) testvalid(t, " false ", true)
testvalid(" False ", false) testvalid(t, " False ", false)
testvalid(" fals", false) testvalid(t, " fals", false)
testvalid("null", true) testvalid(t, "null", true)
testvalid(" null", true) testvalid(t, " null", true)
testvalid(" null ", true) testvalid(t, " null ", true)
testvalid(" Null ", false) testvalid(t, " Null ", false)
testvalid(" nul", false) testvalid(t, " nul", false)
testvalid(" []", true) testvalid(t, " []", true)
testvalid(" [true]", true) testvalid(t, " [true]", true)
testvalid(" [ true, null ]", true) testvalid(t, " [ true, null ]", true)
testvalid(" [ true,]", false) testvalid(t, " [ true,]", false)
testvalid(`{"hello":"world"}`, true) testvalid(t, `{"hello":"world"}`, true)
testvalid(`{ "hello": "world" }`, true) testvalid(t, `{ "hello": "world" }`, true)
testvalid(`{ "hello": "world", }`, false) testvalid(t, `{ "hello": "world", }`, false)
testvalid(`{"a":"b",}`, false) testvalid(t, `{"a":"b",}`, false)
testvalid(`{"a":"b","a"}`, false) testvalid(t, `{"a":"b","a"}`, false)
testvalid(`{"a":"b","a":}`, false) testvalid(t, `{"a":"b","a":}`, false)
testvalid(`{"a":"b","a":1}`, true) testvalid(t, `{"a":"b","a":1}`, true)
testvalid(`{"a":"b","a": 1, "c":{"hi":"there"} }`, true) testvalid(t, `{"a":"b",2"1":2}`, false)
testvalid(`{"a":"b","a": 1, "c":{"hi":"there", "easy":["going",{"mixed":"bag"}]} }`, true) testvalid(t, `{"a":"b","a": 1, "c":{"hi":"there"} }`, true)
testvalid(`""`, true) testvalid(t, `{"a":"b","a": 1, "c":{"hi":"there", "easy":["going",{"mixed":"bag"}]} }`, true)
testvalid(`"`, false) testvalid(t, `""`, true)
testvalid(`"\n"`, true) testvalid(t, `"`, false)
testvalid(`"\"`, false) testvalid(t, `"\n"`, true)
testvalid(`"\\"`, true) testvalid(t, `"\"`, false)
testvalid(`"a\\b"`, true) testvalid(t, `"\\"`, true)
testvalid(`"a\\b\\\"a"`, true) testvalid(t, `"a\\b"`, true)
testvalid(`"a\\b\\\uFFAAa"`, true) testvalid(t, `"a\\b\\\"a"`, true)
testvalid(`"a\\b\\\uFFAZa"`, false) testvalid(t, `"a\\b\\\uFFAAa"`, true)
testvalid(`"a\\b\\\uFFA"`, false) testvalid(t, `"a\\b\\\uFFAZa"`, false)
testvalid(string(complicatedJSON), true) testvalid(t, `"a\\b\\\uFFA"`, false)
testvalid(string(exampleJSON), true) testvalid(t, string(complicatedJSON), true)
testvalid(t, string(exampleJSON), true)
} }
var jsonchars = []string{"{", "[", ",", ":", "}", "]", "1", "0", "true", "false", "null", `""`, `"\""`, `"a"`} var jsonchars = []string{"{", "[", ",", ":", "}", "]", "1", "0", "true", "false", "null", `""`, `"\""`, `"a"`}