Question

Given the following code:

package main

import (
    "encoding/json"
    "log"
)

type Somefin string

func (s *Somefin) UnmarshalJSON(b []byte) error {
    log.Println("Unmarshaling",string(b))
    *s = Somefin("~"+string(b)+"~")
    return nil
}

type Wat struct {
    A, B string
    *Somefin
}

func main() {
    b := []byte(`{"A":"foo","B":"bar","Somefin":"baz"}`)
    w := &Wat{Somefin: new(Somefin)}

    err := json.Unmarshal(b,w)
    log.Println(w, err)
}

I get the following output:

# go run wat.go
2013/12/14 13:59:17 Unmarshaling {"A":"foo","B":"bar","Somefin":"baz"}
2013/12/14 13:59:17 &{  <nil>} <nil>

So the Somefin key is for some reason trying to Unmarshal the entire structure instead of just the key it ought to. Am I doing this wrong or is this a bug in the json encoder? This is on go 1.2, btw.

Was it helpful?

Solution

Why you are getting no result at the end

This is no bug in the decoder, it is a bug in your code. You're just assigning another address to the local pointer s in UnmarshalJSON. Corrected code:

func (s *Somefin) UnmarshalJSON(b []byte) error {
    log.Println("Unmarshaling",string(b))
    sn := Somefin("~"+string(b)+"~")
    *s = sn
    return nil
}

Semantics of s = &sn: Assign the address &sn to s. This is similar to s = 42.

Semantics of *s = sn: Copy whatever is sn to the place where s points to.

One requirement for this to work is that s points to a valid memory location and must not be nil. Example usage of your code (play):

w := &Wat{Somefin: new(Somefin)}

err := json.Unmarshal(b,w)
log.Printf("%#v (%s)\n", w, err)

Crucial is the initialization of Wat with a new Somefin so that the pointer s in UnmarshalJSON is valid (points to the object created with new(Somefin)).

Why you are getting the whole string in UnmarshalJSON

Embedding is not polymorphism. While the method set of the embedded object (in your case Somefin) is promoted to the outside, this does not mean that the method is now working on the embedding struct rather than the embedded one.

Small example (play):

type Inner struct { a int }
func (i *Inner) A() int { return i.a }

type Outer struct {
    *Inner
    a int
}

i := &Inner{}
o := Outer{Inner: i}

fmt.Println("Inner.A():", i.A())
fmt.Println("Outer.A():", o.A())

o.a = 2

fmt.Println("Outer.A():", o.A())

With polymorphism you would expect Outer.A() to return 2 as method A() would operate in the scope of Outer and not Inner anymore. With embedding the scope is never changed and A() will always remain operating on Inner.

The same effect prevents your UnmarshalJSON from seeing the two members A and B as these are simply not in the scope of Somefin:

  1. JSON library sees UnmarshalJSON on Wat because UnmarshalJSON from Somefin gets promoted to the outside
  2. JSON library cannot find any matching element in Somefin and delivers the whole input

OTHER TIPS

I figured this out.

If you have the struct definition like this:

type Wat struct {
    A, B string
    Somefin
}

Then the error I described in the OP happens. But if you do:

type Wat struct {
    A, B string
    Somefin Somefin
}

Then it doesn't. Check out Chris's comment to this answer for a good explanation of why.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top