fix: allow setting and deleting an empty object key
An empty string is a valid JSON object key and gjson reads it, but set()
rejected an empty path outright, so such a key could neither be changed
nor removed through sjson.
Nothing below that guard needed a non-empty path. parsePath("") already
returns a well-formed simple pathResult and gjson.Get resolves the empty
key. What did break was atoui: its digit loop never runs for an empty
part, so it fell through and reported the empty key as numeric index 0.
That also affected paths the guard never blocked. A nested empty key was
built as an array, so Set(`{"x":1}`, "a.", 2) produced
{"x":1,"a":[2]}, which gjson cannot read back.
Return false from atoui for an empty part and drop the guard. An empty
key on an array now reports the same error as any other non-numeric key
instead of a special-cased one.
Fixes #77
This commit is contained in:
1 parent
23241c5ce0
commit
5b70965fcb
2 files changed
+48
-3
No files matched your search
@@ -166,6 +166,10 @@ func atoui(r pathResult) (n int, ok bool) {
|
|||||||
if r.force {
|
if r.force {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
if len(r.part) == 0 {
|
||||||
|
// An empty key is a valid object key, not the number zero.
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
for i := 0; i < len(r.part); i++ {
|
for i := 0; i < len(r.part); i++ {
|
||||||
if r.part[i] < '0' || r.part[i] > '9' {
|
if r.part[i] < '0' || r.part[i] > '9' {
|
||||||
return 0, false
|
return 0, false
|
||||||
@@ -515,9 +519,6 @@ type sliceHeader struct {
|
|||||||
|
|
||||||
func set(jstr, path, raw string,
|
func set(jstr, path, raw string,
|
||||||
stringify, del, optimistic, inplace bool) ([]byte, error) {
|
stringify, del, optimistic, inplace bool) ([]byte, error) {
|
||||||
if path == "" {
|
|
||||||
return []byte(jstr), &errorType{"path cannot be empty"}
|
|
||||||
}
|
|
||||||
if !del && optimistic && isOptimisticPath(path) {
|
if !del && optimistic && isOptimisticPath(path) {
|
||||||
res := gjson.Get(jstr, path)
|
res := gjson.Get(jstr, path)
|
||||||
if res.Exists() && res.Index > 0 {
|
if res.Exists() && res.Index > 0 {
|
||||||
|
|||||||
@@ -351,3 +351,47 @@ func TestIssue61(t *testing.T) {
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEmptyKeyIssue77(t *testing.T) {
|
||||||
|
const json = `{"name":{"first":"Janet"},"age":47,"":"foo"}`
|
||||||
|
|
||||||
|
// An empty key is a valid object key and gjson reads it, so sjson has to
|
||||||
|
// be able to write and delete it too.
|
||||||
|
if gjson.Get(json, "").String() != "foo" {
|
||||||
|
t.Fatalf("gjson cannot read the empty key")
|
||||||
|
}
|
||||||
|
|
||||||
|
testRaw(t, setString, `{"name":{"first":"Janet"},"age":47,"":"bar"}`,
|
||||||
|
json, "", "bar")
|
||||||
|
testRaw(t, setDelete, `{"name":{"first":"Janet"},"age":47}`,
|
||||||
|
json, "", nil)
|
||||||
|
|
||||||
|
// setting an empty key that is not there yet adds it
|
||||||
|
testRaw(t, setString, `{"a":1,"":"bar"}`, `{"a":1}`, "", "bar")
|
||||||
|
testRaw(t, setString, `{"":"bar"}`, `{}`, "", "bar")
|
||||||
|
|
||||||
|
// deleting an empty key that is not there is a no-op, like any other
|
||||||
|
// missing key
|
||||||
|
testRaw(t, setDelete, `{"a":1}`, `{"a":1}`, "", nil)
|
||||||
|
|
||||||
|
// an empty key is a key, never the array index zero
|
||||||
|
testRaw(t, setInt, `{"x":1,"a":{"":2}}`, `{"x":1}`, "a.", 2)
|
||||||
|
testRaw(t, setInt, `{"a":{"b":{"":2}}}`, `{}`, "a.b.", 2)
|
||||||
|
testRaw(t, setInt, `{"a":{"":{"b":2}}}`, `{}`, "a..b", 2)
|
||||||
|
|
||||||
|
// whatever sjson writes for an empty key, gjson has to read back
|
||||||
|
out, err := Set(`{"x":1}`, "a.", 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if res := gjson.Get(out, "a."); !res.Exists() || res.Int() != 2 {
|
||||||
|
t.Fatalf("expected gjson to read back '2', got '%v' from '%v'",
|
||||||
|
res.Raw, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// an empty key still cannot address an array element, and reports the
|
||||||
|
// same error as any other non-numeric key
|
||||||
|
if _, err := Set(`[1,2,3]`, "", "x"); err == nil {
|
||||||
|
t.Fatalf("expected an error for an empty key on an array")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user