【问题标题】:JSON omitempty With time.Time FieldJSON omitempty 带 time.Time 字段
【发布时间】:2015-09-18 04:20:34
【问题描述】:

尝试 json Marshal 包含 2 个时间字段的结构。但我只希望该字段具有时间价值。所以我正在使用json:",omitempty",但它不起作用。

我可以将 Date 值设置为什么,以便 json.Marshal 将其视为空(零)值而不将其包含在 json 字符串中?

游乐场:http://play.golang.org/p/QJwh7yBJlo

实际结果:

{"时间戳":"2015-09-18T00:00:00Z","日期":"0001-01-01T00:00:00Z"}

期望的结果:

{"时间戳":"2015-09-18T00:00:00Z"}

代码:

package main

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

type MyStruct struct {
    Timestamp time.Time `json:",omitempty"`
    Date      time.Time `json:",omitempty"`
    Field     string    `json:",omitempty"`
}

func main() {
    ms := MyStruct{
        Timestamp: time.Date(2015, 9, 18, 0, 0, 0, 0, time.UTC),
        Field:     "",
    }

    bb, err := json.Marshal(ms)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(bb))
}

【问题讨论】:

标签: json go time struct


【解决方案1】:

omitempty 标签选项不适用于time.Time,因为它是struct。结构有一个“零”值,但这是一个结构值,其中所有字段都有其零值。这是一个“有效”值,因此不会被视为“空”。

但只需将其更改为指针:*time.Time,它就可以工作(nil 指针在 json 编组/解组时被视为“空”)。所以在这种情况下无需编写自定义Marshaler

type MyStruct struct {
    Timestamp *time.Time `json:",omitempty"`
    Date      *time.Time `json:",omitempty"`
    Field     string     `json:",omitempty"`
}

使用它:

ts := time.Date(2015, 9, 18, 0, 0, 0, 0, time.UTC)
ms := MyStruct{
    Timestamp: &ts,
    Field:     "",
}

输出(根据需要):

{"Timestamp":"2015-09-18T00:00:00Z"}

Go Playground 上试试。

如果您不能或不想将其更改为指针,您仍然可以通过实现自定义MarshalerUnmarshaler 来实现您想要的。如果这样做,您可以使用Time.IsZero() 方法来确定time.Time 值是否为零值。

【讨论】:

  • 这是一个很好的答案。谢谢!
  • 感觉这个答案救了我的命。
【解决方案2】:

您可以为自定义编组格式定义自己的时间类型,并在任何地方使用它time.Time

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

package main

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

type MyTime struct {
    *time.Time
}

func (t MyTime) MarshalJSON() ([]byte, error) {
    return []byte(t.Format("\"" + time.RFC3339 + "\"")), nil
}

// UnmarshalJSON implements the json.Unmarshaler interface.
// The time is expected to be a quoted string in RFC 3339 format.
func (t *MyTime) UnmarshalJSON(data []byte) (err error) {

    // by convention, unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
    if bytes.Equal(data, []byte("null")) {
        return nil
    }

    // Fractional seconds are handled implicitly by Parse.
    tt, err := time.Parse("\""+time.RFC3339+"\"", string(data))
    *t = MyTime{&tt}
    return
}

func main() {
    t := time.Now()
    d, err := json.Marshal(MyTime{&t})
    fmt.Println(string(d), err)
    var mt MyTime
    json.Unmarshal(d, &mt)
    fmt.Println(mt)
}

【讨论】:

    【解决方案3】:

    作为 icza 的回答的后续行动,这里有一个自定义编组器,它省略了一个空的日期字段,但保持其余字段不变。

    func (ms *MyStruct) MarshalJSON() ([]byte, error) {
        type Alias MyStruct
        if ms.Timestamp.IsZero() {
            return json.Marshal(&struct {
                Timestamp int64 `json:",omitempty"`
                *Alias
            }{
                Timestamp: 0,
                Alias:     (*Alias)(ms),
            })
        } else {
            return json.Marshal(&struct {
                *Alias
            }{
                Alias: (*Alias)(ms),
            })
        }
    }
    

    这是从http://choly.ca/post/go-json-marshalling/借来的

    OPs 案例有两个时间字段,这会使其复杂得多。 (您必须检查两者都不为空!)

    可能有更好的方法来实现这一点,所以欢迎 cmets。

    【讨论】:

      猜你喜欢
      • 2013-06-22
      • 1970-01-01
      • 2022-08-04
      • 2021-12-04
      • 2021-09-20
      • 2013-01-15
      • 2018-06-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多