dongwei1234 2019-05-27 10:29
浏览 155
已采纳

JSON解组中缺失字段和空字段之间的区别

So I have this struct in Go:

type Car struct {
    Name  string `json:"name"`
    Speed int    `json:"speed"`
}

And I have two JSON samples that I unmarshal:

str := `{"name": "", "speed": 0}`
strTwo := `{}`

I do the unmarshaling in this way:

car := Car{}
_ = json.Unmarshal([]byte(str), &car)

carTwo := Car{}
_ = json.Unmarshal([]byte(strTwo), &carTwo)

Now because of the way Go deals with default value types, when I try to print the structure, I get the same result:

car - { 0}
carTwo - { 0}

So I can't see the difference between a missing value in JSON and when a default value is passed. How can I solve this problem?

One way is to use pointers in struct:

type Car struct {
    Name  *string `json:"name"`
    Speed *int    `json:"speed"`
}

But I get a very ugly code when using this values, I have to do pointer dereferencing everywhere

  • 写回答

1条回答 默认 最新

  • douxian0008 2019-05-27 10:43
    关注

    Go's primitive data types are not suitable to handle the "all valid values" and an additional "is present" information.

    If you do need this, one way is to use pointers, where the nil pointer value corresponds to the "missing" state.

    If it is uncomfortable to work with pointers afterwards, do a "post processing": convert your structs with pointer fields to a struct value with non-pointer fields, so you can work with that later on.

    You may do this "manually", or write a custom unmarshaler to make this happen automatically.

    Here's an example how to do it:

    type PCar struct {
        Name  *string `json:"name"`
        Speed *int    `json:"speed"`
    }
    
    type Car struct {
        Name  string `json:"-"`
        Speed int    `json:"-"`
        PCar
    }
    
    func (c *Car) UnmarshalJSON(data []byte) error {
        if err := json.Unmarshal(data, &c.PCar); err != nil {
            return err
        }
    
        if c.PCar.Name != nil {
            c.Name = *c.PCar.Name
        }
        if c.PCar.Speed != nil {
            c.Speed = *c.PCar.Speed
        }
        return nil
    }
    

    Example using it:

    sources := []string{
        `{"name": "", "speed": 0}`,
        `{}`,
        `{"name": "Bob", "speed": 21}`,
    }
    
    for i, src := range sources {
        var c Car
        if err := json.Unmarshal([]byte(src), &c); err != nil {
            panic(err)
        }
        fmt.Println("car", i, c)
    }
    

    Output (try it on the Go Playground):

    car 0 { 0 {0x40c200 0x4140ac}}
    car 1 { 0 {<nil> <nil>}}
    car 2 {Bob 21 {0x40c218 0x41410c}}
    

    As you can see, car 1 contains 2 non-nil pointers, because the respective fields were present in the input JSON, while car 2 contains 2 nil pointers, because those fields were missing in its input. You may use Car.Name and Car.Speed fields as non-pointers (because they are not pointers). To tell if they were present in the input, you may check the corresponding pointers Car.PCar.Name and Car.PCar.Speed if they are nil.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器