Skip to content

Commit 496d327

Browse files
authored
Expose Unescape + AppendUnescape helper functions (#62)
* Expose Unescape + AppendUnescape helper functions * type change input param * testUnescape * append to buf and test * benchmark
1 parent e86bebd commit 496d327

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

json/json.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,25 @@ func AppendEscape(b []byte, s string, flags AppendFlags) []byte {
161161
return b
162162
}
163163

164+
// Unescape is a convenience helper to unescape a JSON value.
165+
// For more control over the unescape behavior and
166+
// to write to a pre-allocated buffer, use AppendUnescape.
167+
func Unescape(s []byte) []byte {
168+
b := make([]byte, 0, len(s))
169+
return AppendUnescape(b, s, ParseFlags(0))
170+
}
171+
172+
// AppendUnescape appends s to b with the string unescaped as a JSON value.
173+
// This will remove starting and ending quote characters, and the
174+
// appropriate characters will be escaped correctly as if JSON decoded.
175+
// New space will be reallocated if more space is needed.
176+
func AppendUnescape(b []byte, s []byte, flags ParseFlags) []byte {
177+
d := decoder{flags: flags}
178+
buf := new(string)
179+
d.decodeString(s, unsafe.Pointer(buf))
180+
return append(b, *buf...)
181+
}
182+
164183
// Compact is documented at https://golang.org/pkg/encoding/json/#Compact
165184
func Compact(dst *bytes.Buffer, src []byte) error {
166185
return json.Compact(dst, src)

json/json_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,3 +1702,78 @@ func TestAppendEscape(t *testing.T) {
17021702
}
17031703
})
17041704
}
1705+
1706+
func TestUnescapeString(t *testing.T) {
1707+
b := Unescape([]byte(`"value"`))
1708+
x := []byte(`value`)
1709+
1710+
if !bytes.Equal(x, b) {
1711+
t.Error(
1712+
"unexpected decoding:",
1713+
"expected", string(x),
1714+
"got", string(b),
1715+
)
1716+
}
1717+
}
1718+
1719+
func TestAppendUnescape(t *testing.T) {
1720+
t.Run("basic", func(t *testing.T) {
1721+
out := AppendUnescape([]byte{}, []byte(`"value"`), ParseFlags(0))
1722+
exp := []byte("value")
1723+
if bytes.Compare(exp, out) != 0 {
1724+
t.Error(
1725+
"unexpected decoding:",
1726+
"expected", exp,
1727+
"got", out,
1728+
)
1729+
}
1730+
})
1731+
1732+
t.Run("escaped", func(t *testing.T) {
1733+
b := AppendUnescape([]byte{}, []byte(`"\"escaped\"\t\u003cvalue\u003e"`), ParseFlags(0))
1734+
exp := []byte(`"escaped" <value>`)
1735+
if bytes.Compare(exp, b) != 0 {
1736+
t.Error(
1737+
"unexpected encoding:",
1738+
"expected", exp,
1739+
"got", b,
1740+
)
1741+
}
1742+
})
1743+
1744+
t.Run("build", func(t *testing.T) {
1745+
b := []byte{}
1746+
b = append(b, []byte(`{"key":`)...)
1747+
b = AppendUnescape(b, []byte(`"\"escaped\"\t\u003cvalue\u003e"`), ParseFlags(0))
1748+
b = append(b, '}')
1749+
exp := []byte(`{"key":"escaped" <value>}`)
1750+
if bytes.Compare(exp, b) != 0 {
1751+
t.Error(
1752+
"unexpected encoding:",
1753+
"expected", string(exp),
1754+
"got", string(b),
1755+
)
1756+
}
1757+
})
1758+
}
1759+
1760+
func BenchmarkUnescape(b *testing.B) {
1761+
s := []byte(`"\"escaped\"\t\u003cvalue\u003e"`)
1762+
out := []byte{}
1763+
for i := 0; i < b.N; i++ {
1764+
out = Unescape(s)
1765+
}
1766+
1767+
b.Log(string(out))
1768+
}
1769+
1770+
func BenchmarkUnmarshalField(b *testing.B) {
1771+
s := []byte(`"\"escaped\"\t\u003cvalue\u003e"`)
1772+
var v string
1773+
1774+
for i := 0; i < b.N; i++ {
1775+
json.Unmarshal(s, &v)
1776+
}
1777+
1778+
b.Log(v)
1779+
}

0 commit comments

Comments
 (0)