Как декодировать JSON в Go, который возвращает несколько элементов как массив типа и отдельные элементы как тип
Я работаю с API, который отправляет данные JSON. Проблема в том, что массив из одного элемента отображается как одно значение. Например, рассмотрим следующий JSON:
{ "names": ["Alice","Bob"] }
API отправляет это как массив. Но когдаnames
имеет один элемент, API отправляет это:
{ "names": "Alice" }
Вот как я обычно расшифровываю этот ответ в Go:
type Response struct {
Names []string `json:"names"`
}
// later
d := &Response{}
_ = json.NewDecoder(resp).Decode(d) // resp would be a http.Response.Body containing the problematic JSON
Go правильно декодирует первый JSON. Однако после декодирования второго JSON объект содержит пустой массив.
У меня нет никакого контроля над API, поэтому мне нужно решить эту проблему. Как я мог правильно декодировать этот JSON в Go, чтобыNames
срез содержит единственный элемент? Спасибо за помощь.
3 ответа
Вы могли бы реализовать json.Unmarshaler
интерфейса и пусть он проверяет 0-й необработанный байт на предмет [
или "
чтобы узнать, массив это или строка соответственно:
type StringSlice []string
func (ss *StringSlice) UnmarshalJSON(data []byte) error {
if data[0] == '[' {
return json.Unmarshal(data, (*[]string)(ss))
} else if data[0] == '"' {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
*ss = append(*ss, s)
}
return nil
}
Вам нужно будет расшифровать это в interface{}
а затем используйте утверждение типа, чтобы проверить, является ли базовый тип фрагментом или просто строкой.
type Response struct {
Names interface{} `json:"names"`
}
Затем после декодирования в d
, вы бы сделали что-то вроде:
slice, ok := d.Names.([]interface{})
if ok {
// it was a slice. use it.
} else {
// it wasn't a slice - so expect it to be a string
// and use that, etc.
}
Использовать json.RawMessage
как тип. RawMessage просто задерживает декодирование части сообщения, чтобы мы могли сделать это сами позже.
type Response struct {
NamesRaw json.RawMessage `json:"names"`
Names []string
}
Сначала расшифруйте ответ, а затем используя json.Unmarshal
расшифровать json.RawMessage
x := &Response{}
_ = json.NewDecoder(resp).Decode(x);
x.Names = DecodeName(x.NamesRaw)
DecodeName
используется для декодирования NameRaw
данные
func DecodeName(nameRaw json.RawMessage) (data []string) {
var s string
if err := json.Unmarshal(nameRaw, &s); err == nil {
v := []string{s}
return v
}
var sn []string
if err := json.Unmarshal(nameRaw, &sn); err == nil {
return sn
}
return
}