- golangでjsonをparseする時、いくつか方法があるので、パターン別に自分の使い分けを解説。
jsonテキストをstructの変数に流し込む(json.Unmarshal)
- jsonテキストをパースする一番よくあるパターン。まあ現実的には同じ関数内で作ったjsonテキストをparseなどしないが…
package main
import (
"encoding/json"
"fmt"
)
type jsonData struct {
Name string `json:"name"`
Num int `json:"num"`
}
func main() {
jsonStr := `{"name":"Apple","num":3}`
jsonBytes := []byte(jsonStr)
var d jsonData
json.Unmarshal(jsonBytes, &d)
fmt.Println(d.Name, d.Num)
}
io.Readerから1回でstructの変数に流し込む場合(json.NewDecoder)
- よくあるパターン。例えばサーバのapiに
http.Get
でリクエストして、Response typeのBody fieldをparseするような場合だ。 - サンプルとかも多いので解説の必要はないだろう。シンプルに以下でいい。
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type respBody struct {
Result int
Message string
}
func main() {
resp, err := http.Get("どっかのAPI")
var data respBody
err = json.NewDecoder(resp.Body).Decode(&data)
if err != nil {
fmt.Println(err.Error())
}
}
- このパターンの場合、
resp.Body
はio.Reader
で読み込みは一度しかできない。
io.Readerから複数回でstructの変数に流し込む場合(json.Unmarshal, json.RawMessage)
- APIからのresponseをparseする場合に割とあるのがこれ。responseの構造がapiの結果によって変わる場合などだ。200応答と、500応答とでpayloadの構造が違う…とか。
- この場合、一部のfieldだけparseしてから残りを処理分岐したい。
- io.Readerのままだと複数回読み込みはできないので、[]byteなどでデータを持っておく必要があるが、加えて一度parseしたfieldを再度parseするというのはどうにもダサい。
- 以下のようにparseを遅らせたい項目を
json.RawMessage
にしておくと、後からjson.Unmarshal
を行うことができる。 -
json.RawMessage
は[]byte
を型定義したものなので、そのままjson.Unmarshal
で処理可能だ。
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type respBody struct {
Result string `json:"result"`
Payload json.RawMessage `json:"payload"`
}
type respPayloadOK struct {
Message string `json:"message"`
}
type respPayloadErr struct {
ErrCode int `json:"errCode"`
ErrMessage string `json:"errMessage"`
}
func main() {
resp, err := http.Get("どっかのAPI")
bodyBytes, err := ioutil.ReadAll(resp.Body)
var data respBody
if err = json.Unmarshal(bodyBytes, &data); err != nil {
fmt.Println(err.Error())
return
}
if data.Result != "ok" {
var payloadErr respPayloadErr
if err = json.Unmarshal(data.Payload, &payloadErr); err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(payloadErr.ErrCode, payloadErr.ErrMessage)
return
}
var payload respPayloadOK
if err = json.Unmarshal(data.Payload, &payload); err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(payload.Message)
}