JSON marshal/unmarshal changes type

910 views
Skip to first unread message

ppeeler

unread,
Jan 14, 2015, 3:09:48 PM1/14/15
to golan...@googlegroups.com

I'm trying to write code to do a custom json marshal/unmarshal and I'm running into some behavior that looks very odd to a go newbie like myself.

You can see the code here on the playground:


It seems like the unmarshal function is changing the type of the interface I'm passing a pointer to.

Before the call to unmarshal, the Kind is "struct" and the Type is "main.MCC"

After the call to unmarshal, the Kind is "map" and the Type is "map[string]interface {}"

I'm certain there is some subtle go behavior I'm missing here (or a bonehead bug) -- when I look at the docs, I see:

"To unmarshal JSON into a struct, Unmarshal matches incoming object keys to the keys used by Marshal (either the struct field name or its tag)"

Which doesn't seem to be what's happening.

Any guidance from those who are wiser in the ways of Go would be really appreciated.

Thanks.




ppeeler

unread,
Jan 14, 2015, 3:51:58 PM1/14/15
to golan...@googlegroups.com
Another example -- when the struct is created via reflection, the type is changed by the unmarshal operation. When it is created directly, it is not changed.

This despite the fact that both structs show the same type/kind prior to unmarshal.

https://play.golang.org/p/muAzDKFDuN

ppeeler

unread,
Jan 14, 2015, 4:11:14 PM1/14/15
to golan...@googlegroups.com

Sorry to keep revising, but I've managed to come up with an example with all the cruft taken out:


package main

import (
"encoding/json"
"fmt"
"reflect"
)

type Foo struct {
Bar string
Baz string
}

func main() {

f := Foo{"Hello", "World"}

result, err := json.Marshal(f)

if err != nil {
fmt.Println(err)
}

fmt.Println(string(result))

// create a Foo directly
dir := Foo{}

// create a Foo via reflect
t := reflect.TypeOf(f)
ref := reflect.New(t).Elem().Interface()

fmt.Printf("Direct before unmarshal type: %v, kind: %v, value: %v\n", reflect.TypeOf(dir), reflect.TypeOf(dir).Kind(), dir)
fmt.Printf("Reflect before unmarshal type: %v, kind: %v, value: %v\n", reflect.TypeOf(ref), reflect.TypeOf(ref).Kind(), dir)
err = json.Unmarshal(result, &dir)
if err != nil {
fmt.Println(err)
}
err = json.Unmarshal(result, &ref)
if err != nil {
fmt.Println(err)
}
fmt.Printf("Direct after unmarshal type: %v, kind: %v, value: %v\n", reflect.TypeOf(dir), reflect.TypeOf(dir).Kind(), dir)
fmt.Printf("Reflect after unmarshal type: %v, kind: %v, value: %v\n", reflect.TypeOf(ref), reflect.TypeOf(ref).Kind(), dir)

Dan Kortschak

unread,
Jan 14, 2015, 4:41:25 PM1/14/15
to ppeeler, golan...@googlegroups.com
You are handing json.Unmarshal an *interface{} (the .Interface() method returns interface{}) so it does the generalised unmarshaling behaviour.

Try this https://play.golang.org/p/B1c5AJIAfd

ppeeler

unread,
Jan 14, 2015, 4:45:40 PM1/14/15
to golan...@googlegroups.com, phelps...@gmail.com
That is absolutely beautiful -- Thanks!

Also inspired another approach, which also seems to work:


Although this returns the pointer instead of the direct result

James Bardin

unread,
Jan 14, 2015, 4:51:50 PM1/14/15
to golan...@googlegroups.com, phelps...@gmail.com


On Wednesday, January 14, 2015 at 4:45:40 PM UTC-5, ppeeler wrote:
That is absolutely beautiful -- Thanks!

Also inspired another approach, which also seems to work:


Although this returns the pointer instead of the direct result


A rule of thumb for someone new to go, you never will probably never need a pointer to an interface, so that's usually a sign that something could be wrong. 
 
Reply all
Reply to author
Forward
0 new messages