diff --git a/src/pkg/json/struct.go b/src/pkg/json/struct.go index 4e560ec866f..6b74cdae0be 100644 --- a/src/pkg/json/struct.go +++ b/src/pkg/json/struct.go @@ -8,6 +8,9 @@ package json import ( + "fmt"; + "io"; + "os"; "reflect"; "strings"; ) @@ -306,3 +309,97 @@ func Unmarshal(s string, val interface{}) (ok bool, errtok string) { } return true, ""; } + +type MarshalError struct { + T reflect.Type; +} + +func (e *MarshalError) String() string { + return "json cannot encode value of type " + e.T.String() +} +func writeArrayOrSlice(w io.Writer, val reflect.ArrayOrSliceValue) os.Error { + fmt.Fprint(w, "["); + + for i := 0; i < val.Len(); i++ { + if err := writeValue(w, val.Elem(i)); err != nil { + return err + } + + if i < val.Len()-1 { + fmt.Fprint(w, ",") + } + } + + fmt.Fprint(w, "]"); + return nil; +} + +func writeMap(w io.Writer, val *reflect.MapValue) os.Error { + key := val.Type().(*reflect.MapType).Key(); + if _, ok := key.(*reflect.StringType); !ok { + return &MarshalError{val.Type()} + } + + keys := val.Keys(); + fmt.Fprint(w, "{"); + for i := 0; i < len(keys); i++ { + fmt.Fprintf(w, "%q:", keys[i].(*reflect.StringValue).Get()); + + if err := writeValue(w, val.Elem(keys[i])); err != nil { + return err + } + + if i < len(keys)-1 { + fmt.Fprint(w, ",") + } + } + + fmt.Fprint(w, "}"); + return nil; +} + +func writeStruct(w io.Writer, val *reflect.StructValue) os.Error { + fmt.Fprint(w, "{"); + + typ := val.Type().(*reflect.StructType); + + for i := 0; i < val.NumField(); i++ { + fieldValue := val.Field(i); + fmt.Fprintf(w, "%q:", typ.Field(i).Name); + writeValue(w, fieldValue); + if i < val.NumField()-1 { + fmt.Fprint(w, ",") + } + } + + fmt.Fprint(w, "}"); + return nil; +} + +func writeValue(w io.Writer, val reflect.Value) (err os.Error) { + switch v := val.(type) { + case *reflect.StringValue: + fmt.Fprintf(w, "%q", v.Get()) + case *reflect.ArrayValue: + err = writeArrayOrSlice(w, v) + case *reflect.SliceValue: + err = writeArrayOrSlice(w, v) + case *reflect.MapValue: + err = writeMap(w, v) + case *reflect.StructValue: + err = writeStruct(w, v) + case *reflect.ChanValue, + *reflect.InterfaceValue, + *reflect.PtrValue, + *reflect.UnsafePointerValue: + return &MarshalError{val.Type()} + default: + value := val.(reflect.Value); + fmt.Fprint(w, value.Interface()); + } + return nil; +} + +func Marshal(w io.Writer, val interface{}) os.Error { + return writeValue(w, reflect.NewValue(val)) +} diff --git a/src/pkg/json/struct_test.go b/src/pkg/json/struct_test.go index 89d363d9ee1..b71c31a8579 100644 --- a/src/pkg/json/struct_test.go +++ b/src/pkg/json/struct_test.go @@ -5,6 +5,7 @@ package json import ( + "bytes"; "reflect"; "strconv"; "testing"; @@ -157,3 +158,48 @@ func TestIssue114(t *testing.T) { } } } + +type marshalTest struct { + val interface{}; + out string; +} + +var marshalTests = []marshalTest{ + // basic string + marshalTest{true, "true"}, + marshalTest{false, "false"}, + marshalTest{123, "123"}, + marshalTest{0.1, "0.1"}, + marshalTest{1e-10, "1e-10"}, + marshalTest{"teststring", `"teststring"`}, + marshalTest{[4]int{1, 2, 3, 4}, "[1,2,3,4]"}, + marshalTest{[]int{1, 2, 3, 4}, "[1,2,3,4]"}, + marshalTest{[][]int{[]int{1, 2}, []int{3, 4}}, "[[1,2],[3,4]]"}, + marshalTest{map[string]string{"one": "one"}, `{"one":"one"}`}, + marshalTest{map[string]int{"one": 1}, `{"one":1}`}, + marshalTest{struct{}{}, "{}"}, + marshalTest{struct{ a int }{1}, `{"a":1}`}, + marshalTest{struct { + a int; + b string; + }{1, "hello"}, + `{"a":1,"b":"hello"}`, + }, + marshalTest{map[string][]int{"3": []int{1, 2, 3}}, `{"3":[1,2,3]}`}, +} + +func TestJsonMarshal(t *testing.T) { + for _, tt := range marshalTests { + var buf bytes.Buffer; + + err := Marshal(&buf, tt.val); + if err != nil { + t.Errorf("Error converting %s to JSON: \n", err.String()) + } + + s := buf.String(); + if s != tt.out { + t.Errorf("Error converting to JSON. Expected: %q Actual %q\n", tt.out, s) + } + } +}