write ahead log

ロールフォワード用

golangでJSONをパースする

いろいろな所に書かれているけど,ハマってしまったので書き残しておく.

golangでJSONをパースするにはencoding/jsonを利用すれば良い.

静的言語だけあってちょっと面倒なところがあって,JSONの構造に合わせて前もってstructを定義しておく必要がある.

基本

一番単純な例だと以下のようなJSONを考える.

{
    "name":"twinbird",
    "age":27,
    "sex":"man"
}

これをパースして表示しようとすると以下のようなコードになる.

package main

import (
    "encoding/json"
    "fmt"
)

const (
    json1 = `
      {
          "name":"twinbird",
          "age":27,
          "sex":"man"
      }`
)

type Person struct {
    Name string `json:"name"`
    Age int `json:"age"`
    Sex string `json:"sex"`
}

func main() {
    bytes := []byte(json1)
    var p Person
    err := json.Unmarshal(bytes, &p)
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    fmt.Printf("Name: %v, Age: %v, Sex: %v\n",
        p.Name, p.Age, p.Sex)
}

結果は以下になる.

Name: twinbird, Age: 27, Sex: man

ここまでは割と単純だ.

大事なのはstructにタグ(`で囲んだやつ)を付けてJSONのキーに合わせてやること.

タグは単なるメタデータらしいのでライブラリに知らせるためだけに使われることになる.
(大文字,小文字に注意.また,キーはダブルクオーテーションで囲む必要がある.)

ところでタグの付け方がおかしいかを確認するコマンドがある

$ go vet

コンパイル前におかしい箇所を色々指摘してくれる.

配列

次にコレを応用して配列をパースする.
実は次のネストした関係よりこちらの方にハマってしまった. 大したことではないのだが,人間一度視野が狭くなると中々抜け出せない.

package main

import (
    "encoding/json"
    "fmt"
)

const (
    json1 = `
  [
      {
          "name":"twinbird",
          "age":27,
          "sex":"man"
      },
      {
          "name":"taro",
          "age":29,
          "sex":"man"
      }
  ]`
)

type Person struct {
    Name string `json:"name"`
    Age int `json:"age"`
    Sex string `json:"sex"`
}

func main() {
    bytes := []byte(json1)
    var people []Person
    err := json.Unmarshal(bytes, &people)
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    for _, person := range people {
        fmt.Printf("Name: %v, Age: %v, Sex: %v\n",
            person.Name, person.Age, person.Sex)
    }
}

ほとんど変わっていない.
だが,とても大事なことはjson.Unmarshalに渡す変数がスライスになった事だ.

やってしまえば当たり前なのだが,随分ハマってしまった.

実行結果は以下

Name: twinbird, Age: 27, Sex: man
Name: taro, Age: 29, Sex: man

ネスト

ネストしたデータは構造体のネストで表す.

package main

import (
    "encoding/json"
    "fmt"
)

const (
    json1 = `
  {
      "company_name":"FooSystem",
      "employees":[
          {
              "name":"twinbird",
              "age":27,
              "sex":"man"
          },
          {
              "name":"taro",
              "age":29,
              "sex":"man"
          }
      ]
  }`
)

type Company struct {
    Name string `json:"company_name"`
    Employees []Person `json:"employees"`
}

type Person struct {
    Name string `json:"name"`
    Age int `json:"age"`
    Sex string `json:"sex"`
}

func main() {
    bytes := []byte(json1)
    var c Company
    err := json.Unmarshal(bytes, &c)
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    fmt.Println(c.Name)
    for _, person := range c.Employees {
        fmt.Printf("Name: %v, Age: %v, Sex: %v\n",
            person.Name, person.Age, person.Sex)
    }
}

これも割と素直.

ちょっと楽する

ところで構造体にタグを書いていましたが実は書かなくて良い状況があります.

JSONのキーが構造体名と完全一致,または大文字小文字の差だけであれば勝手にマッピングしてくれます.

最初の例(タグ無し版)

package main

import (
    "encoding/json"
    "fmt"
)

const (
    json1 = `
      {
          "name":"twinbird",
          "age":27,
          "sex":"man"
      }`
)

type Person struct {
    Name string
    Age int
    Sex string
}

func main() {
    bytes := []byte(json1)
    var p Person
    err := json.Unmarshal(bytes, &p)
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    fmt.Printf("Name: %v, Age: %v, Sex: %v\n",
        p.Name, p.Age, p.Sex)
}

結果は以下になる.

Name: twinbird, Age: 27, Sex: man

まとめ

  • JSONのパースには構造体にタグ付けをしてUnmarshal
  • 配列は構造体を素直にスライスにする
  • ネストも構造体を素直にネストする
  • 実はJSONのキーと構造体名が同じならタグ付け不要