goでjsonの一部のデータだけを後から型を指定して取り出したい
例えばAPIなんかでたまにあるこう言う形。 これを受け取った場合、共通処理としてまずはerrorがあるかどうかを見て、もしerrorがなかった場合にはdataをそのAPIに合わせた型にして取り出したいとする。
{ "error": null, "data": { "title": "fuga" } }
{ "error": null, "data": { "nickname": "foobar" } }
json.RawMessage
を使うと後からjsonの一部だけを型を指定して取り出すことができる。
まさに今回欲しかったものにぴったり。
golang.org
サンプルはこちら https://play.golang.org/p/e2lW0-15o3P
package main import ( "encoding/json" "fmt" "log" ) var userJSON = `{ "error": null, "data": { "nickname": "hoge" } }` var todoJSON = `{ "error": null, "data": { "title": "fuga" } }` type Response struct { Error *ResponseError `json:"error,omitempty"` Data json.RawMessage `json:"data,omitempty"` // <- User か Todo になる } type ResponseError struct { Code int `json:"code,omitempty"` Message string `json:"message,omitempty"` } type User struct { Nickname string `json:"nickname,omitempty"` } type Todo struct { Title string `json:"title,omitempty"` } func parseResponse(data []byte) (*Response, error) { res := &Response{} if err := json.Unmarshal(data, res); err != nil { return nil, err } if res.Error != nil { return nil, fmt.Errorf("error response: %v\n", res.Error) } return res, nil } func main() { // User APIにアクセスした userRes, err := parseResponse([]byte(userJSON)) if err != nil { log.Fatalf("parse error: %v", err) } user := &User{} if err := json.Unmarshal(userRes.Data, user); err != nil { log.Fatalf("error: %v", err) } fmt.Printf("user: %+v\n", user) // TODO APIにアクセスした todoRes, err := parseResponse([]byte(todoJSON)) if err != nil { log.Fatalf("parse error: %v", err) } todo := &Todo{} if err := json.Unmarshal(todoRes.Data, todo); err != nil { log.Fatalf("error: %v", err) } fmt.Printf("todo: %+v\n", todo) }