diff --git a/README.md b/README.md
index e09a51f2..93896469 100644
--- a/README.md
+++ b/README.md
@@ -900,6 +900,29 @@ func main() {
}
```
+#### AsciiJSON
+
+Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII chracters.
+
+```go
+func main() {
+ r := gin.Default()
+
+ r.GET("/someJSON", func(c *gin.Context) {
+ data := map[string]interface{}{
+ "lang": "GO语言",
+ "tag": "
",
+ }
+
+ // will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
+ c.AsciiJSON(http.StatusOK, data)
+ })
+
+ // Listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+}
+```
+
### Serving static files
```go
diff --git a/context.go b/context.go
index 6fc5d25f..d0b4e87f 100644
--- a/context.go
+++ b/context.go
@@ -704,6 +704,12 @@ func (c *Context) JSON(code int, obj interface{}) {
c.Render(code, render.JSON{Data: obj})
}
+// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
+// It also sets the Content-Type as "application/json".
+func (c *Context) AsciiJSON(code int, obj interface{}) {
+ c.Render(code, render.AsciiJSON{Data: obj})
+}
+
// XML serializes the given struct as XML into the response body.
// It also sets the Content-Type as "application/xml".
func (c *Context) XML(code int, obj interface{}) {
diff --git a/context_test.go b/context_test.go
index 12e02fa0..6f37682e 100644
--- a/context_test.go
+++ b/context_test.go
@@ -686,6 +686,17 @@ func TestContextRenderNoContentSecureJSON(t *testing.T) {
assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type"))
}
+func TestContextRenderNoContentAsciiJSON(t *testing.T) {
+ w := httptest.NewRecorder()
+ c, _ := CreateTestContext(w)
+
+ c.AsciiJSON(http.StatusNoContent, []string{"lang", "Go语言"})
+
+ assert.Equal(t, http.StatusNoContent, w.Code)
+ assert.Empty(t, w.Body.String())
+ assert.Equal(t, "application/json", w.HeaderMap.Get("Content-Type"))
+}
+
// Tests that the response executes the templates
// and responds with Content-Type set to text/html
func TestContextRenderHTML(t *testing.T) {
diff --git a/render/json.go b/render/json.go
index 3a2e8b2f..6e5089a0 100755
--- a/render/json.go
+++ b/render/json.go
@@ -6,6 +6,7 @@ package render
import (
"bytes"
+ "fmt"
"html/template"
"net/http"
@@ -30,10 +31,15 @@ type JsonpJSON struct {
Data interface{}
}
+type AsciiJSON struct {
+ Data interface{}
+}
+
type SecureJSONPrefix string
var jsonContentType = []string{"application/json; charset=utf-8"}
var jsonpContentType = []string{"application/javascript; charset=utf-8"}
+var jsonAsciiContentType = []string{"application/json"}
func (r JSON) Render(w http.ResponseWriter) (err error) {
if err = WriteJSON(w, r.Data); err != nil {
@@ -112,3 +118,29 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonpContentType)
}
+
+func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
+ r.WriteContentType(w)
+ ret, err := json.Marshal(r.Data)
+ if err != nil {
+ return err
+ }
+
+ var buffer bytes.Buffer
+ for _, r := range string(ret) {
+ cvt := ""
+ if r < 128 {
+ cvt = string(r)
+ } else {
+ cvt = fmt.Sprintf("\\u%04x", int64(r))
+ }
+ buffer.WriteString(cvt)
+ }
+
+ w.Write(buffer.Bytes())
+ return nil
+}
+
+func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, jsonAsciiContentType)
+}
diff --git a/render/render_test.go b/render/render_test.go
index 40ec806e..2f728441 100755
--- a/render/render_test.go
+++ b/render/render_test.go
@@ -167,6 +167,35 @@ func TestRenderJsonpJSONFail(t *testing.T) {
assert.Error(t, err)
}
+func TestRenderAsciiJSON(t *testing.T) {
+ w1 := httptest.NewRecorder()
+ data1 := map[string]interface{}{
+ "lang": "GO语言",
+ "tag": "
",
+ }
+
+ err := (AsciiJSON{data1}).Render(w1)
+
+ assert.NoError(t, err)
+ assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
+ assert.Equal(t, "application/json", w1.Header().Get("Content-Type"))
+
+ w2 := httptest.NewRecorder()
+ data2 := float64(3.1415926)
+
+ err = (AsciiJSON{data2}).Render(w2)
+ assert.NoError(t, err)
+ assert.Equal(t, "3.1415926", w2.Body.String())
+}
+
+func TestRenderAsciiJSONFail(t *testing.T) {
+ w := httptest.NewRecorder()
+ data := make(chan int)
+
+ // json: unsupported type: chan int
+ assert.Error(t, (AsciiJSON{data}).Render(w))
+}
+
type xmlmap map[string]interface{}
// Allows type H to be used with xml.Marshal