Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add json ASCII string render #1358

Merged
merged 13 commits into from
Jul 3, 2018
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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": "<br>",
}

// will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent

c.AsciiJSON(http.StatusOK, data)
})

// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
```

### Serving static files

```go
Expand Down
6 changes: 6 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}) {
Expand Down
11 changes: 11 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
32 changes: 32 additions & 0 deletions render/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package render

import (
"bytes"
"fmt"
"html/template"
"net/http"

Expand All @@ -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"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add charset-utf-8?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, needn't. it's ascii.


func (r JSON) Render(w http.ResponseWriter) (err error) {
if err = WriteJSON(w, r.Data); err != nil {
Expand Down Expand Up @@ -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)
}
29 changes: 29 additions & 0 deletions render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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": "<br>",
}

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
Expand Down