【问题标题】:Assigning null to JSON fields instead of empty strings将 null 分配给 JSON 字段而不是空字符串
【发布时间】:2021-05-07 20:13:23
【问题描述】:

由于空字符串是 Go string 的零/默认值,因此我决定将所有此类字段定义为 interface{}。例如

type student struct {
    FirstName  interface{} `json:"first_name"`
    MiddleName interface{} `json:"middle_name"`
    LastName   interface{} `json:"last_name"`
}

如果该特定字段的值不可用,我发送数据的应用程序需要一个 null 而不是空字符串。

这是正确的方法还是有人可以指出比这更好的方法。

【问题讨论】:

标签: go


【解决方案1】:

json package documentation

指针值编码为指向的值。 nil 指针编码为 null JSON 对象。

所以你可以存储一个指向字符串的指针,如果不是 nil,它将被编码为字符串,如果为 nil,则将被编码为“null”

type student struct {
  FirstName  *string `json:"first_name"`
  MiddleName *string `json:"middle_name"`
  LastName   *string `json:"last_name"`
}

【讨论】:

  • 我确实尝试过这种方法。这种方法和界面一相比是否有任何优势?
  • 我会说可读性和一致性。很明显,通过指向字符串的方法,您知道 var 是关于什么的。使用接口告诉编译器 FirstName 可以是任何东西,并为错误留出空间
  • 建议的方法不适用于其他类型,如数字。有什么建议吗?
  • @stanley_manley 这种方法对我来说非常适合float64
【解决方案2】:

实际上,另一种方法是使用 golang 的 json 库提供的 MarhshalJSON 和 UnmarshalJSON 接口方法。代码如下:

type MyType string
type MyStruct struct {
    A MyType `json:"my_type"`
}

func (c MyType) MarshalJSON() ([]byte, error) {
    var buf bytes.Buffer
    if len(string(c)) == 0 {
        buf.WriteString(`null`)
    } else {
        buf.WriteString(`"` + string(c) + `"`)   // add double quation mark as json format required
    }
    return buf.Bytes(), nil
}

func (c *MyType)UnmarshalJSON(in []byte) error {
    str := string(in)
    if str == `null` {
        *c = ""
        return nil
    }
    res := MyType(str)
    if len(res) >= 2 {
        res = res[1:len(res)-1]     // remove the wrapped qutation
    }
    *c = res
    return nil
}

那么在使用 json.Marshal 时,MyType 值将被封送为 null。

【讨论】:

    【解决方案3】:

    可以使用https://github.com/guregu/null

    type student struct {
    FirstName  null.String `json:"first_name"`
    MiddleName null.String `json:"middle_name"`
    LastName   null.String `json:"last_name"`}
    

    【讨论】:

    • @Dragonthoughts 异地资源?他正在链接到 Github 上的一个项目,这是非常合适的做法。
    • @Rob_vH 当特定的 github 存储库被删除或设为私有时会发生什么?答案丢失了重要内容。
    • 没错,但是如果 github repo 消失了,go 库也会随之消失
    【解决方案4】:

    对于带有空字符串的 json 对象,最简单的方法是在字段上使用 omitempty 装饰器。

    type student struct {
      FirstName  string `json:"first_name,omitempty"`
      MiddleName string `json:"middle_name,omitempty"`
      LastName   string `json:"last_name"`
    }
    

    通过上述声明,只有指定了 first_name 时,该键才会显示在生成的 json 中。另一方面,如果未分配,last_name 将始终以 "" 的值显示在结果中。

    现在,当您开始包含数字字段(其中 0 可能是一个值)时,使用 omitempty 不会达到预期的效果。 0 值总是丢弃该字段,我们需要能够区分 0 值和未分配的值。这里建议使用 https://github.com/guregu/null 等库。

    更多讨论在这里:https://www.sohamkamani.com/blog/golang/2018-07-19-golang-omitempty/

    【讨论】:

      【解决方案5】:

      你可以使用sql.NullString之类的东西,使用'Valid'来检查它是否是一个零值。

      NullString 表示一个可能为空的字符串。 NullString 实现 Scanner 界面,因此它可以用作扫描目标:

      type NullString struct {
          String string
          Valid  bool // Valid is true if String is not NULL
      }
      
      var s NullString
      err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
      ...
      if s.Valid {
         // use s.String
      } else {
         // NULL value
      }
      

      不要使用如下指针:

      type student struct {
        FirstName  *string `json:"first_name"`
        MiddleName *string `json:"middle_name"`
        LastName   *string `json:"last_name"`
      }
      

      因为您需要检查 nil 值并在任何地方取消引用,并且可能会在某处导致意外崩溃:

      if obj.FirstName != nil {
         fmt.Print("%s", *obj.FirstName)
      }
      

      我比较了两种解决方案,并在我的大量生产代码中选择了前一种方法......它工作正常。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-02
        • 2021-07-22
        • 2016-07-29
        • 2021-04-24
        • 2021-04-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多