diff --git a/pkg/vm/stackitem/json.go b/pkg/vm/stackitem/json.go index 610fe17c3..8b1072d12 100644 --- a/pkg/vm/stackitem/json.go +++ b/pkg/vm/stackitem/json.go @@ -66,16 +66,8 @@ func toJSON(buf *io.BufBinWriter, item Item) { for i := range it.value { // map key can always be converted to []byte // but are not always a valid UTF-8. - key, err := ToString(it.value[i].Key) - if err != nil { - if buf.Err == nil { - buf.Err = err - } - return - } - w.WriteB('"') - w.WriteBytes([]byte(key)) - w.WriteBytes([]byte(`":`)) + writeJSONString(buf.BinWriter, it.value[i].Key) + w.WriteBytes([]byte(`:`)) toJSON(buf, it.value[i].Value) if i < len(it.value)-1 { w.WriteB(',') @@ -89,16 +81,7 @@ func toJSON(buf *io.BufBinWriter, item Item) { } w.WriteBytes([]byte(it.value.String())) case *ByteArray, *Buffer: - w.WriteB('"') - s, err := ToString(it) - if err != nil { - if buf.Err == nil { - buf.Err = err - } - return - } - w.WriteBytes([]byte(s)) - w.WriteB('"') + writeJSONString(w, it) case *Bool: if it.value { w.WriteBytes([]byte("true")) @@ -116,6 +99,21 @@ func toJSON(buf *io.BufBinWriter, item Item) { } } +// writeJSONString converts it to string and writes it to w as JSON value +// surrounded in quotes with control characters escaped. +func writeJSONString(w *io.BinWriter, it Item) { + if w.Err != nil { + return + } + s, err := ToString(it) + if err != nil { + w.Err = err + return + } + data, _ := json.Marshal(s) // error never occurs because `ToString` checks for validity + w.WriteBytes(data) +} + // FromJSON decodes Item from JSON. // It behaves as following: // string -> ByteArray from base64 diff --git a/pkg/vm/stackitem/json_test.go b/pkg/vm/stackitem/json_test.go index fb6fcc672..9536d403e 100644 --- a/pkg/vm/stackitem/json_test.go +++ b/pkg/vm/stackitem/json_test.go @@ -30,6 +30,7 @@ func TestFromToJSON(t *testing.T) { t.Run("ByteString", func(t *testing.T) { t.Run("Empty", getTestDecodeFunc(`""`, []byte{})) t.Run("Base64", getTestDecodeFunc(`"test"`, "test")) + t.Run("Escape", getTestDecodeFunc(`"\"quotes\""`, `"quotes"`)) }) t.Run("BigInteger", func(t *testing.T) { t.Run("ZeroFloat", getTestDecodeFunc(`12.000`, 12, nil)) @@ -58,6 +59,10 @@ func TestFromToJSON(t *testing.T) { t.Run("Empty", getTestDecodeFunc(`{}`, NewMap())) t.Run("Small", getTestDecodeFunc(`{"a":3}`, small)) t.Run("Big", getTestDecodeFunc(`{"3":{"a":3},"arr":["test"]}`, large)) + + m := NewMap() + m.Add(NewByteArray([]byte("\t")), NewBool(true)) + t.Run("escape keys", getTestDecodeFunc(`{"\t":true}`, m)) }) t.Run("Invalid", func(t *testing.T) { t.Run("Empty", getTestDecodeFunc(``, nil))