Pārlūkot izejas kodu

Object now implements json.Marshaler

Dmitry Panov 8 gadi atpakaļ
vecāks
revīzija
d9584dc190
3 mainītis faili ar 83 papildinājumiem un 3 dzēšanām
  1. 7 3
      builtin_json.go
  2. 59 0
      builtin_json_test.go
  3. 17 0
      value.go

+ 7 - 3
builtin_json.go

@@ -266,14 +266,18 @@ func (r *Runtime) builtinJSON_stringify(call FunctionCall) Value {
 		}
 	}
 
-	holder := r.NewObject()
-	holder.self.putStr("", call.Argument(0), false)
-	if ctx.str(stringEmpty, holder) {
+	if ctx.do(call.Argument(0)) {
 		return newStringValue(ctx.buf.String())
 	}
 	return _undefined
 }
 
+func (ctx *_builtinJSON_stringifyContext) do(v Value) bool {
+	holder := ctx.r.NewObject()
+	holder.self.putStr("", v, false)
+	return ctx.str(stringEmpty, holder)
+}
+
 func (ctx *_builtinJSON_stringifyContext) str(key Value, holder *Object) bool {
 	value := holder.self.get(key)
 	if value == nil {

+ 59 - 0
builtin_json_test.go

@@ -0,0 +1,59 @@
+package goja
+
+import (
+	"encoding/json"
+	"strings"
+	"testing"
+)
+
+func TestJSONMarshalObject(t *testing.T) {
+	vm := New()
+	o := vm.NewObject()
+	o.Set("test", 42)
+	o.Set("testfunc", vm.Get("Error"))
+	b, err := json.Marshal(o)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if string(b) != `{"test":42}` {
+		t.Fatalf("Unexpected value: %s", b)
+	}
+}
+
+func TestJSONMarshalObjectCircular(t *testing.T) {
+	vm := New()
+	o := vm.NewObject()
+	o.Set("o", o)
+	_, err := json.Marshal(o)
+	if err == nil {
+		t.Fatal("Expected error")
+	}
+	if !strings.HasSuffix(err.Error(), "Converting circular structure to JSON") {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+}
+
+func BenchmarkJSONStringify(b *testing.B) {
+	b.StopTimer()
+	vm := New()
+	var createObj func(level int) *Object
+	createObj = func(level int) *Object {
+		o := vm.NewObject()
+		o.Set("field1", "test")
+		o.Set("field2", 42)
+		if level > 0 {
+			level--
+			o.Set("obj1", createObj(level))
+			o.Set("obj2", createObj(level))
+		}
+		return o
+	}
+
+	o := createObj(3)
+	json := vm.Get("JSON").(*Object)
+	stringify, _ := AssertFunction(json.Get("stringify"))
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		stringify(nil, o)
+	}
+}

+ 17 - 0
value.go

@@ -731,6 +731,23 @@ func (o *Object) Set(name string, value interface{}) (err error) {
 	return
 }
 
+// MarshalJSON returns JSON representation of the Object. It is equivalent to JSON.stringify(o).
+// Note, this implements json.Marshaler so that json.Marshal() can be used without the need to Export().
+func (o *Object) MarshalJSON() ([]byte, error) {
+	ctx := _builtinJSON_stringifyContext{
+		r: o.runtime,
+	}
+	ex := o.runtime.vm.try(func() {
+		if !ctx.do(o) {
+			ctx.buf.WriteString("null")
+		}
+	})
+	if ex != nil {
+		return nil, ex
+	}
+	return ctx.buf.Bytes(), nil
+}
+
 func (o valueUnresolved) throw() {
 	o.r.throwReferenceError(o.ref)
 }