summaryrefslogtreecommitdiff
path: root/src/pkg/json/encode.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/json/encode.go')
-rw-r--r--src/pkg/json/encode.go99
1 files changed, 86 insertions, 13 deletions
diff --git a/src/pkg/json/encode.go b/src/pkg/json/encode.go
index 5d7ce35cb..759b49dbe 100644
--- a/src/pkg/json/encode.go
+++ b/src/pkg/json/encode.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// The json package implements encoding and decoding of JSON objects as
+// defined in RFC 4627.
package json
import (
@@ -11,6 +13,7 @@ import (
"runtime"
"sort"
"strconv"
+ "utf8"
)
// Marshal returns the JSON encoding of v.
@@ -34,6 +37,7 @@ import (
// a member of the object. By default the object's key name is the
// struct field name converted to lower case. If the struct field
// has a tag, that tag will be used as the name instead.
+// Only exported fields will be encoded.
//
// Map values encode as JSON objects.
// The map's key type must be string; the object keys are used directly
@@ -76,6 +80,43 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, os.Error) {
return buf.Bytes(), nil
}
+// MarshalForHTML is like Marshal but applies HTMLEscape to the output.
+func MarshalForHTML(v interface{}) ([]byte, os.Error) {
+ b, err := Marshal(v)
+ if err != nil {
+ return nil, err
+ }
+ var buf bytes.Buffer
+ HTMLEscape(&buf, b)
+ return buf.Bytes(), nil
+}
+
+// HTMLEscape appends to dst the JSON-encoded src with <, >, and &
+// characters inside string literals changed to \u003c, \u003e, \u0026
+// so that the JSON will be safe to embed inside HTML <script> tags.
+// For historical reasons, web browsers don't honor standard HTML
+// escaping within <script> tags, so an alternative JSON encoding must
+// be used.
+func HTMLEscape(dst *bytes.Buffer, src []byte) {
+ // < > & can only appear in string literals,
+ // so just scan the string one byte at a time.
+ start := 0
+ for i, c := range src {
+ if c == '<' || c == '>' || c == '&' {
+ if start < i {
+ dst.Write(src[start:i])
+ }
+ dst.WriteString(`\u00`)
+ dst.WriteByte(hex[c>>4])
+ dst.WriteByte(hex[c&0xF])
+ start = i + 1
+ }
+ }
+ if start < len(src) {
+ dst.Write(src[start:])
+ }
+}
+
// Marshaler is the interface implemented by objects that
// can marshal themselves into valid JSON.
type Marshaler interface {
@@ -90,6 +131,14 @@ func (e *UnsupportedTypeError) String() string {
return "json: unsupported type: " + e.Type.String()
}
+type InvalidUTF8Error struct {
+ S string
+}
+
+func (e *InvalidUTF8Error) String() string {
+ return "json: invalid UTF-8 in string: " + strconv.Quote(e.S)
+}
+
type MarshalerError struct {
Type reflect.Type
Error os.Error
@@ -171,11 +220,17 @@ func (e *encodeState) reflectValue(v reflect.Value) {
e.WriteByte('{')
t := v.Type().(*reflect.StructType)
n := v.NumField()
+ first := true
for i := 0; i < n; i++ {
- if i > 0 {
+ f := t.Field(i)
+ if f.PkgPath != "" {
+ continue
+ }
+ if first {
+ first = false
+ } else {
e.WriteByte(',')
}
- f := t.Field(i)
if f.Tag != "" {
e.string(f.Tag)
} else {
@@ -242,18 +297,36 @@ func (sv stringValues) get(i int) string { return sv[i].(*reflect.StringValue)
func (e *encodeState) string(s string) {
e.WriteByte('"')
- for _, c := range s {
- switch {
- case c < 0x20:
- e.WriteString(`\u00`)
- e.WriteByte(hex[c>>4])
- e.WriteByte(hex[c&0xF])
- case c == '\\' || c == '"':
- e.WriteByte('\\')
- fallthrough
- default:
- e.WriteRune(c)
+ start := 0
+ for i := 0; i < len(s); {
+ if b := s[i]; b < utf8.RuneSelf {
+ if 0x20 <= b && b != '\\' && b != '"' {
+ i++
+ continue
+ }
+ if start < i {
+ e.WriteString(s[start:i])
+ }
+ if b == '\\' || b == '"' {
+ e.WriteByte('\\')
+ e.WriteByte(b)
+ } else {
+ e.WriteString(`\u00`)
+ e.WriteByte(hex[b>>4])
+ e.WriteByte(hex[b&0xF])
+ }
+ i++
+ start = i
+ continue
+ }
+ c, size := utf8.DecodeRuneInString(s[i:])
+ if c == utf8.RuneError && size == 1 {
+ e.error(&InvalidUTF8Error{s})
}
+ i += size
+ }
+ if start < len(s) {
+ e.WriteString(s[start:])
}
e.WriteByte('"')
}