go でjson をパースする(構造体あり、なしパターン)

はじめに

go でjson をパースする方法をまとめます。
構造体を使うパターンとbuger/jsonparser を利用して構造体を使わないパターンを書きます。

github.com

go でjson をパースする

個人的には構造体を書けばいいと思ってますが、めんどくさいなぁと思うこともあるかもしれません。ないかもしれません。ないですね。書きましょう。

今回利用するjson はこんな感じです。

{
        "person": {
          "name": {
            "first": "Leonid",
            "last": "Bugaev",
            "fullName": "Leonid Bugaev"
          },
          "github": {
            "handle": "buger",
            "followers": 109
          },
          "avatars": [
            { "url": "https://avatars1.githubusercontent.com/u/14009?v=3&s=460", "type": "thumbnail" },
            { "url": "https://avatars1.githubusercontent.com/u/14010?v=2&s=460", "type": "thumbnail" }      ]
        },
        "company": {
          "name": "Acme"
        }
}

構造体の生成といえばここですね。

構造体を使ってjson をパースする

構造体を使ってjson をパースします。
ちなみに例えば外部サービスのAPIで予期せず項目が追加になるかもしれなくて不安。みたいなお話がありますが、増える分にはエラーにもなりません。

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type Employee struct {
    Person struct {
        Name struct {
            First    string `json:"first"`
            Last     string `json:"last"`
            FullName string `json:"fullName"`
        } `json:"name"`
        Github struct {
            Handle    string `json:"handle"`
            Followers int    `json:"followers"`
        } `json:"github"`
        Avatars []struct {
            URL  string `json:"url"`
            Type string `json:"type"`
        } `json:"avatars"`
    } `json:"person"`
    // Company struct {
    //     Name string `json:"name"`
    // } `json:"company"`
}

func main() {
    data := []byte(`{
      "person": {
        "name": {
          "first": "Leonid",
          "last": "Bugaev",
          "fullName": "Leonid Bugaev"
        },
        "github": {
          "handle": "buger",
          "followers": 109
        },
        "avatars": [
          { "url": "https://avatars1.githubusercontent.com/u/14009?v=3&s=460", "type": "thumbnail" },
          { "url": "https://avatars1.githubusercontent.com/u/14010?v=2&s=460", "type": "thumbnail" }
        ]
      },
      "company": {
        "name": "Acme"
      }
  }`)

    // Use struct
    var employee Employee
    if err := json.Unmarshal(data, &employee); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Employee Person FullName: %s\n", employee.Person.Name.FullName)
    for _, a := range employee.Person.Avatars {
        fmt.Printf("Employee Person Avatars: %s, %s\n", a.URL, a.Type)
    }
}
$ go run jsonparser.go
Employee Person FullName: Leonid Bugaev
Employee Person Avatars: https://avatars1.githubusercontent.com/u/14009?v=3&s=460, thumbnail
Employee Person Avatars: https://avatars1.githubusercontent.com/u/14010?v=2&s=460, thumbnail

構造体を使わずjson をパースする

構造体を使わずにjson をパースします。
buger/jsonparser を利用します。

package main

import (
    "fmt"
    "log"

    "github.com/buger/jsonparser"
)

func main() {
    data := []byte(`{
      "person": {
        "name": {
          "first": "Leonid",
          "last": "Bugaev",
          "fullName": "Leonid Bugaev"
        },
        "github": {
          "handle": "buger",
          "followers": 109
        },
        "avatars": [
          { "url": "https://avatars1.githubusercontent.com/u/14009?v=3&s=460", "type": "thumbnail" },
          { "url": "https://avatars1.githubusercontent.com/u/14010?v=2&s=460", "type": "thumbnail" }
        ]
      },
      "company": {
        "name": "Acme"
      }
  }`)

    // Not use struct
    value, _, _, err := jsonparser.Get(data, "person", "name", "fullName")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Employee Person FullName: %s\n", string(value))

    _, err = jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
        url, _, _, err := jsonparser.Get(value, "url")
        if err != nil {
            log.Fatal(err)
        }
        urlType, _, _, err := jsonparser.Get(value, "type")
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Employee Person Avatars: %s, %s\n", string(url), string(urlType))
    }, "person", "avatars")
    if err != nil {
        log.Fatal(err)
    }
}

配列内の要素へのアクセスが少し手間ですが構造体なしにここまでできるのはすごいですね。

$ go run jsonparser.go
Employee Person FullName: Leonid Bugaev
Employee Person Avatars: https://avatars1.githubusercontent.com/u/14009?v=3&s=460, thumbnail
Employee Person Avatars: https://avatars1.githubusercontent.com/u/14010?v=2&s=460, thumbnail

まとめ

go でjson をパースする方法をまとめました。
技術書展に行って私の中のgo熱の高まりを感じます。