This is not possible:
type Entity interface { ... }
func GetEntity() Entity {
return Apple{}
}
// ...
v := GetEntity()
err := json.Unmarshal(b, &v)
It fails with "json: cannot unmarshal object into Go value of type Entity". Apparently json.Unmarshal() needs a value that is *Apple.
If GetEntity() returns &Apple{}, the example works. It seems the unmarshaler doesn't understand interfaces containing non-pointer values.
Is there a solution? Can you use reflection to turn v into the right pointer, without knowing exactly what it contains?
To make things more concrete, here's the use case that led me to this point; maybe there's a better way to do this:
I want to unmarshal polymorphic JSON that cannot be mapped to a single type. For example, we may have messages such as this:
{"apple":{"color": "green"}}
or
{"boat":{"type":"sailboat"}}
These should unmarshal into Apple{Color: "green"} and Sailboat{Type:"sailboat"}, respectively.
However, the central logic that performs unmarshaling does not know about the concrete types. In this case, it's a message bus that doesn't know anything messages other than the fact that messages it gets can be marshaled into various formats. A big "switch" to check the key is not possible. (Imagine this is a library and none of the structs aren't even declared in this package.) The entire API is:
Send(Entity)
Receive() Entity
The naive solution is to have a central registry of names:
var registry = map[string]reflect.Type{}
func RegisterType(name string, t reflect.Type) {
registry[name] = t
}
func UnmarshalJSON(msg []byte) Entity {
var raw map[string]*json.RawMessage
if err := json.Unmarshal(msg, &raw); err != nil {
return err
}
if len(raw) != 1 { // error }
for key, value := range raw {
if t, ok := registry[key]; ok {
v := reflect.New(t).Interface()
if err := json.Unmarshal(*value, &v); err != nil {
// ...
}
return v
}
}
return nil
}
And then callers do:
RegisterType("apple", reflect.TypeOf(&Apple{}))
RegisterType("sailboat", reflect.TypeOf(&Sailboat{}))
This works, but:
(1) Requires the use of pointer structs. It always returns *Apple, etc.
(2) It relies on reflection, which is not super fast.
(3) Every struct becomes heap-allocated.
My hope was to avoid those three problems altogether. My entity values are tiny and immutable, and are best passed around as copies.
My other idea was to use a map of concrete types:
var registry = map[string]Entity
func Register(name string, e Entity) {
registry[name] = e
}
...and then rely on copying. This is where it falls down, since json.Unmarshal() does not want to deserialize into non-pointers.
I could, of course, ask every Entity implementation to implement unmarshaling:
type Entity interface {
json.Unmarshaler
}
And then simply call `UnmarshalJSON()` myself. But every single implementation of it would look like this:
func (apple *Apple) UnmarshalJSON(b []byte) error {
return json.Unmarshal(b, apple)
}
This seems a bit stupid.