Browse Source

codec: support json.(M|Unm)arshaler interfaces for json.

Some customers of json currently have the json.(M|Unm)arshaler interfaces implemented.
We now support them, in addition to the encoding/Text(M|Unm)arshaler interfaces.

Updates #95
Ugorji Nwoke 10 years ago
parent
commit
067c77c51c
3 changed files with 52 additions and 0 deletions
  1. 12 0
      codec/decode.go
  2. 18 0
      codec/encode.go
  3. 22 0
      codec/helper.go

+ 12 - 0
codec/decode.go

@@ -386,6 +386,14 @@ func (f decFnInfo) textUnmarshal(rv reflect.Value) {
 	}
 }
 
+func (f decFnInfo) jsonUnmarshal(rv reflect.Value) {
+	tm := f.getValueForUnmarshalInterface(rv, f.ti.junmIndir).(jsonUnmarshaler)
+	fnerr := tm.UnmarshalJSON(f.dd.DecodeBytes(f.d.b[:], true, true))
+	if fnerr != nil {
+		panic(fnerr)
+	}
+}
+
 func (f decFnInfo) kErr(rv reflect.Value) {
 	f.d.errorf("no decoding function defined for kind %v", rv.Kind())
 }
@@ -1275,6 +1283,10 @@ func (d *Decoder) getDecFn(rt reflect.Type, checkFastpath, checkCodecSelfer bool
 	} else if supportMarshalInterfaces && !d.be && ti.tunm {
 		fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}
 		fn.f = (decFnInfo).textUnmarshal
+	} else if supportMarshalInterfaces && !d.be && ti.junm {
+		//TODO: This only works NOW, as JSON is the ONLY text format.
+		fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}
+		fn.f = (decFnInfo).jsonUnmarshal
 	} else {
 		rk := rt.Kind()
 		if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) {

+ 18 - 0
codec/encode.go

@@ -363,6 +363,20 @@ func (f encFnInfo) textMarshal(rv reflect.Value) {
 	}
 }
 
+func (f encFnInfo) jsonMarshal(rv reflect.Value) {
+	if v, proceed := f.getValueForMarshalInterface(rv, f.ti.jmIndir); proceed {
+		bs, fnerr := v.(jsonMarshaler).MarshalJSON()
+		if fnerr != nil {
+			panic(fnerr)
+		}
+		if bs == nil {
+			f.ee.EncodeNil()
+		} else {
+			f.ee.EncodeStringBytes(c_UTF8, bs)
+		}
+	}
+}
+
 func (f encFnInfo) kBool(rv reflect.Value) {
 	f.ee.EncodeBool(rv.Bool())
 }
@@ -1110,6 +1124,10 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCo
 	} else if supportMarshalInterfaces && !e.be && ti.tm {
 		fi.encFnInfoX = &encFnInfoX{e: e, ti: ti}
 		fn.f = (encFnInfo).textMarshal
+	} else if supportMarshalInterfaces && !e.be && ti.jm {
+		//TODO: This only works NOW, as JSON is the ONLY text format.
+		fi.encFnInfoX = &encFnInfoX{e: e, ti: ti}
+		fn.f = (encFnInfo).jsonMarshal
 	} else {
 		rk := rt.Kind()
 		// if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) {

+ 22 - 0
codec/helper.go

@@ -186,6 +186,14 @@ const (
 
 type seqType uint8
 
+// mirror json.Marshaler and json.Unmarshaler here, so we don't import the encoding/json package
+type jsonMarshaler interface {
+	MarshalJSON() ([]byte, error)
+}
+type jsonUnmarshaler interface {
+	UnmarshalJSON([]byte) error
+}
+
 const (
 	_ seqType = iota
 	seqTypeArray
@@ -217,6 +225,9 @@ var (
 	textMarshalerTyp   = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
 	textUnmarshalerTyp = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
 
+	jsonMarshalerTyp   = reflect.TypeOf((*jsonMarshaler)(nil)).Elem()
+	jsonUnmarshalerTyp = reflect.TypeOf((*jsonUnmarshaler)(nil)).Elem()
+
 	selferTyp = reflect.TypeOf((*Selfer)(nil)).Elem()
 
 	uint8SliceTypId = reflect.ValueOf(uint8SliceTyp).Pointer()
@@ -593,6 +604,11 @@ type typeInfo struct {
 	tmIndir   int8 // number of indirections to get to textMarshaler type
 	tunmIndir int8 // number of indirections to get to textUnmarshaler type
 
+	jm        bool // base type (T or *T) is a jsonMarshaler
+	junm      bool // base type (T or *T) is a jsonUnmarshaler
+	jmIndir   int8 // number of indirections to get to jsonMarshaler type
+	junmIndir int8 // number of indirections to get to jsonUnmarshaler type
+
 	cs      bool // base type (T or *T) is a Selfer
 	csIndir int8 // number of indirections to get to Selfer type
 
@@ -668,6 +684,12 @@ func getTypeInfo(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
 	if ok, indir = implementsIntf(rt, textUnmarshalerTyp); ok {
 		ti.tunm, ti.tunmIndir = true, indir
 	}
+	if ok, indir = implementsIntf(rt, jsonMarshalerTyp); ok {
+		ti.jm, ti.jmIndir = true, indir
+	}
+	if ok, indir = implementsIntf(rt, jsonUnmarshalerTyp); ok {
+		ti.junm, ti.junmIndir = true, indir
+	}
 	if ok, indir = implementsIntf(rt, selferTyp); ok {
 		ti.cs, ti.csIndir = true, indir
 	}