Home About Contact
Go , JSON

Go で JSON を ファイルから読み込んで使う(備忘録)

以前にGolang で JSON を Marshal, Unmarshal する というエントリーを書きましたが、その続きです。 ファイルとして用意してある json データを読み込んで使う方法。

環境:

$ go version
go version go1.17 linux/amd64

一番簡単な json データからはじめる

こんな laptop.json があるとする。

{
  "name": "MacBook Air",
  "price": 128000
}

なお、値段は適当です。

それではこれを読み込んで使う go のコードを書きます。 適当なディレクトリを作成(ここでは myjson とします)して、初期化。

$ mkdir myjson
$ cd myjson
$ go mod init myjson
$ touch main.go

main.go を記述。

まずは、単に laptop.json を読み込んで標準出力するところまで。

package main

import (
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	b, err := ioutil.ReadFile("laptop.json")
	if err != nil {
		fmt.Println(os.Stderr, err)
		os.Exit(1)
	}

	lines := string(b)
	fmt.Print(lines)
}

実行。

$ go run main.go
{
  "name": "MacBook Air",
  "price": 128000
}

それでは json を読み取って内容を出力できるようにします。

まず、 laptop.json の内容を表現した Laptop タイプを定義します。

type Laptop struct {
	Name  string `json:"name"`
	Price int    `json:"price"`
}

次にこの Laptop タイプに読み込んだテキストデータをアンマーシャルするコードを追加。

	var aLaptop Laptop
	err1 := json.Unmarshal(b, &aLaptop)
	if err1 != nil {
		fmt.Println(os.Stderr, err)
		os.Exit(1)
	}

json.Unmarshal() すると aLaptop が構築される感じ。なんか気持悪いけれど Go の場合だいたいこういうお作法なので慣れるしかないな。

aLaptop が意図通りできているか確認。

	fmt.Print("- " + aLaptop.Name)
	price := fmt.Sprintf("%v", aLaptop.Price)
	fmt.Println(" -> " + price)

最後に、json のモジュール( encoding/json )を使ったので、それを先頭の import に追加しておきます。

コード全体:

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
)

type Laptop struct {
	Name  string `json:"name"`
	Price int    `json:"price"`
}

func main() {
	b, err := ioutil.ReadFile("laptop.json")
	if err != nil {
		fmt.Println(os.Stderr, err)
		os.Exit(1)
	}

	var aLaptop Laptop
	err1 := json.Unmarshal(b, &aLaptop)
	if err1 != nil {
		fmt.Println(os.Stderr, err)
		os.Exit(1)
	}

	fmt.Print("- " + aLaptop.Name)
	price := fmt.Sprintf("%v", aLaptop.Price)
	fmt.Println(" -> " + price)
}

では実行してみる。

$ go run main.go
- MacBook Air -> 128000

うまくいきました。

複数のラップトップを扱う

ここまでは一つのラップトップデータを読んだだけなので、あまり実用的ではない。 複数のラップトップデータの入った json を読むにはどうするか?

まずは、次のような laptopList.json を考える。

{
  "laptopList": [
    {
      "name": "MacBook Air",
      "price": 128000
    },
    {
      "name": "MacBook Pro",
      "price": 248000
    }
  ]
}

json データが複数のラップトップを保持しているので、それ用のタイプ LaptopList タイプを追加します。

type LaptopList struct {
	LaptopList []Laptop `json:"laptopList"`
}

タイプ名が LaptopList でそのフィールド名も LaptopList になっていて気持悪いのですが、とりあえずこれでいきます。

jsonを アンマーシャルする部分のコードを修正します。

	var aLaptopList LaptopList
	err1 := json.Unmarshal(b, &aLaptopList)
	if err1 != nil {
		fmt.Println(os.Stderr, err)
		os.Exit(1)
	}

コード上では Laptop を LaptopList に rename しただけですね。

コード全体はこのようになりました。

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
)

type Laptop struct {
	Name  string `json:"name"`
	Price int    `json:"price"`
}

type LaptopList struct {
	LaptopList []Laptop `json:"laptopList"`
}

func main() {
	b, err := ioutil.ReadFile("laptopList.json")
	if err != nil {
		fmt.Println(os.Stderr, err)
		os.Exit(1)
	}

	var aLaptopList LaptopList
	err1 := json.Unmarshal(b, &aLaptopList)
	if err1 != nil {
		fmt.Println(os.Stderr, err)
		os.Exit(1)
	}

	fmt.Println(aLaptopList)
}

実行。

$ go run main.go
{[{MacBook Air 128000} {MacBook Pro 248000}]}

できました。

雑に fmt.Println(aLaptopList) するのをやめて、きちんとデータを読み取るコードにします。

	for _, aLaptop := range aLaptopList.LaptopList {
		name := aLaptop.Name
		price := fmt.Sprintf("%v", aLaptop.Price)
		fmt.Println("- " + name + " -> " + price)
	}

実行します。

$ go run main.go
- MacBook Air -> 128000
- MacBook Pro -> 248000

価格は適当です(二度目)! MacBook Pro はこんなに安く買えない気がする。

では、最後に実行ファイルをビルドして締めくくりとします。

$ go build

これで myjson という実行ファイルができるので、あとはこれを実行するだけです。

$ ./myjson
- MacBook Air -> 128000
- MacBook Pro -> 248000

以上です。