Help us understand the problem. What is going on with this article?

golangのjson parseについて(marshal/decode/json.RawMessage)

More than 1 year has passed since last update.
  • 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.Bodyio.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)
}
sochiai
freelance. engineer. node.js, golang, etc...
https://sochiai.dev/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした