Декодирование переменной-схемы JSON в Go
Я прошу об этом Go encoding/json
, но я думаю, что это также относится к любым другим JSON-библиотекам, которые сопоставляют JSON-капли с объектами на любом языке.
Вот пример. Если вы хотите сократить URL-адрес с помощью API goo.gl URL shortener API, вы получите либо успешный ответ:
{
"kind": "urlshortener#url",
"id": "http://goo.gl/fbsS",
"longUrl": "http://www.google.com/"
}
Или ответ об ошибке:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Required",
"locationType": "parameter",
"location": "resource.longUrl"
}
],
"code": 400,
"message": "Required"
}
}
Есть ли идиоматический способ справиться с этим - ответ, который может придерживаться двух совершенно разных схем?
Обычно я имею дело с JSON, используя карты/списки; Я знаю, что это возможно в Го. Я мог бы отключить map[string]interface{}
, а затем проверить, имеет ли карта "error"
в качестве ключа. Но тогда мне придется снова декодировать правильный struct
. (Я не прав?)
Я делаю что-то вроде этого. У меня есть один тип для каждого вида ответа:
type successResponse struct {
Kind string
Id string
LongUrl string
}
type errorResponse struct {
Error struct {
Errors []struct {
Domain string
Reason string
Message string
LocationType string
Location string
}
Code int
Message string
}
}
И декодирование выглядит следующим образом:
s := new(successResponse)
err := json.Unmarshal(blob, s)
if err == nil {
// handle success
} else {
e := new(errorResponse)
err = json.Unmarshal(blob, e)
if err == nil {
// handle error response
} else {
// handle actual error
}
}
Но это кажется уродливым. Как мне подойти к этому?
Ответы
Ответ 1
Поскольку поля в ваших ответах json отличаются друг от друга, вы можете просто создать одну структуру с объединением всех полей. Json-декодер будет игнорировать поля, которые не присутствуют в строке json, и вы можете проверить существование полей, чтобы узнать, какой тип ответа вы возвращаете.
Ответ 2
Я тоже смутился об этом и подумал, что мне придется расшифровать его снова. Но вы этого не сделаете. Вам просто нужно вывести данные интерфейса {} в соответствующую структуру.
Например, если пакет json поместил это значение в общий interface{}
, вы можете преобразовать его в ErrorType
с помощью error := val.(ErrorType)
.
Вы можете использовать foo.(type)
в инструкции switch
для "делать правильную вещь", если вы разбираете на основе того, какой тип имеет значение.
Я изучаю только на этой неделе, так что это не самый красивый код, но есть примеры в geodns JSON-настройка синтаксического анализа.
Ответ 3
Вы пробовали Go-SimpleJSON? Я думаю, это может решить вашу проблему.
Ответ 4
type Response struct {
Kind string
Id string
LongUrl string
Error struct {
Errors []struct {
Domain string
Reason string
Message string
LocationType string
Location string
}
Code int
Message string
}
}
s := Response{}
if err := json.Unmarshal(blob, &s); err == nil {
if s.Error == nil {
// success
} else {
// error
}
} else {
// something went wrong
}