【问题标题】:JSON field set to null vs field not thereJSON 字段设置为 null vs 字段不存在
【发布时间】:2021-04-26 14:43:06
【问题描述】:

有没有办法在 golang 中查看我是否可以区分设置为 null 的 json 字段与解组为结构时不存在的 json 字段?因为两者都将结构中的值设置为 nil,但我需要知道该字段是否开始存在并查看是否有人将其设置为 null。

{
  "somefield1":"somevalue1",
  "somefield2":null
}

VS

{
  "somefield1":"somevalue1",
}

当解组为结构时,两个 json 都将为 nil。 任何有用的资源将不胜感激!

【问题讨论】:

    标签: json go struct null


    【解决方案1】:

    在决定做某事之前,使用json.RawMessage“延迟”解组过程以确定原始字节:

    var data = []byte(`{
            "somefield1":"somevalue1",
            "somefield2": null
    }`)
    
    type Data struct {
        SomeField1 string          
        SomeField2 json.RawMessage
    }
    
    func main() {
        d := &Data{}
    
        _ = json.Unmarshal(data, &d)
    
        fmt.Println(d.SomeField1)
    
        if len(d.SomeField2) > 0 {
            if string(d.SomeField2) == "null" {
                fmt.Println("somefield2 is there but null")
            } else {
                fmt.Println("somefield2 is there and not null")
                // Do something with the data
            }
        } else {
            fmt.Println("somefield2 doesn't exist")
        }
    }
    

    看操场https://play.golang.org/p/Wganpf4sbO

    【讨论】:

    • 抱歉,我是 Go 新手,我不确定,但如果说 SomeField2 是“你好”,这不是说它是空的吗?
    • @AlexanderTrakhimenok 感谢您按照评论进行编辑。
    • @CetinBasoz 是的,我已经在代码中添加了条件来覆盖它。
    【解决方案2】:

    好问题。

    我相信你可以使用https://golang.org/pkg/encoding/json/#RawMessage作为:

    type MyMessage struct {
      somefield1 string
      somefield2 json.RawMessage
    }
    

    所以在解组后你应该有[]byte("null") 以防nullnil 如果丢失。

    这是一个游乐场代码:https://play.golang.org/p/UW8L68K068

    【讨论】:

      【解决方案3】:

      如果您将对象解组为 map[string]interface{},那么您只需检查字段是否存在

      type unMarshalledObject map[string]interface{}
      json.Unmarshal(input, unMarshalledObject)
      _, ok := unMarshalledObject["somefield2"]
      

      Go Playground

      【讨论】:

      • map[string]interface{} 的使用虽然很有趣,但在这种情况下过于矫枉过正且过于复杂。
      • 同意@pie-o-pah,这将隐藏 somefield1 的类型,但知道它完全可以工作非常有趣。
      【解决方案4】:

      使用自定义类型的另一种方法:

      // OptionalString is a struct that represents a JSON string that can be
      // undefined (Defined == false), null (Value == nil && Defined == true) or 
      // defined with a string value
      type OptionalString struct {
          Defined bool
          Value   *string
      }
      
      // UnmarshalJSON implements the json.Unmarshaler interface.
      // When called, it means that the value is defined in the JSON payload.
      func (os *OptionalString) UnmarshalJSON(data []byte) error {
          // UnmarshalJSON is called only if the key is present
          os.Defined = true
          return json.Unmarshal(data, &os.Value)
      }
      
      
      // Payload represents the JSON payload that you want to represent.
      type Payload struct {
          SomeField1 string         `json:"somefield1"`
          SomeField2 OptionalString `json:"somefield2"`
      }
      

      然后您可以使用常规的json.Unmarshal 函数来获取您的值,例如:

          var p Payload
          _ = json.Unmarshal([]byte(`{
                  "somefield1":"somevalue1",
                  "somefield2":null
              }`), &p)
          fmt.Printf("Should be defined == true and value == nil: \n%+v\n\n", p)
      
      
          p = Payload{}
          _ = json.Unmarshal([]byte(`{"somefield1":"somevalue1"}`), &p)
          fmt.Printf("Should be defined == false \n%+v\n\n", p)
          
          p = Payload{}
          _ = json.Unmarshal([]byte(`{
                  "somefield1":"somevalue1",
                  "somefield2":"somevalue2"
              }`), &p)
          fmt.Printf("Parsed should be defined == true and value != nil \n%+v\n", p)
          if p.SomeField2.Value != nil {
            fmt.Printf("SomeField2's value is %s", *p.SomeField2.Value)
          }
      

      应该给你这个输出:

      Should be defined == true and value == nil: 
      {SomeField1:somevalue1 SomeField2:{Defined:true Value:<nil>}}
      
      Should be defined == false 
      {SomeField1:somevalue1 SomeField2:{Defined:false Value:<nil>}}
      
      Parsed should be defined == true and value != nil 
      {SomeField1:somevalue1 SomeField2:{Defined:true Value:0xc000010370}}
      SomeField2's value is somevalue2
      

      带有完整示例的操场链接:https://play.golang.org/p/AUDwPKHBs62

      请注意,您需要为每种要包装的类型创建一个结构,因此,如果您需要一个可选数字,则需要创建一个 OptionalFloat64(所有 JSON 数字都可以64 位浮点数)具有类似实现的结构。 如果/当泛型出现在 Go 中,这可以简化为单个泛型结构。

      【讨论】:

        【解决方案5】:

        通过使用github.com/golang/protobuf/ptypes/struct 和jsonpb github.com/golang/protobuf/jsonpb,你可以这样做:

        func TestFunTest(t *testing.T) {
            p := &pb.KnownTypes{}
            e := UnmarshalString(`{"val":null}`, p)
            fmt.Println(e, p)
            p = &pb.KnownTypes{}
            e = UnmarshalString(`{"val":1}`, p)
            fmt.Println(e, p)
            p = &pb.KnownTypes{}
            e = UnmarshalString(`{"val":"string"}`, p)
            fmt.Println(e, p)
            p = &pb.KnownTypes{}
            e = UnmarshalString(`{}`, p)
            fmt.Println(e, p)
        }
        

        输出:

        [ `go test -test.run="^TestFunTest$"` | done: 1.275431416s ]
            <nil> val:<null_value:NULL_VALUE > 
            <nil> val:<number_value:1 > 
            <nil> val:<string_value:"string" > 
            <nil> 
            PASS
        

        【讨论】:

          【解决方案6】:

          如果 struct field 是一个指针,如果该字段存在,JSON 解码器将分配新变量,如果不存在则将其留空。所以我建议使用指针。

          type Data struct {
              StrField *string
              IntField *int
          }
          ...
          if data.StrField != nil {
              handle(*data.StrField)
          }
          

          【讨论】:

          • 如果 JSON 中的值为“null”,则该变量的值也将为 nil。
          猜你喜欢
          • 2021-12-28
          • 2016-06-26
          • 2011-05-25
          • 2018-01-04
          • 1970-01-01
          • 1970-01-01
          • 2018-05-24
          • 2012-05-09
          • 1970-01-01
          相关资源
          最近更新 更多