嵌套结构的非编组字段不起作用

Given the following structs

type Foo struct {
    Thing time.Duration `json:"thing"`
}

type Bar struct {
    Foo
    Entry time.Duration `json:"entry"`
}

I want to custom time.Duration format and load Bar value from json string like:

{
  "thing": "hour",
  "entry": "second"
}

So I override UnmarshalJSON for Foo and Bar (https://play.golang.org/p/6v71eG_Xr98):

package main

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

type Foo struct {
    Thing time.Duration `json:"thing"`
}

type Bar struct {
    Foo
    Entry time.Duration `json:"entry"`
}

func timeConvert(s string) time.Duration {
    if s == "hour" {
        return time.Hour
    }
    if s == "second" {
        return time.Second
    }
    return time.Duration(0)
}

func (t *Foo) UnmarshalJSON(data []byte) error {
    type Alias Foo
    a := struct {
        Thing string `json:"thing"`
        *Alias
    }{Alias: (*Alias)(t)}
    err := json.Unmarshal(data, &a)
    t.Thing = timeConvert(a.Thing)
    fmt.Printf("Foo: %v [%v]
", *t, err)
    return err
}

func (t *Bar) UnmarshalJSON(data []byte) error {
    type Alias Bar
    a := struct {
        Entry string `json:"entry"`
        *Alias
    }{Alias: (*Alias)(t)}
    err := json.Unmarshal(data, &a)
    t.Entry = timeConvert(a.Entry)
    fmt.Printf("Bar: %v [%v]
", *t, err)
    return err
}

func main() {
    data := []byte(`{"entry": "second", "thing": "hour"}`)
    json.Unmarshal(data, &Bar{})
}

But it outputs unexpected:

Foo: {1h0m0s} [<nil>]
Bar: {{1h0m0s} 0s} [<nil>]

Why Entry value is incorrect?

Thanks for mkopriva. I found out because 'json.Unmarshal' works on any type, it gives me a hint that all type have implemented 'UnmarshalJSON' function, but it's NOT.

The calling of 'json.Unmarshal' on 'Bar' will actually do the following things:

  1. calling UnmarshalJSON on Bar.
  2. Because anonymous struct in Bar don't implemented UnmarshalJSON, so calling UnmarshalJSON on it's embedding struct Foo instead.

That's why 'Entry' in anonymous struct will not be unmarshal.

mkopriva suggest use a custom type to get the custom parsing, but it's inconvenient when you need pass it as argument on functions in the time package. I figure out another way to do it (just unmarshal twice, see https://play.golang.org/p/2ahDX-0hsRt):

func (t *Bar) UnmarshalJSON(data []byte) error {
    type Alias Bar
    if err := json.Unmarshal(data, (*Alias)(t)); err != nil {
        return err
    }
    var tmp struct {
        Entry string `json:"entry"`
    }
    err := json.Unmarshal(data, &tmp)
    t.Entry = timeConvert(tmp.Entry)
    fmt.Printf("Bar: %v [%v]
", *t, err)
    return err
}