I’m trying to write a function which converts a byte array of JSON string to another in accordance with the type parameter of the return value as the following rule:
- map[string]interface{}: convert to map[string]interface{}
- []byte: no conversion, return as is
- struct: convert to the struc
My code is as follow:
func GetJsonData[T any](jsonByteArray []byte) (result *T, err error) {
var buff T
switch any(result).(type) { // https://appliedgo.com/blog/a-tip-and-a-trick-when-working-with-generics
case *[]byte:
result = &T(jsonByteArray)
default:
err = json.Unmarshal(jsonByteArray, &buff)
result = &buff
}
return
}
This code occurs following type error at the point of cast the type of jsonByteArray to T as follows:
cannot convert jsonByteArray (variable of type []byte) to type T
How can I assign the pointer of this []byte type variable to the generic type return value?
>Solution :
Since T
is constrained by any
, you can’t directly convert. You have to assert that &jsonByteArray
is really the same type as *T
in that switch case:
func GetJsonData[T any](jsonByteArray []byte) (result *T, err error) {
var buff T
switch any(result).(type) {
case *[]byte:
result = any(&jsonByteArray).(*T)
default:
err = json.Unmarshal(jsonByteArray, &buff)
result = &buff
}
return
}
This makes the compile error go away, however it isn’t particularly good design. If you need to specialize json.Unmarshal
for only one type (*[]byte
) you are better off changing the call sites instead of using a generic function.
I assume that your goal is to allow callers to get the byte slice as is, instead of unmarshalling. Then at call site you would call the function as
data := GetJsonData[[]byte](jsonByteArray)
This means that at that point you already know that jsonByteArray
is a byte slice.
Then, there’s no reason to call the function. You can simply take the address of the argument: data := &jsonByteArray
, and use json.Unmarshal
everywhere else.