概要
JSONの読み込みは「json.Unmarshal」「json.NewDecoder」の2種類、JSONの生成は「json.Marshal」「json.Marshalindent」「json.NewDecoder」の3種類を使った方法を紹介する。
encoding/jsonパッケージ
JSONデータのエンコードとデコードをするためのパッケージ。
今回はこのパッケージを使用してJSONデータの解析を行う。
JSONデータの読み込み
json.Unmarshal
json.Unmarshal()
はJSONを構造体に変換する。
第一引数にJSON形式のデータを、第二引数にポインタを指定することでJSON形式で受け取った値を指定したポインタに保存することができる。
json.Unmarshal(JSON形式のデータ, ポインタ)
JSONデータを解析してみる
今回は以下のようなJSONデータを用意する。ファイルの名前はtest.jsonとする。
{
"id" : 1,
"content" : "Hello World!",
"author" : {
"id" : 2,
"name" : "Goromaru"
},
"comments" : [
{
"id" : 1,
"content" : "Rugby is great sport",
"author" : "Matsushima"
},
{
"id" : 2,
"content" : "Try!!",
"author" : "Fukuoka"
}
]
}
以下がJSONデータを解析するコード。test.jsonは同じ階層にあるものとする。
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type Post struct {
Id int `json:"id"`
Content string `json:"content"`
Author Author `json:"author"`
Comments []Comment `json:"comments"`
}
type Author struct {
Id int `json:"id"`
Name string `json:"name"`
}
type Comment struct {
Id int `json:"id"`
Content string `json:"content"`
Author string `json:"author"`
}
func main() {
jsonFile, err := os.Open("test.json")
if err != nil {
fmt.Println("JSONファイルを開けません", err)
return
}
defer jsonFile.Close()
jsonData, err := ioutil.ReadAll(jsonFile)
if err != nil {
fmt.Println("JSONデータを読み込めません", err)
return
}
var post Post
json.Unmarshal(jsonData, &post)
fmt.Println(post)
fmt.Println(post.Comments)
fmt.Println(post.Comments[0].Content)
}
ポイント
- まず
os.Open()
を使ってJSONファイルを開き、ioutil.ReadAll()
で読み込む。 - 解析したJSONの情報を保存するための構造体を作成する必要がある。一番大きな枠組みとして構造体Postを定義している。
- test.jsonの
author
はキー・バリュー形式になっているので別途、構造体Authorを定義する。構造体PostのAuthorフィールドはAuthor型となる。 - test.jsonの
comments
はスライスになっているので、別途、構造体Commentを定義する。構造体PostのCommentsフィールドはComment型のデータを格納するスライスとする。 -
json.Unmarshal(jsonData, &post)
で読み込んだtest.jsonの情報を構造体postに格納している。 -
fmt.Println(post)
は以下のようになる。
{1 Hello World! {2 Goromaru} [{1 Rugby is great sport Matsushima} {2 Try!! Fukuoka}]}
-
fmt.Println(post.Comments)
は以下のようになる。
[{1 Rugby is great sport Matsushima} {2 Try!! Fukuoka}]
-
fmt.Println(post.Comments[0].Content)
は以下のようになる。
Rugby is great sport
json.NewDecoderを使った読み込み方法
もう1つ、json.NewDecoder()
を使う方法がある。
使い分けとしては、ストリームから情報を読み込んだ時はjson.NewDecoder()
を、それ以外はjson.Unmarshal()
を使うことになる。
json.NewDecoder()
は例えば上記のtest.jsonに複数のJSONが含まれていても対応できるが、json.Unmarshal()
は対応できない。
test.jsonを以下のように書き換えてみる。
{
"id" : 1,
"content" : "Hello World!",
"author" : {
"id" : 2,
"name" : "Goromaru"
},
"comments" : [
{
"id" : 1,
"content" : "Rugby is great sport",
"author" : "Matsushima"
},
{
"id" : 2,
"content" : "Try!!",
"author" : "Fukuoka"
}
]
}
{
"id" : 2,
"content" : "Hahahahaha",
"author" : {
"id" : 3,
"name" : "Nakamura"
},
"comments" : [
{
"id" : 1,
"content" : "Rugby is brilliant",
"author" : "Yamanaka"
},
{
"id" : 2,
"content" : "Scrum!!",
"author" : "Inagaki"
}
]
}
json.Unmarshal()
を使って解析しようとするとエラーが出る。
package ファイル名 is not in GOROOT (xxx/..../test.json)
json.NewDecoder()
使用した場合以下のようになる。
※構造体の定義など重複する部分は省略。
import (
"encoding/json"
"fmt"
"io"
"os"
)
func main() {
jsonFile, err := os.Open("test.json")
if err != nil {
fmt.Println("JSONファイルを開けません", err)
return
}
defer jsonFile.Close()
decoder := json.NewDecoder(jsonFile)
for {
var post Post
err := decoder.Decode(&post)
if err == io.EOF {
break
}
if err != nil {
fmt.Println("JSONデータを読み込めません", err)
return
}
fmt.Println(post)
}
}
ポイント
-
decoder := json.NewDecoder(jsonFile)
でファイルを読み出し、for文でdecoder.Decode(&post)
を実行することで構造体postにJSONデータを格納する。 - 出力結果は以下のようになる。
{1 Hello World! {2 Goromaru} [{1 Rugby is great sport Matsushima} {2 Try!! Fukuoka}]}
{2 Hahahahaha {3 Nakamura} [{1 Rugby is brilliant Yamanaka} {2 Scrum!! Inagaki}]}
JSONデータの生成
json.Marsharl
json.Marsharl()
はJSONを生成する。json.Marsharl()
はJSONを整形しないで1行で出力する。
package main
import (
"encoding/json"
"fmt"
)
type Post struct {
Id int `json:"id"`
Content string `json:"content"`
Author Author `json:"author"`
Comments []Comment `json:"comments"`
}
type Author struct {
Id int `json:"id"`
Name string `json:"name"`
}
type Comment struct {
Id int `json:"id"`
Content string `json:"content"`
Author string `json:"author"`
}
func main() {
post := Post{
Id: 1,
Content: "Hello World!",
Author: Author{
Id: 2,
Name: "Goromaru",
},
Comments: []Comment{
Comment{
Id: 3,
Content: "Rugby is great sport",
Author: "Matsushima",
},
Comment{
Id: 4,
Content: "Try!!",
Author: "Fukuoka",
},
},
}
output, err := json.Marshal(&post)
if err != nil {
fmt.Println("Error marshalling to JSON:", err)
return
}
fmt.Println(string(output))
}
-
post := Post{…}
の形でJSONの内容を作成する。作成したいJSONの構造そのままに書けば良いのでそんなに難しくはない。 -
output, err := json.Marshal(&post)
のようにpostへのポインタを渡すと、JSONを生成してくれる。 - 以下のように出力される。
{"id":1,"content":"Hello World!","author":{"id":2,"name":"Goromaru"},"comments":[{"id":3,"content":"Rugby is great sport","author":"Matsushima"},{"id":4,"content":"Try!!","author":"Fukuoka"}]}
json.MarshalIndent
json.MarshalIndent()
はJSONを整形して出力する。以下のように使う。
json.MarshalIndent(jsonを作成した構造体のポインタ, 各行の先頭につけるプレフィックス, インデント)
上記のoutput, err := json.Marshal(&post)
を以下のように変えるだけ。
output, err := json.MarshalIndent(&post, "", "\t\t")
ポイント
- ファイルに書き出したり、コマンドラインに出力する時はこちらの方が良いだろう。
- 以下のようなきれいな出力になる。
{
"id": 1,
"content": "Hello World!",
"author": {
"id": 2,
"name": "Goromaru"
},
"comments": [
{
"id": 3,
"content": "Rugby is great sport",
"author": "Matsushima"
},
{
"id": 4,
"content": "Try!!",
"author": "Fukuoka"
}
]
}
json.NewDecoderを使った解析方法
こちらもまた、json.NewDecoder()
を使ってJSONの生成ができる。
以下のように使う。※重複部分は省略している。
err := json.NewEncoder(os.Stdout).Encode(post)
if err != nil {
fmt.Println("Error encoding JSON to file:", err)
return
}
-
json.NewEncoder()
は引数で指定された場所にJSONを出力する。この場合は標準出力している。 -
.Encode(post)
でPost構造体をエンコードしている。 - 以下のような出力になる。
{"id":1,"content":"Hello World!","author":{"id":2,"name":"Goromaru"},"comments":[{"id":3,"content":"Rugby is great sport","author":"Matsushima"},{"id":4,"content":"Try!!","author":"Fukuoka"}]}