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