Немаршальное поле вложенной структуры не работает - PullRequest
0 голосов
/ 09 ноября 2018

Учитывая следующие структуры

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

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

Я хочу настроить time.Duration формат и загрузить Bar значение из строки json, например:

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

Поэтому я переопределяю UnmarshalJSON для Foo и 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]\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 голосов
/ 10 ноября 2018

Спасибо за mkopriva. Я узнал, потому что json.Unmarshal работает с любым типом, он подсказывает мне, что во всех типах реализована функция UnmarshalJSON, но это НЕ.

Вызов 'json.Unmarshal' на 'Bar' фактически сделает следующие вещи:

  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
}
...