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

时间:2018-11-09 15:43:36

标签: json go types marshalling unmarshalling

给出以下结构

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

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

我想自定义time.Duration格式并从json字符串中加载Bar值,例如:

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

因此,我为UnmarshalJSONFoohttps://play.golang.org/p/6v71eG_Xr98)覆盖了Bar

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]\n", *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]\n", *t, err)
    return err
}

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

但是它输出意外:

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

为什么Entry的值不正确?

1 个答案:

答案 0 :(得分:1)

感谢mkopriva。我发现这是因为'json.Unmarshal'适用于任何类型,它提示我所有类型均已实现'UnmarshalJSON'函数,但并非如此。

在'Bar'上调用'json.Unmarshal'实际上将执行以下操作:

  1. UnmarshalJSON上呼叫Bar
  2. 由于Bar中的匿名结构未实现UnmarshalJSON,因此在其上调用UnmarshalJSON会嵌入结构Foo

这就是为什么匿名结构中的“条目”不会被解组的原因。

mkopriva建议使用自定义类型来获取自定义解析,但是当您需要将其作为参数传递给time包中的函数时,这样做很不方便。我想出了另一种方法(只需解组两次,请参阅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]\n", *t, err)
    return err
}