pkg/serializer: support primitive user types in interfaces

Add handling of user types that has underlying primitive types.
Consider:

	type T int
	var obj interface{} = T(42)

T has kind reflect.Int. But if we serialize obj as just "42",
it will be turned into plain int.
Detect this case and serialize obj as "T(42)".
This commit is contained in:
Dmitry Vyukov 2020-04-25 11:01:02 +02:00
parent b3cb1996a4
commit b8bb8e5f8e
2 changed files with 31 additions and 11 deletions

View File

@ -5,6 +5,7 @@ package serializer
import (
"reflect"
"strings"
"fmt"
"io"
@ -35,11 +36,7 @@ func (w *writer) do(v reflect.Value, sliceElem bool) {
case reflect.Ptr:
w.doPtr(v, sliceElem)
case reflect.Interface:
if v.IsNil() {
w.string("nil")
} else {
w.do(v.Elem(), false)
}
w.doInterface(v)
case reflect.Slice:
w.doSlice(v)
case reflect.Struct:
@ -81,6 +78,29 @@ func (w *writer) doPtr(v reflect.Value, sliceElem bool) {
w.do(v.Elem(), sliceElem)
}
func (w *writer) doInterface(v reflect.Value) {
if v.IsNil() {
w.string("nil")
return
}
elem := v.Elem()
// Handling of user types that has underlying primitive types. Consider:
// type T int
// var obj interface{} = T(42)
// T has kind reflect.Int. But if we serialize obj as just "42", it will be turned into plain int.
// Detect this case and serialize obj as "T(42)".
if (elem.Kind() == reflect.Bool || elem.Kind() == reflect.String ||
elem.Type().ConvertibleTo(reflect.TypeOf(0))) &&
strings.Contains(elem.Type().String(), ".") {
w.string(elem.Type().Name())
w.byte('(')
w.do(elem, false)
w.byte(')')
return
}
w.do(elem, false)
}
func (w *writer) doSlice(v reflect.Value) {
if v.IsNil() || v.Len() == 0 {
w.string("nil")

View File

@ -47,15 +47,15 @@ Y{},
nil,
0,
42,
0,
42,
96,
false,
T(0),
T(42),
U(96),
false,
B(false),
"",
"foo",
"",
"foo",
S(""),
S("foo"),
},nil}`
buf := new(bytes.Buffer)
Write(buf, x)