David Adrian
David Adrian

Reputation: 1109

Go: Pointer to interface{} loses underyling type

I'm working with some "generic" functions in Go that operate on interface{} and send things around channels, etc. Slimmed down, let's say I have something like:

type MyType struct {
    // Fields
}

func (m *MyType) MarshalJSON() ([]byte, error) {
    // MarshalJSON
    log.Print("custom JSON marshal")
    return []byte("hello"), nil
}

func GenericFunc(v interface{}) {
    // Do things...
    log.Print(reflect.TypeOf(v))
    log.Print(reflect.TypeOf(&v))
    b, _ = json.Marshal(&v)
    fmt.Println(string(b))
}

func main() {
    m := MyType{}
    GenericFunc(m)
}

This outputs:

2014/11/16 12:41:44 MyType 
2014/11/16 12:41:44 *interface {}

Followed by the default json.Marshal output, rather than the custom one. As far as I can tell, that's because the call to Marshal sees a value of type pointer-to-interface rather than pointer-to-MyType.

Why do I lose type information when I take &v? I would expect the second line of the output to be *MyType and not *interface {}.

Is there any way for me have the custom JSON Marshaller called without explicitly casting?

Upvotes: 0

Views: 296

Answers (2)

Logiraptor
Logiraptor

Reputation: 1528

It sounds like you want to send non-pointer values over a chan interface{} and have a custom MarshalJSON method work as expected. In that case, just don't define the method on the pointer type.

See here

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "time"
)

func printer(in chan interface{}) {
    for val := range in {
        buf, err := json.Marshal(val)
        if err != nil {
            log.Println(err.Error())
        }
        log.Println(string(buf))
    }
}

type MyType struct {
    name string
}

func (m MyType) MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf(`"%s"`, m.name)), nil
}

func main() {
    ch := make(chan interface{})

    go printer(ch)
    ch <- "string value"
    ch <- 25
    ch <- MyType{
        name: "foo",
    }

    time.Sleep(time.Second)
}

The only real difference is the method receiver. func (m MyType) MarshalJSON ([]byte, error) instead of func (m *MyType) MarshalJSON ([]byte, error)

Upvotes: 0

Not_a_Golfer
Not_a_Golfer

Reputation: 49265

Just pass a pointer to your struct and not its value to the function. The pointer is still interface{} but a pointer to the interface is meaningless.

Upvotes: 2

Related Questions