はじめに
最近Nvimを使ってみたんですが、Nvimの設定ファイルであるinit.vimを編集するのに使い慣れているvsCodeVimを使いました。いつかNvimを使いこなしたいですまる
さて、バックエンドの勉強をしたくて最近Go言語に入門してみました。購入したGOの書籍を読み終えて何か書きたくなったところで、JavascriptでやってたことをGoではどうやっているのか確かめてみようと思いました。
ということでGoで簡単なApiを叩いてみました。
書いてあること
- Unmarshalでjsonを解析する方法
- encoderで解析する方法
叩くApi
定番のOpen Weather Map です。
まず、どんなJsonが返ってくるか確認します。
curl http://api.openweathermap.org/data/2.5/weather?q=nagano -H "X-API-KEY: YOUR API KEY" | jq
こんなん↓が返ってくると思います。
{
"coord": {
"lon": 138.1811,
"lat": 36.6514
},
"weather": [
{
"id": 804,
"main": "Clouds",
"description": "overcast clouds",
"icon": "04n"
}
],
"base": "stations",
"main": {
"temp": 276.93,
"feels_like": 274.22,
"temp_min": 275.37,
"temp_max": 278.15,
"pressure": 1024,
"humidity": 67
},
"visibility": 10000,
"wind": {
"speed": 0.69,
"deg": 182
},
"clouds": {
"all": 100
},
"dt": 1613130091,
"sys": {
"type": 3,
"id": 19237,
"country": "JP",
"sunrise": 1613079554,
"sunset": 1613118238
},
"timezone": 32400,
"id": 1856215,
"name": "Nagano",
"cod": 200
}
Unmarshalを使う
まずは全体のコードをぱっとみ↓
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
)
var (
Key string
)
// 構造体のタグによりjsonと結びつける
type Data struct {
Weather []Weather `json:"weather"`
Main Main `json:"main"`
}
type Weather struct {
ID int `json:"id"`
Main string `json:"main"`
Description string `json:"description"`
ICON string `json:"icon"`
}
type Main struct {
Temp float32 `json:"temp"`
FeelsLike float32 `json:"feels_like"`
TempMin float32 `json:"temp_min"`
TempMax float32 `json:"temp_max"`
Pressure int `json:"pressure"`
Humidity int `json:"humidity"`
}
// コマンドラインの引数を取得
func init() {
flag.StringVar(&Key, "key", "", "Api key")
flag.Parse()
}
func main() {
url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=nagano&appid=%s", Key)
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
var data Data
if err := json.Unmarshal(body, &data); err != nil {
log.Fatal(err)
}
fmt.Println(data)
}
全体の流れはほしいjsonデータに対応する構造体をつくり、その構造体にUnmarshalやdecoderで解析した値を入れていきます。
今回はさきほど返ってきたjsonのweatherとmainの値がほしいとします。
{
"weather": [
{
"id": 804,
"main": "Clouds",
"description": "overcast clouds",
"icon": "04n"
}
],
"main": {
"temp": 276.93,
"feels_like": 274.22,
"temp_min": 275.37,
"temp_max": 278.15,
"pressure": 1024,
"humidity": 67
},
}
このjsonをGOの構造体で表現しタグで結びつけます。
type Data struct {
Weather []Weather `json:"weather"`
Main Main `json:"main"`
}
type Weather struct {
ID int `json:"id"`
Main string `json:"main"`
Description string `json:"description"`
ICON string `json:"icon"`
}
type Main struct {
Temp float32 `json:"temp"`
FeelsLike float32 `json:"feels_like"`
TempMin float32 `json:"temp_min"`
TempMax float32 `json:"temp_max"`
Pressure int `json:"pressure"`
Humidity int `json:"humidity"`
}
あとはUnmarshalに渡すだけです。Marshalは構築するみたいな意味があり、その反対のUnmershalはjsonを崩すみたいなイメージを勝手に持っています。jsonのMarshalメソッドは構造体からjsonを構築するときに使います。(余談ですがMarshalという名前の微妙な性能を持った武器がバロラントというゲームに存在していて何か変な親近感を持ってます)
var data Data
if err := json.Unmarshal(body, &data); err != nil {
log.Fatal(err)
}
コードを実行
go run main.go -key OpenWeatherMapのApiKey
こんなんが返ってくれば成功です。
{[{804 Clouds overcast clouds 04n}] {276.93 274.22 275.37 278.15 1024 67}}
decoderを使う
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
)
var (
Key string
)
type Data struct {
Weather []Weather `json:"weather"`
Main Main `json:"main"`
}
type Weather struct {
ID int `json:"id"`
Main string `json:"main"`
Description string `json:"description"`
ICON string `json:"icon"`
}
type Main struct {
Temp float32 `json:"temp"`
FeelsLike float32 `json:"feels_like"`
TempMin float32 `json:"temp_min"`
TempMax float32 `json:"temp_max"`
Pressure int `json:"pressure"`
Humidity int `json:"humidity"`
}
func init() {
flag.StringVar(&Key, "key", "", "Api key")
flag.Parse()
}
func main() {
url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=nagano&appid=%s", Key)
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error get api:", err)
return
}
defer resp.Body.Close()
// デコーダ作成
decoder := json.NewDecoder(resp.Body)
for {
var data Data
err := decoder.Decode(&data)
if err == io.EOF {
break
}
if err != nil {
fmt.Println("Error decode json:", err)
return
}
fmt.Println(data)
}
}
decoderを作成してそれによってjsonを一行ずつ解析しています。
jsonの最後の行が解析できたらdecoderがio.EOFを返すので、それが返されたら無限ループから抜け出しています。
構造体を使ったプログラミングはほぼ初めてだったので新鮮ですごく楽しいと感じました。
また、少しGoを触ってみてjavascriptとCの中間みたいな書きごごちだと感じました。javascriptを触ったことがある方はきっとGoも楽しめると思うのでぜひ!!