how to unmarshal json object if object is returning as empty string instead of empty struct

六月ゝ 毕业季﹏ 提交于 2021-02-05 11:37:24

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!