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"
"unicode/utf16"
"unicode/utf8"
"unsafe"
"github.com/tidwall/match"
)
@@ -583,6 +582,8 @@ func (t Result) Exists() bool {
// Number, for JSON numbers
// string, for JSON string literals
// nil, for JSON null
// map[string]interface{}, for JSON objects
// []interface{}, for JSON arrays
//
func (t Result) Value() interface{} {
if t.Type == String {
@@ -1383,64 +1384,14 @@ func Get(json, path string) Result {
}
}
}
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
}
}
fillIndex(json, c)
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.
// If working with bytes, this method preferred over Get(string(data), path)
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
return getBytes(json, path)
}
// 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] == '}' {
return i + 1, true
}
i++
for ; i < len(data); i++ {
if data[i] == '"' {
switch data[i] {
default:
return i, false
case ' ', '\t', '\n', '\r':
continue
case '"':
goto key
}
}
@@ -2056,6 +2013,20 @@ func Valid(json string) bool {
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) {
var i int
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())
}
func testvalid(json string, expect bool) {
func testvalid(t *testing.T, json string, expect bool) {
t.Helper()
_, ok := validpayload([]byte(json), 0)
if ok != expect {
panic("mismatch")
t.Fatal("mismatch")
}
}
func TestValidBasic(t *testing.T) {
testvalid("0", true)
testvalid("00", false)
testvalid("-00", false)
testvalid("-.", false)
testvalid("0.0", true)
testvalid("10.0", true)
testvalid("10e1", true)
testvalid("10EE", false)
testvalid("10E-", false)
testvalid("10E+", false)
testvalid("10E123", true)
testvalid("10E-123", true)
testvalid("10E-0123", true)
testvalid("", false)
testvalid(" ", false)
testvalid("{}", true)
testvalid("{", false)
testvalid("-", false)
testvalid("-1", true)
testvalid("-1.", false)
testvalid("-1.0", true)
testvalid(" -1.0", true)
testvalid(" -1.0 ", true)
testvalid("-1.0 ", true)
testvalid("-1.0 i", false)
testvalid("-1.0 i", false)
testvalid("true", true)
testvalid(" true", true)
testvalid(" true ", true)
testvalid(" True ", false)
testvalid(" tru", false)
testvalid("false", true)
testvalid(" false", true)
testvalid(" false ", true)
testvalid(" False ", false)
testvalid(" fals", false)
testvalid("null", true)
testvalid(" null", true)
testvalid(" null ", true)
testvalid(" Null ", false)
testvalid(" nul", false)
testvalid(" []", true)
testvalid(" [true]", true)
testvalid(" [ true, null ]", true)
testvalid(" [ true,]", false)
testvalid(`{"hello":"world"}`, true)
testvalid(`{ "hello": "world" }`, true)
testvalid(`{ "hello": "world", }`, false)
testvalid(`{"a":"b",}`, false)
testvalid(`{"a":"b","a"}`, false)
testvalid(`{"a":"b","a":}`, false)
testvalid(`{"a":"b","a":1}`, true)
testvalid(`{"a":"b","a": 1, "c":{"hi":"there"} }`, true)
testvalid(`{"a":"b","a": 1, "c":{"hi":"there", "easy":["going",{"mixed":"bag"}]} }`, true)
testvalid(`""`, true)
testvalid(`"`, false)
testvalid(`"\n"`, true)
testvalid(`"\"`, false)
testvalid(`"\\"`, true)
testvalid(`"a\\b"`, true)
testvalid(`"a\\b\\\"a"`, true)
testvalid(`"a\\b\\\uFFAAa"`, true)
testvalid(`"a\\b\\\uFFAZa"`, false)
testvalid(`"a\\b\\\uFFA"`, false)
testvalid(string(complicatedJSON), true)
testvalid(string(exampleJSON), true)
testvalid(t, "0", true)
testvalid(t, "00", false)
testvalid(t, "-00", false)
testvalid(t, "-.", false)
testvalid(t, "0.0", true)
testvalid(t, "10.0", true)
testvalid(t, "10e1", true)
testvalid(t, "10EE", false)
testvalid(t, "10E-", false)
testvalid(t, "10E+", false)
testvalid(t, "10E123", true)
testvalid(t, "10E-123", true)
testvalid(t, "10E-0123", true)
testvalid(t, "", false)
testvalid(t, " ", false)
testvalid(t, "{}", true)
testvalid(t, "{", false)
testvalid(t, "-", false)
testvalid(t, "-1", true)
testvalid(t, "-1.", false)
testvalid(t, "-1.0", true)
testvalid(t, " -1.0", true)
testvalid(t, " -1.0 ", true)
testvalid(t, "-1.0 ", true)
testvalid(t, "-1.0 i", false)
testvalid(t, "-1.0 i", false)
testvalid(t, "true", true)
testvalid(t, " true", true)
testvalid(t, " true ", true)
testvalid(t, " True ", false)
testvalid(t, " tru", false)
testvalid(t, "false", true)
testvalid(t, " false", true)
testvalid(t, " false ", true)
testvalid(t, " False ", false)
testvalid(t, " fals", false)
testvalid(t, "null", true)
testvalid(t, " null", true)
testvalid(t, " null ", true)
testvalid(t, " Null ", false)
testvalid(t, " nul", false)
testvalid(t, " []", true)
testvalid(t, " [true]", true)
testvalid(t, " [ true, null ]", true)
testvalid(t, " [ true,]", false)
testvalid(t, `{"hello":"world"}`, true)
testvalid(t, `{ "hello": "world" }`, true)
testvalid(t, `{ "hello": "world", }`, false)
testvalid(t, `{"a":"b",}`, false)
testvalid(t, `{"a":"b","a"}`, false)
testvalid(t, `{"a":"b","a":}`, false)
testvalid(t, `{"a":"b","a":1}`, true)
testvalid(t, `{"a":"b",2"1":2}`, false)
testvalid(t, `{"a":"b","a": 1, "c":{"hi":"there"} }`, true)
testvalid(t, `{"a":"b","a": 1, "c":{"hi":"there", "easy":["going",{"mixed":"bag"}]} }`, true)
testvalid(t, `""`, true)
testvalid(t, `"`, false)
testvalid(t, `"\n"`, true)
testvalid(t, `"\"`, false)
testvalid(t, `"\\"`, true)
testvalid(t, `"a\\b"`, true)
testvalid(t, `"a\\b\\\"a"`, true)
testvalid(t, `"a\\b\\\uFFAAa"`, true)
testvalid(t, `"a\\b\\\uFFAZa"`, false)
testvalid(t, `"a\\b\\\uFFA"`, false)
testvalid(t, string(complicatedJSON), true)
testvalid(t, string(exampleJSON), true)
}
var jsonchars = []string{"{", "[", ",", ":", "}", "]", "1", "0", "true", "false", "null", `""`, `"\""`, `"a"`}