Skip to content

Commit 4e825ca

Browse files
authored
Merge pull request #673 from benluddy/json-interface-transcoding
Add optional support for json.Marshaler and json.Unmarshaler via transcoding
2 parents a8dbe3e + ba129eb commit 4e825ca

7 files changed

Lines changed: 679 additions & 143 deletions

File tree

cache.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const (
3737
specialTypeIface
3838
specialTypeTag
3939
specialTypeTime
40+
specialTypeJSONUnmarshalerIface
4041
)
4142

4243
type typeInfo struct {
@@ -75,6 +76,8 @@ func newTypeInfo(t reflect.Type) *typeInfo {
7576
tInfo.spclType = specialTypeUnexportedUnmarshalerIface
7677
} else if reflect.PointerTo(t).Implements(typeUnmarshaler) {
7778
tInfo.spclType = specialTypeUnmarshalerIface
79+
} else if reflect.PointerTo(t).Implements(typeJSONUnmarshaler) {
80+
tInfo.spclType = specialTypeJSONUnmarshalerIface
7881
}
7982

8083
switch k {

common.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package cbor
55

66
import (
77
"fmt"
8+
"io"
89
"strconv"
910
)
1011

@@ -180,3 +181,11 @@ func validBuiltinTag(tagNum uint64, contentHead byte) error {
180181

181182
return nil
182183
}
184+
185+
// Transcoder is a scheme for transcoding a single CBOR encoded data item to or from a different
186+
// data format.
187+
type Transcoder interface {
188+
// Transcode reads the data item in its source format from a Reader and writes a
189+
// corresponding representation in its destination format to a Writer.
190+
Transcode(dst io.Writer, src io.Reader) error
191+
}

decode.go

Lines changed: 128 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package cbor
55

66
import (
7+
"bytes"
78
"encoding"
89
"encoding/base64"
910
"encoding/binary"
@@ -906,6 +907,11 @@ type DecOptions struct {
906907
// TextUnmarshaler specifies how to decode into types that implement
907908
// encoding.TextUnmarshaler.
908909
TextUnmarshaler TextUnmarshalerMode
910+
911+
// JSONUnmarshalerTranscoder sets the transcoding scheme used to unmarshal types that
912+
// implement json.Unmarshaler but do not also implement cbor.Unmarshaler. If nil, decoding
913+
// behavior is not influenced by whether or not a type implements json.Unmarshaler.
914+
JSONUnmarshalerTranscoder Transcoder
909915
}
910916

911917
// DecMode returns DecMode with immutable options and no tags (safe for concurrency).
@@ -1123,33 +1129,34 @@ func (opts DecOptions) decMode() (*decMode, error) { //nolint:gocritic // ignore
11231129
}
11241130

11251131
dm := decMode{
1126-
dupMapKey: opts.DupMapKey,
1127-
timeTag: opts.TimeTag,
1128-
maxNestedLevels: opts.MaxNestedLevels,
1129-
maxArrayElements: opts.MaxArrayElements,
1130-
maxMapPairs: opts.MaxMapPairs,
1131-
indefLength: opts.IndefLength,
1132-
tagsMd: opts.TagsMd,
1133-
intDec: opts.IntDec,
1134-
mapKeyByteString: opts.MapKeyByteString,
1135-
extraReturnErrors: opts.ExtraReturnErrors,
1136-
defaultMapType: opts.DefaultMapType,
1137-
utf8: opts.UTF8,
1138-
fieldNameMatching: opts.FieldNameMatching,
1139-
bigIntDec: opts.BigIntDec,
1140-
defaultByteStringType: opts.DefaultByteStringType,
1141-
byteStringToString: opts.ByteStringToString,
1142-
fieldNameByteString: opts.FieldNameByteString,
1143-
unrecognizedTagToAny: opts.UnrecognizedTagToAny,
1144-
timeTagToAny: opts.TimeTagToAny,
1145-
simpleValues: simpleValues,
1146-
nanDec: opts.NaN,
1147-
infDec: opts.Inf,
1148-
byteStringToTime: opts.ByteStringToTime,
1149-
byteStringExpectedFormat: opts.ByteStringExpectedFormat,
1150-
bignumTag: opts.BignumTag,
1151-
binaryUnmarshaler: opts.BinaryUnmarshaler,
1152-
textUnmarshaler: opts.TextUnmarshaler,
1132+
dupMapKey: opts.DupMapKey,
1133+
timeTag: opts.TimeTag,
1134+
maxNestedLevels: opts.MaxNestedLevels,
1135+
maxArrayElements: opts.MaxArrayElements,
1136+
maxMapPairs: opts.MaxMapPairs,
1137+
indefLength: opts.IndefLength,
1138+
tagsMd: opts.TagsMd,
1139+
intDec: opts.IntDec,
1140+
mapKeyByteString: opts.MapKeyByteString,
1141+
extraReturnErrors: opts.ExtraReturnErrors,
1142+
defaultMapType: opts.DefaultMapType,
1143+
utf8: opts.UTF8,
1144+
fieldNameMatching: opts.FieldNameMatching,
1145+
bigIntDec: opts.BigIntDec,
1146+
defaultByteStringType: opts.DefaultByteStringType,
1147+
byteStringToString: opts.ByteStringToString,
1148+
fieldNameByteString: opts.FieldNameByteString,
1149+
unrecognizedTagToAny: opts.UnrecognizedTagToAny,
1150+
timeTagToAny: opts.TimeTagToAny,
1151+
simpleValues: simpleValues,
1152+
nanDec: opts.NaN,
1153+
infDec: opts.Inf,
1154+
byteStringToTime: opts.ByteStringToTime,
1155+
byteStringExpectedFormat: opts.ByteStringExpectedFormat,
1156+
bignumTag: opts.BignumTag,
1157+
binaryUnmarshaler: opts.BinaryUnmarshaler,
1158+
textUnmarshaler: opts.TextUnmarshaler,
1159+
jsonUnmarshalerTranscoder: opts.JSONUnmarshalerTranscoder,
11531160
}
11541161

11551162
return &dm, nil
@@ -1202,34 +1209,35 @@ type DecMode interface {
12021209
}
12031210

12041211
type decMode struct {
1205-
tags tagProvider
1206-
dupMapKey DupMapKeyMode
1207-
timeTag DecTagMode
1208-
maxNestedLevels int
1209-
maxArrayElements int
1210-
maxMapPairs int
1211-
indefLength IndefLengthMode
1212-
tagsMd TagsMode
1213-
intDec IntDecMode
1214-
mapKeyByteString MapKeyByteStringMode
1215-
extraReturnErrors ExtraDecErrorCond
1216-
defaultMapType reflect.Type
1217-
utf8 UTF8Mode
1218-
fieldNameMatching FieldNameMatchingMode
1219-
bigIntDec BigIntDecMode
1220-
defaultByteStringType reflect.Type
1221-
byteStringToString ByteStringToStringMode
1222-
fieldNameByteString FieldNameByteStringMode
1223-
unrecognizedTagToAny UnrecognizedTagToAnyMode
1224-
timeTagToAny TimeTagToAnyMode
1225-
simpleValues *SimpleValueRegistry
1226-
nanDec NaNMode
1227-
infDec InfMode
1228-
byteStringToTime ByteStringToTimeMode
1229-
byteStringExpectedFormat ByteStringExpectedFormatMode
1230-
bignumTag BignumTagMode
1231-
binaryUnmarshaler BinaryUnmarshalerMode
1232-
textUnmarshaler TextUnmarshalerMode
1212+
tags tagProvider
1213+
dupMapKey DupMapKeyMode
1214+
timeTag DecTagMode
1215+
maxNestedLevels int
1216+
maxArrayElements int
1217+
maxMapPairs int
1218+
indefLength IndefLengthMode
1219+
tagsMd TagsMode
1220+
intDec IntDecMode
1221+
mapKeyByteString MapKeyByteStringMode
1222+
extraReturnErrors ExtraDecErrorCond
1223+
defaultMapType reflect.Type
1224+
utf8 UTF8Mode
1225+
fieldNameMatching FieldNameMatchingMode
1226+
bigIntDec BigIntDecMode
1227+
defaultByteStringType reflect.Type
1228+
byteStringToString ByteStringToStringMode
1229+
fieldNameByteString FieldNameByteStringMode
1230+
unrecognizedTagToAny UnrecognizedTagToAnyMode
1231+
timeTagToAny TimeTagToAnyMode
1232+
simpleValues *SimpleValueRegistry
1233+
nanDec NaNMode
1234+
infDec InfMode
1235+
byteStringToTime ByteStringToTimeMode
1236+
byteStringExpectedFormat ByteStringExpectedFormatMode
1237+
bignumTag BignumTagMode
1238+
binaryUnmarshaler BinaryUnmarshalerMode
1239+
textUnmarshaler TextUnmarshalerMode
1240+
jsonUnmarshalerTranscoder Transcoder
12331241
}
12341242

12351243
var defaultDecMode, _ = DecOptions{}.decMode()
@@ -1244,33 +1252,34 @@ func (dm *decMode) DecOptions() DecOptions {
12441252
}
12451253

12461254
return DecOptions{
1247-
DupMapKey: dm.dupMapKey,
1248-
TimeTag: dm.timeTag,
1249-
MaxNestedLevels: dm.maxNestedLevels,
1250-
MaxArrayElements: dm.maxArrayElements,
1251-
MaxMapPairs: dm.maxMapPairs,
1252-
IndefLength: dm.indefLength,
1253-
TagsMd: dm.tagsMd,
1254-
IntDec: dm.intDec,
1255-
MapKeyByteString: dm.mapKeyByteString,
1256-
ExtraReturnErrors: dm.extraReturnErrors,
1257-
DefaultMapType: dm.defaultMapType,
1258-
UTF8: dm.utf8,
1259-
FieldNameMatching: dm.fieldNameMatching,
1260-
BigIntDec: dm.bigIntDec,
1261-
DefaultByteStringType: dm.defaultByteStringType,
1262-
ByteStringToString: dm.byteStringToString,
1263-
FieldNameByteString: dm.fieldNameByteString,
1264-
UnrecognizedTagToAny: dm.unrecognizedTagToAny,
1265-
TimeTagToAny: dm.timeTagToAny,
1266-
SimpleValues: simpleValues,
1267-
NaN: dm.nanDec,
1268-
Inf: dm.infDec,
1269-
ByteStringToTime: dm.byteStringToTime,
1270-
ByteStringExpectedFormat: dm.byteStringExpectedFormat,
1271-
BignumTag: dm.bignumTag,
1272-
BinaryUnmarshaler: dm.binaryUnmarshaler,
1273-
TextUnmarshaler: dm.textUnmarshaler,
1255+
DupMapKey: dm.dupMapKey,
1256+
TimeTag: dm.timeTag,
1257+
MaxNestedLevels: dm.maxNestedLevels,
1258+
MaxArrayElements: dm.maxArrayElements,
1259+
MaxMapPairs: dm.maxMapPairs,
1260+
IndefLength: dm.indefLength,
1261+
TagsMd: dm.tagsMd,
1262+
IntDec: dm.intDec,
1263+
MapKeyByteString: dm.mapKeyByteString,
1264+
ExtraReturnErrors: dm.extraReturnErrors,
1265+
DefaultMapType: dm.defaultMapType,
1266+
UTF8: dm.utf8,
1267+
FieldNameMatching: dm.fieldNameMatching,
1268+
BigIntDec: dm.bigIntDec,
1269+
DefaultByteStringType: dm.defaultByteStringType,
1270+
ByteStringToString: dm.byteStringToString,
1271+
FieldNameByteString: dm.fieldNameByteString,
1272+
UnrecognizedTagToAny: dm.unrecognizedTagToAny,
1273+
TimeTagToAny: dm.timeTagToAny,
1274+
SimpleValues: simpleValues,
1275+
NaN: dm.nanDec,
1276+
Inf: dm.infDec,
1277+
ByteStringToTime: dm.byteStringToTime,
1278+
ByteStringExpectedFormat: dm.byteStringExpectedFormat,
1279+
BignumTag: dm.bignumTag,
1280+
BinaryUnmarshaler: dm.binaryUnmarshaler,
1281+
TextUnmarshaler: dm.textUnmarshaler,
1282+
JSONUnmarshalerTranscoder: dm.jsonUnmarshalerTranscoder,
12741283
}
12751284
}
12761285

@@ -1497,6 +1506,14 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
14971506

14981507
case specialTypeUnexportedUnmarshalerIface:
14991508
return d.parseToUnexportedUnmarshaler(v)
1509+
1510+
case specialTypeJSONUnmarshalerIface:
1511+
// This special type implies that the type does not also implement
1512+
// cbor.Umarshaler.
1513+
if d.dm.jsonUnmarshalerTranscoder == nil {
1514+
break
1515+
}
1516+
return d.parseToJSONUnmarshaler(v)
15001517
}
15011518
}
15021519

