@@ -132,6 +132,20 @@ func (e *MarshalerError) Unwrap() error {
132132 return e .err
133133}
134134
135+ type TranscodeError struct {
136+ err error
137+ rtype reflect.Type
138+ source , target string
139+ }
140+
141+ func (e TranscodeError ) Error () string {
142+ return "cbor: error transcoding " + e .rtype .String () + " from " + e .source + " to " + e .target + ": " + e .err .Error ()
143+ }
144+
145+ func (e TranscodeError ) Unwrap () error {
146+ return e .err
147+ }
148+
135149// UnsupportedTypeError is returned by Marshal when attempting to encode value
136150// of an unsupported type.
137151type UnsupportedTypeError struct {
@@ -567,6 +581,11 @@ type EncOptions struct {
567581
568582 // BinaryMarshaler specifies how to encode types that implement encoding.BinaryMarshaler.
569583 BinaryMarshaler BinaryMarshalerMode
584+
585+ // JSONMarshalerTranscoder sets the transcoding scheme used to marshal types that implement
586+ // json.Marshaler but do not also implement cbor.Marshaler. If nil, encoding behavior is not
587+ // influenced by whether or not a type implements json.Marshaler.
588+ JSONMarshalerTranscoder Transcoder
570589}
571590
572591// CanonicalEncOptions returns EncOptions for "Canonical CBOR" encoding,
@@ -796,6 +815,7 @@ func (opts EncOptions) encMode() (*encMode, error) { //nolint:gocritic // ignore
796815 byteSliceLaterEncodingTag : byteSliceLaterEncodingTag ,
797816 byteArray : opts .ByteArray ,
798817 binaryMarshaler : opts .BinaryMarshaler ,
818+ jsonMarshalerTranscoder : opts .JSONMarshalerTranscoder ,
799819 }
800820 return & em , nil
801821}
@@ -841,6 +861,7 @@ type encMode struct {
841861 byteSliceLaterEncodingTag uint64
842862 byteArray ByteArrayMode
843863 binaryMarshaler BinaryMarshalerMode
864+ jsonMarshalerTranscoder Transcoder
844865}
845866
846867var defaultEncMode , _ = EncOptions {}.encMode ()
@@ -917,22 +938,23 @@ func getMarshalerDecMode(indefLength IndefLengthMode, tagsMd TagsMode) *decMode
917938// EncOptions returns user specified options used to create this EncMode.
918939func (em * encMode ) EncOptions () EncOptions {
919940 return EncOptions {
920- Sort : em .sort ,
921- ShortestFloat : em .shortestFloat ,
922- NaNConvert : em .nanConvert ,
923- InfConvert : em .infConvert ,
924- BigIntConvert : em .bigIntConvert ,
925- Time : em .time ,
926- TimeTag : em .timeTag ,
927- IndefLength : em .indefLength ,
928- NilContainers : em .nilContainers ,
929- TagsMd : em .tagsMd ,
930- OmitEmpty : em .omitEmpty ,
931- String : em .stringType ,
932- FieldName : em .fieldName ,
933- ByteSliceLaterFormat : em .byteSliceLaterFormat ,
934- ByteArray : em .byteArray ,
935- BinaryMarshaler : em .binaryMarshaler ,
941+ Sort : em .sort ,
942+ ShortestFloat : em .shortestFloat ,
943+ NaNConvert : em .nanConvert ,
944+ InfConvert : em .infConvert ,
945+ BigIntConvert : em .bigIntConvert ,
946+ Time : em .time ,
947+ TimeTag : em .timeTag ,
948+ IndefLength : em .indefLength ,
949+ NilContainers : em .nilContainers ,
950+ TagsMd : em .tagsMd ,
951+ OmitEmpty : em .omitEmpty ,
952+ String : em .stringType ,
953+ FieldName : em .fieldName ,
954+ ByteSliceLaterFormat : em .byteSliceLaterFormat ,
955+ ByteArray : em .byteArray ,
956+ BinaryMarshaler : em .binaryMarshaler ,
957+ JSONMarshalerTranscoder : em .jsonMarshalerTranscoder ,
936958 }
937959}
938960
@@ -1704,6 +1726,59 @@ func (bme binaryMarshalerEncoder) isEmpty(em *encMode, v reflect.Value) (bool, e
17041726 return len (data ) == 0 , nil
17051727}
17061728
1729+ type jsonMarshalerEncoder struct {
1730+ alternateEncode encodeFunc
1731+ alternateIsEmpty isEmptyFunc
1732+ }
1733+
1734+ func (jme jsonMarshalerEncoder ) encode (e * bytes.Buffer , em * encMode , v reflect.Value ) error {
1735+ if em .jsonMarshalerTranscoder == nil {
1736+ return jme .alternateEncode (e , em , v )
1737+ }
1738+
1739+ vt := v .Type ()
1740+ m , ok := v .Interface ().(jsonMarshaler )
1741+ if ! ok {
1742+ pv := reflect .New (vt )
1743+ pv .Elem ().Set (v )
1744+ m = pv .Interface ().(jsonMarshaler )
1745+ }
1746+
1747+ json , err := m .MarshalJSON ()
1748+ if err != nil {
1749+ return err
1750+ }
1751+
1752+ offset := e .Len ()
1753+
1754+ if b := em .encTagBytes (vt ); b != nil {
1755+ e .Write (b )
1756+ }
1757+
1758+ if err := em .jsonMarshalerTranscoder .Transcode (e , bytes .NewReader (json )); err != nil {
1759+ return & TranscodeError {err : err , rtype : vt , source : "json" , target : "cbor" }
1760+ }
1761+
1762+ // Validate that the transcode function has written exactly one well-formed data item.
1763+ d := decoder {data : e .Bytes ()[offset :], dm : getMarshalerDecMode (em .indefLength , em .tagsMd )}
1764+ if err := d .wellformed (false , true ); err != nil {
1765+ e .Truncate (offset )
1766+ return & TranscodeError {err : err , rtype : vt , source : "json" , target : "cbor" }
1767+ }
1768+
1769+ return nil
1770+ }
1771+
1772+ func (jme jsonMarshalerEncoder ) isEmpty (em * encMode , v reflect.Value ) (bool , error ) {
1773+ if em .jsonMarshalerTranscoder == nil {
1774+ return jme .alternateIsEmpty (em , v )
1775+ }
1776+
1777+ // As with types implementing cbor.Marshaler, transcoded json.Marshaler values always encode
1778+ // as exactly one complete CBOR data item.
1779+ return false , nil
1780+ }
1781+
17071782func encodeMarshalerType (e * bytes.Buffer , em * encMode , v reflect.Value ) error {
17081783 if em .tagsMd == TagsForbidden && v .Type () == typeRawTag {
17091784 return errors .New ("cbor: cannot encode cbor.RawTag when TagsMd is TagsForbidden" )
@@ -1807,9 +1882,12 @@ func encodeHead(e *bytes.Buffer, t byte, n uint64) int {
18071882 return headSize
18081883}
18091884
1885+ type jsonMarshaler interface { MarshalJSON () ([]byte , error ) }
1886+
18101887var (
18111888 typeMarshaler = reflect .TypeOf ((* Marshaler )(nil )).Elem ()
18121889 typeBinaryMarshaler = reflect .TypeOf ((* encoding .BinaryMarshaler )(nil )).Elem ()
1890+ typeJSONMarshaler = reflect .TypeOf ((* jsonMarshaler )(nil )).Elem ()
18131891 typeRawMessage = reflect .TypeOf (RawMessage (nil ))
18141892 typeByteString = reflect .TypeOf (ByteString ("" ))
18151893)
@@ -1852,6 +1930,19 @@ func getEncodeFuncInternal(t reflect.Type) (ef encodeFunc, ief isEmptyFunc, izf
18521930 ief = bme .isEmpty
18531931 }()
18541932 }
1933+ if reflect .PointerTo (t ).Implements (typeJSONMarshaler ) {
1934+ defer func () {
1935+ // capture encoding method used for modes that don't support transcoding
1936+ // from types that implement json.Marshaler.
1937+ jme := jsonMarshalerEncoder {
1938+ alternateEncode : ef ,
1939+ alternateIsEmpty : ief ,
1940+ }
1941+ ef = jme .encode
1942+ ief = jme .isEmpty
1943+ }()
1944+ }
1945+
18551946 switch k {
18561947 case reflect .Bool :
18571948 return encodeBool , isEmptyBool , getIsZeroFunc (t )
0 commit comments