mirror of https://github.com/tidwall/gjson.git
added path escaping
This commit is contained in:
parent
06af1af34e
commit
1e941a433e
148
gjson.go
148
gjson.go
|
@ -121,7 +121,38 @@ func Get(json string, path string) Result {
|
|||
|
||||
// parse the path. just split on the dot
|
||||
for i := 0; i < len(path); i++ {
|
||||
if path[i] == '.' {
|
||||
next_part:
|
||||
if path[i] == '\\' {
|
||||
// go into escape mode
|
||||
epart := []byte(path[s:i])
|
||||
i++
|
||||
if i < len(path) {
|
||||
epart = append(epart, path[i])
|
||||
i++
|
||||
for ; i < len(path); i++ {
|
||||
if path[i] == '\\' {
|
||||
i++
|
||||
if i < len(path) {
|
||||
epart = append(epart, path[i])
|
||||
}
|
||||
continue
|
||||
} else if path[i] == '.' {
|
||||
parts = append(parts, part{wild: wild, key: string(epart)})
|
||||
if wild {
|
||||
wild = false
|
||||
}
|
||||
s = i + 1
|
||||
i++
|
||||
goto next_part
|
||||
} else if path[i] == '*' || path[i] == '?' {
|
||||
wild = true
|
||||
}
|
||||
epart = append(epart, path[i])
|
||||
}
|
||||
}
|
||||
parts = append(parts, part{wild: wild, key: string(epart)})
|
||||
goto end_parts
|
||||
} else if path[i] == '.' {
|
||||
parts = append(parts, part{wild: wild, key: path[s:i]})
|
||||
if wild {
|
||||
wild = false
|
||||
|
@ -132,6 +163,7 @@ func Get(json string, path string) Result {
|
|||
}
|
||||
}
|
||||
parts = append(parts, part{wild: wild, key: path[s:]})
|
||||
end_parts:
|
||||
|
||||
var i, depth int
|
||||
var squashed string
|
||||
|
@ -223,33 +255,19 @@ read_key:
|
|||
var val string
|
||||
var vc byte
|
||||
for ; i < len(json); i++ {
|
||||
switch json[i] {
|
||||
case 't', 'f', 'n': // true, false, null
|
||||
vc = json[i]
|
||||
s = i
|
||||
i++
|
||||
for ; i < len(json); i++ {
|
||||
// let's pick up any character. it doesn't matter.
|
||||
if json[i] < 'a' || json[i] > 'z' {
|
||||
break
|
||||
}
|
||||
}
|
||||
val = json[s:i]
|
||||
goto proc_val
|
||||
case '{': // open object
|
||||
i++
|
||||
vc = '{'
|
||||
goto proc_delim
|
||||
case '[': // open array
|
||||
i++
|
||||
vc = '['
|
||||
goto proc_delim
|
||||
case '"': // string
|
||||
if json[i] < '"' { // control character
|
||||
continue
|
||||
}
|
||||
if json[i] < '-' { // string
|
||||
i++
|
||||
// we read the val below
|
||||
vc = '"'
|
||||
goto proc_val
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': // number
|
||||
}
|
||||
if json[i] < '[' { // number
|
||||
if json[i] == ':' {
|
||||
continue
|
||||
}
|
||||
vc = '0'
|
||||
s = i
|
||||
i++
|
||||
|
@ -265,6 +283,28 @@ read_key:
|
|||
val = json[s:i]
|
||||
goto proc_val
|
||||
}
|
||||
if json[i] < ']' { // '['
|
||||
i++
|
||||
vc = '['
|
||||
goto proc_delim
|
||||
}
|
||||
if json[i] < 'u' { // true, false, null
|
||||
vc = json[i]
|
||||
s = i
|
||||
i++
|
||||
for ; i < len(json); i++ {
|
||||
// let's pick up any character. it doesn't matter.
|
||||
if json[i] < 'a' || json[i] > 'z' {
|
||||
break
|
||||
}
|
||||
}
|
||||
val = json[s:i]
|
||||
goto proc_val
|
||||
}
|
||||
// must be an open objet
|
||||
i++
|
||||
vc = '{'
|
||||
goto proc_delim
|
||||
}
|
||||
|
||||
// sanity check before we move on
|
||||
|
@ -280,37 +320,39 @@ proc_delim:
|
|||
// the first '[' or '{' has already been read
|
||||
depth := 1
|
||||
for ; i < len(json); i++ {
|
||||
if json[i] == '{' || json[i] == '[' {
|
||||
depth++
|
||||
} else if json[i] == '}' || json[i] == ']' {
|
||||
depth--
|
||||
if depth == 0 {
|
||||
i++
|
||||
break
|
||||
}
|
||||
} else if json[i] == '"' {
|
||||
i++
|
||||
s2 := i
|
||||
for ; i < len(json); i++ {
|
||||
if json[i] == '"' {
|
||||
// look for an escaped slash
|
||||
if json[i-1] == '\\' {
|
||||
n := 0
|
||||
for j := i - 2; j > s2-1; j-- {
|
||||
if json[j] != '\\' {
|
||||
break
|
||||
}
|
||||
n++
|
||||
}
|
||||
if n%2 == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if json[i] >= '"' && json[i] <= '}' {
|
||||
if json[i] == '{' || json[i] == '[' {
|
||||
depth++
|
||||
} else if json[i] == '}' || json[i] == ']' {
|
||||
depth--
|
||||
if depth == 0 {
|
||||
i++
|
||||
break
|
||||
}
|
||||
} else if json[i] == '"' {
|
||||
i++
|
||||
s2 := i
|
||||
for ; i < len(json); i++ {
|
||||
if json[i] == '"' {
|
||||
// look for an escaped slash
|
||||
if json[i-1] == '\\' {
|
||||
n := 0
|
||||
for j := i - 2; j > s2-1; j-- {
|
||||
if json[j] != '\\' {
|
||||
break
|
||||
}
|
||||
n++
|
||||
}
|
||||
if n%2 == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == len(json) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == len(json) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,12 +61,42 @@ func TestRandomValidStrings(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
func testEscapePath(t *testing.T, json, path, expect string) {
|
||||
if Get(json, path).String() != expect {
|
||||
t.Fatalf("expected '%v', got '%v'", expect, Get(json, path).String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestEscapePath(t *testing.T) {
|
||||
json := `{
|
||||
"test":{
|
||||
"*":"valZ",
|
||||
"*v":"val0",
|
||||
"keyv*":"val1",
|
||||
"key*v":"val2",
|
||||
"keyv?":"val3",
|
||||
"key?v":"val4",
|
||||
"keyv.":"val5",
|
||||
"key.v":"val6",
|
||||
"keyk*":{"key?":"val7"}
|
||||
}
|
||||
}`
|
||||
|
||||
testEscapePath(t, json, "test.\\*", "valZ")
|
||||
testEscapePath(t, json, "test.\\*v", "val0")
|
||||
testEscapePath(t, json, "test.keyv\\*", "val1")
|
||||
testEscapePath(t, json, "test.key\\*v", "val2")
|
||||
testEscapePath(t, json, "test.keyv\\?", "val3")
|
||||
testEscapePath(t, json, "test.key\\?v", "val4")
|
||||
testEscapePath(t, json, "test.keyv\\.", "val5")
|
||||
testEscapePath(t, json, "test.key\\.v", "val6")
|
||||
testEscapePath(t, json, "test.keyk\\*.key\\?", "val7")
|
||||
}
|
||||
|
||||
// this json block is poorly formed on purpose.
|
||||
var basicJSON = `{"age":100, "name":{"here":"B\\\"R"},
|
||||
"noop":{"what is a wren?":"a bird"},
|
||||
"happy":true,"immortal":false,
|
||||
"escaped\\\"":true,
|
||||
"arr":["1",2,"3",{"hello":"world"},"4",5],
|
||||
"vals":[1,2,3,{"sadf":sdf"asdf"}],"name":{"first":"tom","last":null}}`
|
||||
|
||||
|
@ -123,10 +153,6 @@ func TestBasic(t *testing.T) {
|
|||
t.Fatal("should be nil")
|
||||
}
|
||||
|
||||
if !Get(basicJSON, "escaped\\\"").Value().(bool) {
|
||||
t.Fatal("could not escape")
|
||||
}
|
||||
|
||||
Get(basicJSON, "vals.hello")
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue