]> Cypherpunks repositories - gostls13.git/commitdiff
Add json.Marshal to json package
authorMichael Hoisie <hoisie@gmail.com>
Fri, 20 Nov 2009 04:45:03 +0000 (20:45 -0800)
committerRuss Cox <rsc@golang.org>
Fri, 20 Nov 2009 04:45:03 +0000 (20:45 -0800)
R=rsc
CC=golang-dev
https://golang.org/cl/157068

src/pkg/json/struct.go
src/pkg/json/struct_test.go

index 4e560ec866f354373fd2b25a189c83c09a8896aa..6b74cdae0be733b53cc945889b6dd7cbbf28daa8 100644 (file)
@@ -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))
+}
index 89d363d9ee15e749eb7bce8dd58d36d710b85d44..b71c31a85796b61e30bcee8239ae2aab68251911 100644 (file)
@@ -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)
+               }
+       }
+}