From 6ee9f877d683381343bc998c137339c7ae908b86 Mon Sep 17 00:00:00 2001 From: tidwall Date: Fri, 22 Sep 2023 10:13:56 -0700 Subject: [PATCH] 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 --- gjson.go | 30 ++++++++++++++++++++++-------- gjson_test.go | 13 +++++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/gjson.go b/gjson.go index a1633be..7949825 100644 --- a/gjson.go +++ b/gjson.go @@ -3410,7 +3410,7 @@ func (t Result) Path(json string) string { if !rcomp.Exists() { goto fail } - comp := escapeComp(rcomp.String()) + comp := Escape(rcomp.String()) path = append(path, '.') path = append(path, comp...) } @@ -3425,17 +3425,31 @@ fail: // isSafePathKeyChar returns true if the input character is safe for not // needing escaping. func isSafePathKeyChar(c byte) bool { - return c <= ' ' || c > '~' || c == '_' || c == '-' || c == ':' || - (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c <= ' ' || c > '~' || c == '_' || + c == '-' || c == ':' } -// escapeComp escaped a path compontent, making it safe for generating a -// path for later use. -func escapeComp(comp string) string { +// Escape returns an escaped path component. +// +// 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++ { 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++ { if !isSafePathKeyChar(comp[i]) { ncomp = append(ncomp, '\\') diff --git a/gjson_test.go b/gjson_test.go index aacc891..247688a 100644 --- a/gjson_test.go +++ b/gjson_test.go @@ -2701,3 +2701,16 @@ func TestModDig(t *testing.T) { assert(t, Get(json, "@dig:name").String() == `["melinda","jake"]`) 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() == "") +}