Repository: segmentio/encoding
Pull request #60 (2021-01-13 19:22:02)

add json.Escape helper
@kalamay following up on your PR #59 

I'm suggesting we add a convenience helper to make it simpler to escape strings when we care less about performance and just need the feature.

After thinking about naming a bit more, I would also like to rename `AppendEscaped` to `AppendEscape`, as it would be more consistent with `Escape` and other similar functions in the standard library:
* [json.HTMLEscape](https://golang.org/pkg/encoding/json/#HTMLEscape)
* [url.QuoteEscape](https://golang.org/pkg/net/url/#QueryEscape)


achille-roussel: Thanks for the quick review!

Patch:
diff --git a/json/json.go b/json/json.go
index 294b724..7e0515f 100644
--- a/json/json.go
+++ b/json/json.go
@@ -142,10 +142,20 @@ func Append(b []byte, x interface{}, flags AppendFlags) ([]byte, error) {
 	return b, err
 }
 
-// AppendEscaped appends s to b with the string escaped as a JSON value.
+// Escape is a convenience helper to construct an escaped JSON string from s.
+// The function escales HTML characters, for more control over the escape
+// behavior and to write to a pre-allocated buffer, use AppendEscape.
+func Escape(s string) []byte {
+	// +10 for extra escape characters, maybe not enough and the buffer will
+	// be reallocated.
+	b := make([]byte, 0, len(s)+10)
+	return AppendEscape(b, s, EscapeHTML)
+}
+
+// AppendEscape appends s to b with the string escaped as a JSON value.
 // This will include the starting and ending quote characters, and the
 // appropriate characters will be escaped correctly for JSON encoding.
-func AppendEscaped(b []byte, s string, flags AppendFlags) []byte {
+func AppendEscape(b []byte, s string, flags AppendFlags) []byte {
 	e := encoder{flags: flags}
 	b, _ = e.encodeString(b, unsafe.Pointer(&s))
 	return b


Test patch:
diff --git a/json/json_test.go b/json/json_test.go
index 9b06337..1811819 100644
--- a/json/json_test.go
+++ b/json/json_test.go
@@ -1647,9 +1647,22 @@ func TestSetTrustRawMessage(t *testing.T) {
 	}
 }
 
-func TestAppendEscaped(t *testing.T) {
+func TestEscapeString(t *testing.T) {
+	b := Escape(`value`)
+	x := []byte(`"value"`)
+
+	if !bytes.Equal(x, b) {
+		t.Error(
+			"unexpected encoding:",
+			"expected", string(x),
+			"got", string(b),
+		)
+	}
+}
+
+func TestAppendEscape(t *testing.T) {
 	t.Run("basic", func(t *testing.T) {
-		b := AppendEscaped([]byte{}, `value`, AppendFlags(0))
+		b := AppendEscape([]byte{}, `value`, AppendFlags(0))
 		exp := []byte(`"value"`)
 		if bytes.Compare(exp, b) != 0 {
 			t.Error(
@@ -1661,7 +1674,7 @@ func TestAppendEscaped(t *testing.T) {
 	})
 
 	t.Run("escaped", func(t *testing.T) {
-		b := AppendEscaped([]byte{}, `"escaped"	<value>`, EscapeHTML)
+		b := AppendEscape([]byte{}, `"escaped"	<value>`, EscapeHTML)
 		exp := []byte(`"\"escaped\"\t\u003cvalue\u003e"`)
 		if bytes.Compare(exp, b) != 0 {
 			t.Error(
@@ -1675,9 +1688,9 @@ func TestAppendEscaped(t *testing.T) {
 	t.Run("build", func(t *testing.T) {
 		b := []byte{}
 		b = append(b, '{')
-		b = AppendEscaped(b, `key`, EscapeHTML)
+		b = AppendEscape(b, `key`, EscapeHTML)
 		b = append(b, ':')
-		b = AppendEscaped(b, `"escaped"	<value>`, EscapeHTML)
+		b = AppendEscape(b, `"escaped"	<value>`, EscapeHTML)
 		b = append(b, '}')
 		exp := []byte(`{"key":"\"escaped\"\t\u003cvalue\u003e"}`)
 		if bytes.Compare(exp, b) != 0 {