@@ -1862,6 +1879,32 @@ func (d *decoder) parseToUnexportedUnmarshaler(v reflect.Value) error {
18621879
return errors.New("cbor: failed to assert " + v.Type().String() + " as cbor.unmarshaler")
18631880
}
18641881

1882+
// parseToJSONUnmarshaler parses CBOR data to be transcoded to JSON and passed to the value's
1883+
// implementation of the json.Unmarshaler interface. It assumes data is well-formed, and does not
1884+
// perform bounds checking.
1885+
func (d *decoder) parseToJSONUnmarshaler(v reflect.Value) error {
1886+
if d.nextCBORNil() && v.Kind() == reflect.Pointer && v.IsNil() {
1887+
d.skip()
1888+
return nil
1889+
}
1890+
1891+
if v.Kind() != reflect.Pointer && v.CanAddr() {
1892+
v = v.Addr()
1893+
}
1894+
if u, ok := v.Interface().(jsonUnmarshaler); ok {
1895+
start := d.off
1896+
d.skip()
1897+
e := getEncodeBuffer()
1898+
defer putEncodeBuffer(e)
1899+
if err := d.dm.jsonUnmarshalerTranscoder.Transcode(e, bytes.NewReader(d.data[start:d.off])); err != nil {
1900+
return &TranscodeError{err: err, rtype: v.Type(), sourceFormat: "cbor", targetFormat: "json"}
1901+
}
1902+
return u.UnmarshalJSON(e.Bytes())
1903+
}
1904+
d.skip()
1905+
return errors.New("cbor: failed to assert " + v.Type().String() + " as json.Unmarshaler")
1906+
}
1907+
18651908
// parse parses CBOR data and returns value in default Go type.
18661909
// It assumes data is well-formed, and does not perform bounds checking.
18671910
func (d *decoder) parse(skipSelfDescribedTag bool) (any, error) { //nolint:gocyclo
@@ -3018,6 +3061,8 @@ func (d *decoder) nextCBORNil() bool {
30183061
return d.data[d.off] == 0xf6 || d.data[d.off] == 0xf7
30193062
}
30203063

3064+
type jsonUnmarshaler interface{ UnmarshalJSON([]byte) error }
3065+
30213066
var (
30223067
typeIntf = reflect.TypeOf([]any(nil)).Elem()
30233068
typeTime = reflect.TypeOf(time.Time{})
@@ -3026,6 +3071,7 @@ var (
30263071
typeUnexportedUnmarshaler = reflect.TypeOf((*unmarshaler)(nil)).Elem()
30273072
typeBinaryUnmarshaler = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
30283073
typeTextUnmarshaler = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
3074+
typeJSONUnmarshaler = reflect.TypeOf((*jsonUnmarshaler)(nil)).Elem()
30293075
typeString = reflect.TypeOf("")
30303076
typeByteSlice = reflect.TypeOf([]byte(nil))
30313077
)

0 commit comments

Comments
 (0)