Commit 9d930040 authored by Andrew Gerrand's avatar Andrew Gerrand

json: add MarshalIndent (accepts user-specified indent string)

Fixes #661

R=r, rsc, skorobo
CC=golang-dev
https://golang.org/cl/576042
parent e153d3d8
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
package json package json
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"os" "os"
...@@ -318,131 +319,160 @@ func (e *MarshalError) String() string { ...@@ -318,131 +319,160 @@ func (e *MarshalError) String() string {
return "json cannot encode value of type " + e.T.String() return "json cannot encode value of type " + e.T.String()
} }
func writeArrayOrSlice(w io.Writer, val reflect.ArrayOrSliceValue) (err os.Error) { type writeState struct {
if _, err = fmt.Fprint(w, "["); err != nil { bytes.Buffer
return indent string
newlines bool
depth int
}
func (s *writeState) descend(bra byte) {
s.depth++
s.WriteByte(bra)
}
func (s *writeState) ascend(ket byte) {
s.depth--
s.writeIndent()
s.WriteByte(ket)
}
func (s *writeState) writeIndent() {
if s.newlines {
s.WriteByte('\n')
}
for i := 0; i < s.depth; i++ {
s.WriteString(s.indent)
} }
}
func (s *writeState) writeArrayOrSlice(val reflect.ArrayOrSliceValue) (err os.Error) {
s.descend('[')
for i := 0; i < val.Len(); i++ { for i := 0; i < val.Len(); i++ {
if err = writeValue(w, val.Elem(i)); err != nil { s.writeIndent()
if err = s.writeValue(val.Elem(i)); err != nil {
return return
} }
if i < val.Len()-1 { if i < val.Len()-1 {
if _, err = fmt.Fprint(w, ","); err != nil { s.WriteByte(',')
return
}
} }
} }
_, err = fmt.Fprint(w, "]") s.ascend(']')
return return
} }
func writeMap(w io.Writer, val *reflect.MapValue) (err os.Error) { func (s *writeState) writeMap(val *reflect.MapValue) (err os.Error) {
key := val.Type().(*reflect.MapType).Key() key := val.Type().(*reflect.MapType).Key()
if _, ok := key.(*reflect.StringType); !ok { if _, ok := key.(*reflect.StringType); !ok {
return &MarshalError{val.Type()} return &MarshalError{val.Type()}
} }
keys := val.Keys() s.descend('{')
if _, err = fmt.Fprint(w, "{"); err != nil {
return
}
keys := val.Keys()
for i := 0; i < len(keys); i++ { for i := 0; i < len(keys); i++ {
if _, err = fmt.Fprintf(w, "%s:", Quote(keys[i].(*reflect.StringValue).Get())); err != nil { s.writeIndent()
return
} fmt.Fprintf(s, "%s:", Quote(keys[i].(*reflect.StringValue).Get()))
if err = writeValue(w, val.Elem(keys[i])); err != nil { if err = s.writeValue(val.Elem(keys[i])); err != nil {
return return
} }
if i < len(keys)-1 { if i < len(keys)-1 {
if _, err = fmt.Fprint(w, ","); err != nil { s.WriteByte(',')
return
}
} }
} }
_, err = fmt.Fprint(w, "}") s.ascend('}')
return return
} }
func writeStruct(w io.Writer, val *reflect.StructValue) (err os.Error) { func (s *writeState) writeStruct(val *reflect.StructValue) (err os.Error) {
if _, err = fmt.Fprint(w, "{"); err != nil { s.descend('{')
return
}
typ := val.Type().(*reflect.StructType) typ := val.Type().(*reflect.StructType)
for i := 0; i < val.NumField(); i++ { for i := 0; i < val.NumField(); i++ {
s.writeIndent()
fieldValue := val.Field(i) fieldValue := val.Field(i)
if _, err = fmt.Fprintf(w, "%s:", Quote(typ.Field(i).Name)); err != nil { fmt.Fprintf(s, "%s:", Quote(typ.Field(i).Name))
return if err = s.writeValue(fieldValue); err != nil {
}
if err = writeValue(w, fieldValue); err != nil {
return return
} }
if i < val.NumField()-1 { if i < val.NumField()-1 {
if _, err = fmt.Fprint(w, ","); err != nil { s.WriteByte(',')
return
}
} }
} }
_, err = fmt.Fprint(w, "}") s.ascend('}')
return return
} }
func writeValue(w io.Writer, val reflect.Value) (err os.Error) { func (s *writeState) writeValue(val reflect.Value) (err os.Error) {
if val == nil { if val == nil {
_, err = fmt.Fprint(w, "null") fmt.Fprint(s, "null")
return return
} }
switch v := val.(type) { switch v := val.(type) {
case *reflect.StringValue: case *reflect.StringValue:
_, err = fmt.Fprint(w, Quote(v.Get())) fmt.Fprint(s, Quote(v.Get()))
case *reflect.ArrayValue: case *reflect.ArrayValue:
err = writeArrayOrSlice(w, v) err = s.writeArrayOrSlice(v)
case *reflect.SliceValue: case *reflect.SliceValue:
err = writeArrayOrSlice(w, v) err = s.writeArrayOrSlice(v)
case *reflect.MapValue: case *reflect.MapValue:
err = writeMap(w, v) err = s.writeMap(v)
case *reflect.StructValue: case *reflect.StructValue:
err = writeStruct(w, v) err = s.writeStruct(v)
case *reflect.ChanValue, case *reflect.ChanValue,
*reflect.UnsafePointerValue, *reflect.UnsafePointerValue,
*reflect.FuncValue: *reflect.FuncValue:
err = &MarshalError{val.Type()} err = &MarshalError{val.Type()}
case *reflect.InterfaceValue: case *reflect.InterfaceValue:
if v.IsNil() { if v.IsNil() {
_, err = fmt.Fprint(w, "null") fmt.Fprint(s, "null")
} else { } else {
err = writeValue(w, v.Elem()) err = s.writeValue(v.Elem())
} }
case *reflect.PtrValue: case *reflect.PtrValue:
if v.IsNil() { if v.IsNil() {
_, err = fmt.Fprint(w, "null") fmt.Fprint(s, "null")
} else { } else {
err = writeValue(w, v.Elem()) err = s.writeValue(v.Elem())
} }
case *reflect.UintptrValue: case *reflect.UintptrValue:
_, err = fmt.Fprintf(w, "%d", v.Get()) fmt.Fprintf(s, "%d", v.Get())
case *reflect.Uint64Value: case *reflect.Uint64Value:
_, err = fmt.Fprintf(w, "%d", v.Get()) fmt.Fprintf(s, "%d", v.Get())
case *reflect.Uint32Value: case *reflect.Uint32Value:
_, err = fmt.Fprintf(w, "%d", v.Get()) fmt.Fprintf(s, "%d", v.Get())
case *reflect.Uint16Value: case *reflect.Uint16Value:
_, err = fmt.Fprintf(w, "%d", v.Get()) fmt.Fprintf(s, "%d", v.Get())
case *reflect.Uint8Value: case *reflect.Uint8Value:
_, err = fmt.Fprintf(w, "%d", v.Get()) fmt.Fprintf(s, "%d", v.Get())
default: default:
value := val.(reflect.Value) value := val.(reflect.Value)
_, err = fmt.Fprintf(w, "%#v", value.Interface()) fmt.Fprintf(s, "%#v", value.Interface())
}
return
}
func (s *writeState) marshal(w io.Writer, val interface{}) (err os.Error) {
err = s.writeValue(reflect.NewValue(val))
if err != nil {
return
}
if s.newlines {
s.WriteByte('\n')
} }
_, err = s.WriteTo(w)
return return
} }
...@@ -451,5 +481,16 @@ func writeValue(w io.Writer, val reflect.Value) (err os.Error) { ...@@ -451,5 +481,16 @@ func writeValue(w io.Writer, val reflect.Value) (err os.Error) {
// Due to limitations in JSON, val cannot include cyclic data // Due to limitations in JSON, val cannot include cyclic data
// structures, channels, functions, or maps. // structures, channels, functions, or maps.
func Marshal(w io.Writer, val interface{}) os.Error { func Marshal(w io.Writer, val interface{}) os.Error {
return writeValue(w, reflect.NewValue(val)) s := &writeState{indent: "", newlines: false, depth: 0}
return s.marshal(w, val)
}
// MarshalIndent writes the JSON encoding of val to w,
// indenting nested values using the indent string.
//
// Due to limitations in JSON, val cannot include cyclic data
// structures, channels, functions, or maps.
func MarshalIndent(w io.Writer, val interface{}, indent string) os.Error {
s := &writeState{indent: indent, newlines: true, depth: 0}
return s.marshal(w, val)
} }
...@@ -246,7 +246,98 @@ func TestMarshal(t *testing.T) { ...@@ -246,7 +246,98 @@ func TestMarshal(t *testing.T) {
s := buf.String() s := buf.String()
if s != tt.out { if s != tt.out {
t.Errorf("Marshal(%T) = %q, want %q\n", tt.val, tt.out, s) t.Errorf("Marshal(%T) = %q, want %q\n", tt.val, s, tt.out)
}
}
}
type marshalIndentTest struct {
val interface{}
indent string
out string
}
const marshalIndentTest1 = `[
1,
2,
3,
4
]
`
const marshalIndentTest2 = `[
[
1,
2
],
[
3,
4
]
]
`
const marshalIndentTest3 = `[
[
1,
2
],
[
3,
4
]
]
`
const marshalIndentTest4 = `[
[
1,
2
],
[
3,
4
]
]
`
const marshalIndentTest5 = `{
"a":1,
"b":"hello"
}
`
const marshalIndentTest6 = `{
"3":[
1,
2,
3
]
}
`
var marshalIndentTests = []marshalIndentTest{
marshalIndentTest{[]int{1, 2, 3, 4}, " ", marshalIndentTest1},
marshalIndentTest{[][]int{[]int{1, 2}, []int{3, 4}}, "", marshalIndentTest2},
marshalIndentTest{[][]int{[]int{1, 2}, []int{3, 4}}, " ", marshalIndentTest3},
marshalIndentTest{[][]int{[]int{1, 2}, []int{3, 4}}, " ", marshalIndentTest4},
marshalIndentTest{struct {
a int
b string
}{1, "hello"},
" ",
marshalIndentTest5,
},
marshalIndentTest{map[string][]int{"3": []int{1, 2, 3}}, " ", marshalIndentTest6},
}
func TestMarshalIndent(t *testing.T) {
for _, tt := range marshalIndentTests {
var buf bytes.Buffer
err := MarshalIndent(&buf, tt.val, tt.indent)
if err != nil {
t.Fatalf("MarshalIndent(%v): %s", tt.val, err)
}
s := buf.String()
if s != tt.out {
t.Errorf("MarshalIndent(%v) = %q, want %q\n", tt.val, s, tt.out)
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment