added path escaping

This commit is contained in:
Josh Baker 2016-08-11 18:51:29 -07:00
parent 06af1af34e
commit 1e941a433e
2 changed files with 126 additions and 58 deletions

148
gjson.go
View File

@ -121,7 +121,38 @@ func Get(json string, path string) Result {
// parse the path. just split on the dot // parse the path. just split on the dot
for i := 0; i < len(path); i++ { 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]}) parts = append(parts, part{wild: wild, key: path[s:i]})
if wild { if wild {
wild = false wild = false
@ -132,6 +163,7 @@ func Get(json string, path string) Result {
} }
} }
parts = append(parts, part{wild: wild, key: path[s:]}) parts = append(parts, part{wild: wild, key: path[s:]})
end_parts:
var i, depth int var i, depth int
var squashed string var squashed string
@ -223,33 +255,19 @@ read_key:
var val string var val string
var vc byte var vc byte
for ; i < len(json); i++ { for ; i < len(json); i++ {
switch json[i] { if json[i] < '"' { // control character
case 't', 'f', 'n': // true, false, null continue
vc = json[i] }
s = i if json[i] < '-' { // string
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
i++ i++
// we read the val below // we read the val below
vc = '"' vc = '"'
goto proc_val 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' vc = '0'
s = i s = i
i++ i++
@ -265,6 +283,28 @@ read_key:
val = json[s:i] val = json[s:i]
goto proc_val 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 // sanity check before we move on
@ -280,37 +320,39 @@ proc_delim:
// the first '[' or '{' has already been read // the first '[' or '{' has already been read
depth := 1 depth := 1
for ; i < len(json); i++ { for ; i < len(json); i++ {
if json[i] == '{' || json[i] == '[' { if json[i] >= '"' && json[i] <= '}' {
depth++ if json[i] == '{' || json[i] == '[' {
} else if json[i] == '}' || json[i] == ']' { depth++
depth-- } else if json[i] == '}' || json[i] == ']' {
if depth == 0 { depth--
i++ if depth == 0 {
break i++
} break
} else if json[i] == '"' { }
i++ } else if json[i] == '"' {
s2 := i i++
for ; i < len(json); i++ { s2 := i
if json[i] == '"' { for ; i < len(json); i++ {
// look for an escaped slash if json[i] == '"' {
if json[i-1] == '\\' { // look for an escaped slash
n := 0 if json[i-1] == '\\' {
for j := i - 2; j > s2-1; j-- { n := 0
if json[j] != '\\' { for j := i - 2; j > s2-1; j-- {
break if json[j] != '\\' {
} break
n++ }
} n++
if n%2 == 0 { }
continue if n%2 == 0 {
} continue
} }
}
break
}
}
if i == len(json) {
break break
} }
}
if i == len(json) {
break
} }
} }
} }

View File

@ -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. // this json block is poorly formed on purpose.
var basicJSON = `{"age":100, "name":{"here":"B\\\"R"}, var basicJSON = `{"age":100, "name":{"here":"B\\\"R"},
"noop":{"what is a wren?":"a bird"}, "noop":{"what is a wren?":"a bird"},
"happy":true,"immortal":false, "happy":true,"immortal":false,
"escaped\\\"":true,
"arr":["1",2,"3",{"hello":"world"},"4",5], "arr":["1",2,"3",{"hello":"world"},"4",5],
"vals":[1,2,3,{"sadf":sdf"asdf"}],"name":{"first":"tom","last":null}}` "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") t.Fatal("should be nil")
} }
if !Get(basicJSON, "escaped\\\"").Value().(bool) {
t.Fatal("could not escape")
}
Get(basicJSON, "vals.hello") Get(basicJSON, "vals.hello")
} }