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
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
.