как демаршалировать объект json, если объект возвращается как пустая строка вместо пустой структуры
Я получаю некоторые данные в формате JSON, но если объект пуст, он возвращает не пустую структуру, а пустую строку, а при демаршалинге возвращает ошибку.
Поэтому вместо того, чтобы быть {"key":{}}
является {"key":""}}
, это не работает даже при использовании пустого поля
Пример: 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)
}
3 ответа
Вы можете получить свой item
тип реализовать json.Unmarshaler
интерфейс.
func (i *item) UnmarshalJSON(data []byte) error {
if string(data) == `""` {
return nil
}
type tmp item
return json.Unmarshal(data, (*tmp)(i))
}
Создайте тип вроде type ItemOrEmptyString item
И реализуйте интерфейс Unmarshal, чтобы он мог обрабатывать ваш индивидуальный случай.
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
}
Полный код здесь
Это может быть дело вкуса, но ""
это строка нулевой длины. Не пустой объект. JSON используетnull
описать что-то пустое. Это работает:
json3 := []byte(`{"title":"hello world","item":null}`)
if err := json.Unmarshal(json3, &data3); err != nil {
log.Println("3, err: ", err)
return
}
Что касается документации,omitempty
это вариант кодировки:
Параметр "omitempty" указывает, что поле должно быть исключено из кодировки, если поле имеет пустое значение, определенное как false, 0, указатель nil, значение интерфейса nil и любой пустой массив, фрагмент, карту или строку.
json.Unmarshal
не указывает какое-либо использование omitempty
тег.
Если вы не имеете контроля над входом, используйте тип интерфейса, а переключатель типа и утверждение типа:
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)
}
}