问题
I'm receiving some data as JSON, but if a object is empty, it does not return a empty struct but a empty string instead, and when unmarshaling, it returns an error.
So instead of data being {"key":{}}
is {"key":""}}
, it does not work even using omitempty field
Example: https://play.golang.org/p/N1iuWBxuo1C
type Store struct {
Title string `json:"title,omitempty"`
Item item `json:"item,omitempty"`
}
type item struct {
Price float32 `json:"price,omitempty"`
Kind string `json:"kind,omitempty"`
}
func main() {
var data1 Store
json1 := []byte(`{"title":"hello world","item":{"price":45.2,"kind":"fruit"}}`)
if err := json.Unmarshal(json1, &data1); err != nil {
log.Println("1, err: ", err)
return
}
log.Printf("data1: %+v\n", data1)
var data2 Store
json2 := []byte(`{"title":"hello world","item":{}}`)
if err := json.Unmarshal(json2, &data2); err != nil {
log.Println("2, err: ", err)
return
}
log.Printf("data2: %+v\n", data2)
var data3 Store
json3 := []byte(`{"title":"hello world","item":""}`)
if err := json.Unmarshal(json3, &data3); err != nil {
log.Println("3, err: ", err)
return
}
log.Printf("data3: %+v\n", data3)
}
回答1:
You can have your item
type implement the json.Unmarshaler interface.
func (i *item) UnmarshalJSON(data []byte) error {
if string(data) == `""` {
return nil
}
type tmp item
return json.Unmarshal(data, (*tmp)(i))
}
https://play.golang.org/p/1TrD57XULo9
回答2:
Create a type like type ItemOrEmptyString item
And implement Unmarshal interface for it to handle your custom case.
func(ies *ItemOrEmptyString)UnmarshalJSON(d []byte) error{
var i item
if string(d) == `""` {
return nil
}
err := json.Unmarshal(d, &i)
*ies = ItemOrEmptyString(i)
return err
}
Full code here
回答3:
This might be a matter of taste, but ""
is a string with zero length. Not an empty object. JSON uses null
to describe something empty. This works:
json3 := []byte(`{"title":"hello world","item":null}`)
if err := json.Unmarshal(json3, &data3); err != nil {
log.Println("3, err: ", err)
return
}
As far as the documentation goes, omitempty
is an encoding option:
The "omitempty" option specifies that the field should be omitted from the encoding if the field has an empty value, defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string.
json.Unmarshal does not specify any use of the omitempty
tag.
If you don't have control over the input, use an interface type, a type switch and type assertion:
type Store struct {
Title string `json:"title,omitempty"`
Item item `json:"item,omitempty"`
}
type item struct {
Price float32 `json:"price,omitempty"`
Kind string `json:"kind,omitempty"`
}
func unmarshal(js []byte) (*Store, error) {
var data = struct { // Intermediate var for unmarshal
Title string
Item interface{}
}{}
if err := json.Unmarshal(js, &data); err != nil {
return nil, err
}
s := &Store{Title: data.Title}
switch item := data.Item.(type) { // type switch
case string, nil:
return s, nil // Item remains empty
case map[string]interface{}:
p, ok := item["price"].(float64) // assertion
if ok {
s.Item.Price = float32(p)
}
s.Item.Kind, _ = item["kind"].(string) // _ prevents panic
return s, nil
default:
return nil, errors.New("Unknown type")
}
}
func main() {
jsons := [][]byte{
[]byte(`{"title":"hello world","item":{"price":45.2,"kind":"fruit"}}`),
[]byte(`{"title":"hello world","item":{}}`),
[]byte(`{"title":"hello world","item":""}`),
[]byte(`{"title":"hello world","item":null}`),
}
for i, js := range jsons {
data, err := unmarshal(js)
if err != nil {
log.Println("1, err: ", err)
return
}
log.Printf("data %d: %+v\n", i, data)
}
}
https://play.golang.org/p/Dnq1ZVfGPE7
来源:https://stackoverflow.com/questions/61425314/how-to-unmarshal-json-object-if-object-is-returning-as-empty-string-instead-of-e