Golang - JSON字段设置为null vs field not there

Nig*_*hee 12 null json struct go

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

{
  "somefield1":"somevalue1",
  "somefield2":null
}
Run Code Online (Sandbox Code Playgroud)

VS

{
  "somefield1":"somevalue1",
}
Run Code Online (Sandbox Code Playgroud)

当编组成结构时,两个jsons都将为nil.任何有用的资源将非常感谢!

Sos*_*doc 16

如果您使用的是 Go 1.18+,您可以使用一个简单的通用结构来了解 JSON 值何时为undefinedor null

type Optional[T any] struct {
    Defined bool
    Value   *T
}

// UnmarshalJSON is implemented by deferring to the wrapped type (T).
// It will be called only if the value is defined in the JSON payload.
func (o *Optional[T]) UnmarshalJSON(data []byte) error {
    o.Defined = true
    return json.Unmarshal(data, &o.Value)
}
Run Code Online (Sandbox Code Playgroud)

就这样,您可以在结构中使用这种类型:

type Payload struct {
    Field1 Optional[string] `json:"field1"`
    Field2 Optional[bool]   `json:"field2"`
    Field3 Optional[int32]  `json:"field3"`
}
Run Code Online (Sandbox Code Playgroud)

您将能够使用该Defined字段来了解该字段是否是nullundefined

检查此游乐场链接以获取完整示例: https: //go.dev/play/p/JZfZyVVUABz

原始答案(预泛型)

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

// 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"`
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用常规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)
    }
Run Code Online (Sandbox Code Playgroud)

应该给你这个输出:

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
Run Code Online (Sandbox Code Playgroud)

链接到包含完整示例的游乐场:https ://play.golang.org/p/AUDwPKHBs62

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


Pie*_*Pah 10

使用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")
    }
}
Run Code Online (Sandbox Code Playgroud)

查看游乐场https://play.golang.org/p/Wganpf4sbO


Ben*_*ish 5

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

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

去游乐场

  • 使用“map[string]interface{}”虽然很有趣,但在这种情况下是一种矫枉过正且过于复杂的情况。 (3认同)

Ale*_*nok 5

好问题。

我相信您可以将https://golang.org/pkg/encoding/json/#RawMessage用作:

type MyMessage struct {
  somefield1 string
  somefield2 json.RawMessage
}
Run Code Online (Sandbox Code Playgroud)

因此,解组后,你应该[]byte("null")在的情况下,nullnil如果它丢失了。

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