Added Escape function

This commit adds the Escape function for escaping a path
component, making it possible to directly querying keys that have
special characters like dots.

```
json := `{
  "user":{
      "first.name": "Janet",
      "last.name": "Prichard"
    }
}`
user := gjson.Get(json, "user")
println(user.Get(gjson.Escape("first.name")).String())
println(user.Get(gjson.Escape("last.name")).String())
// Output:
// Janet
// Prichard
```

See #333
This commit is contained in:
tidwall 2023-09-22 10:13:56 -07:00
parent be1bb7d64a
commit 6ee9f877d6
2 changed files with 35 additions and 8 deletions

View File

@ -3410,7 +3410,7 @@ func (t Result) Path(json string) string {
if !rcomp.Exists() { if !rcomp.Exists() {
goto fail goto fail
} }
comp := escapeComp(rcomp.String()) comp := Escape(rcomp.String())
path = append(path, '.') path = append(path, '.')
path = append(path, comp...) path = append(path, comp...)
} }
@ -3425,17 +3425,31 @@ fail:
// isSafePathKeyChar returns true if the input character is safe for not // isSafePathKeyChar returns true if the input character is safe for not
// needing escaping. // needing escaping.
func isSafePathKeyChar(c byte) bool { func isSafePathKeyChar(c byte) bool {
return c <= ' ' || c > '~' || c == '_' || c == '-' || c == ':' || return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c <= ' ' || c > '~' || c == '_' ||
(c >= '0' && c <= '9') c == '-' || c == ':'
} }
// escapeComp escaped a path compontent, making it safe for generating a // Escape returns an escaped path component.
// path for later use. //
func escapeComp(comp string) string { // json := `{
// "user":{
// "first.name": "Janet",
// "last.name": "Prichard"
// }
// }`
// user := gjson.Get(json, "user")
// println(user.Get(gjson.Escape("first.name"))
// println(user.Get(gjson.Escape("last.name"))
// // Output:
// // Janet
// // Prichard
func Escape(comp string) string {
for i := 0; i < len(comp); i++ { for i := 0; i < len(comp); i++ {
if !isSafePathKeyChar(comp[i]) { if !isSafePathKeyChar(comp[i]) {
ncomp := []byte(comp[:i]) ncomp := make([]byte, len(comp)+1)
copy(ncomp, comp[:i])
ncomp = ncomp[:i]
for ; i < len(comp); i++ { for ; i < len(comp); i++ {
if !isSafePathKeyChar(comp[i]) { if !isSafePathKeyChar(comp[i]) {
ncomp = append(ncomp, '\\') ncomp = append(ncomp, '\\')

View File

@ -2701,3 +2701,16 @@ func TestModDig(t *testing.T) {
assert(t, Get(json, "@dig:name").String() == `["melinda","jake"]`) assert(t, Get(json, "@dig:name").String() == `["melinda","jake"]`)
assert(t, Get(json, "@dig:secret").String() == `["password"]`) assert(t, Get(json, "@dig:secret").String() == `["password"]`)
} }
func TestEscape(t *testing.T) {
json := `{
"user":{
"first.name": "Janet",
"last.name": "Prichard"
}
}`
user := Get(json, "user")
assert(t, user.Get(Escape("first.name")).String() == "Janet")
assert(t, user.Get(Escape("last.name")).String() == "Prichard")
assert(t, user.Get("first.name").String() == "")
}