今回は、Goの標準パッケージの一つであるjsonパッケージについて解説します。
Goでは、jsonパッケージを使用することで、外部ライブラリ等に依存することなく、JSONのエンコードとデコードを行うことができます。
JSON形式で記述した設定ファイルを読み込む、Web APIにおけるデータの送受信に用いるなど、WEB開発とも親和性の高いパッケージですので、今回基本的な使い方を抑えておきたいと思います。
Marshal関数 ― 構造体型からJSONテキストへの変換
構造体型をJSONテキストへ変換するには、json.Marshalを使用します。
関数json.Marshalは、任意の構造体型からJSONテキストへのエンコードを行います。
返り値は[]byte型なので、テキストとして扱いたい場合は文字列型へ変換する必要があります。
内部的には、前回学習したreflectパッケージの反射型を使用しています。
このため、特別な設定を行わなくても構造体型に定義されたフィールド名と値の組み合わせから自動的にJSONテキストを生成することができます。
それではサンプルを見てみましょう。
package main import ( "encoding/json" "fmt" ) // 顧客情報構造体 type Customer struct { Id int Name string Age int Gender int } func main() { // 顧客情報構造体を生成 cst := Customer{Id: 1, Name: "山田hoge郎", Age: 24, Gender: 1} // JSONエンコード jm, err := json.Marshal(cst) if err != nil { fmt.Println("JSONエンコードに失敗しました。") } // エンコード結果を出力 fmt.Println(string(jm)) }
このサンプルの実行結果は以下の通りです。
{“Id”:1,”Name”:”山田hoge郎”,”Age”:24,”Gender”:1}
このように、フィールドと値がJSONフォーマットでマッピング出力されることが確認できます。
タグを利用して任意のフィールド名にマッピングする
先の例で見たとおり、通常、出力されるJSONテキストのキーは構造体に定義したフィールド名になります。
この際、構造体のフィールド名とは異なる任意の名前にマッピングしたい場合、構造体のタグ機能を利用できます。
構造体のタグ機能については、以下の記事で解説していますので必要に応じて参照してください。
【Go入門】構造体のタグ ~ メタ情報の付与と取得
【Go入門】構造体のフィールド定義、値、メタ情報を取得して列挙する
構造体フィールドのタグに、 `json:”任意のフィールド名”`という書式で文字列を格納することで、任意のJSONフィールド名を指定することができます。
フィールド名をダブルクォートで囲う必要がある点に注意してください。
特殊な用法として、フィールドタグの値にハイフン”-“を指定した場合、該当のフィールドは省略されます。
“-“という名前のフィールド名にしたい場合は、ハイフンとカンマ”-,”を指定します。
サンプルと実行結果は以下の通りです。
package main import ( "encoding/json" "fmt" ) // 顧客情報構造体 type Customer struct { Id int `json:"顧客ID"` Name string `json:"顧客名称"` Age int `json:"-"` // フィールドを省略する Gender int `json:"-,"` // フィールド名を"-"(ハイフン)にする } func main() { // 顧客情報構造体を生成 cst := Customer{Id: 1, Name: "山田hoge郎", Age: 24, Gender: 1} // JSONエンコード jm, err := json.Marshal(cst) if err != nil { fmt.Println("JSONエンコードに失敗しました。") } // エンコード結果を出力 fmt.Println(string(jm)) }
{“顧客ID”:1,”顧客名称”:”山田hoge郎”,”-“:1}
IdとNameは、それぞれタグに指定した”顧客ID”、”顧客名称”に変換されています。
また、Ageはタグにハイフン(“-“)を指定したため省略されています。
Genderはタグにハイフンとカンマ(“-,”)を指定したため、フィールド名がハイフンになっています。
エンコード可能な型
なお、整数型および浮動小数点数型の値は、JSON数値としてエンコードされます。
チャネル型、複素数型、関数の値はJSONエンコードできません。
そのような値をエンコードしようとすると、MarshalはUnsupportedTypeErrorが返されるので注意してください。
Unmarshal関数 ― JSONテキストから構造体型への変換
JSONテキストを構造体型へ変換するには、json.Unmarshalを使用します。
関数json.Unmarshalは、json.Marshalとは逆に、JSONテキストのデコード結果を任意の構造体にマッピングします。
Unmarshalには、第1引数にJSONテキストをバイト化したデータを取り、第2引数に結果を格納するためのインターフェースをとります。
UnmarshalはJSONエンコードされたデータを解析し、結果をインターフェースが指す値に格納します。
それではサンプルを見てみましょう。
package main import ( "encoding/json" "fmt" ) // 顧客情報構造体 type Customer struct { Id int Name string Age int Gender int } func main() { // 変換元のJSONテキスト src := ` { "Id":2, "Name":"鈴木fuga子", "Age":30, "Gender":2 } ` // 空の顧客情報構造体を生成 cst := new(Customer) // JSONエンコード err := json.Unmarshal([]byte(src), cst) if err != nil { fmt.Println("JSONデコードに失敗しました。") } // デコード結果を出力 fmt.Printf("%+v\n", cst) }
このサンプルの実行結果は以下の通りです。
&{Id:2 Name:鈴木fuga子 Age:30 Gender:2}
タグを利用して任意のフィールド名にマッピングする
json.Unmarshalでも、タグ機能によってJSONテキストのキーと構造体のフィールドのマッピングをカスタマイズできます。
方法はjson.Marshalと同様です。
package main import ( "encoding/json" "fmt" ) // 顧客情報構造体 type Customer struct { Id int `json:"顧客ID"` Name string `json:"顧客名称"` Age int `json:"年齢"` Gender int `json:"-,"` // フィールド名を"-"(ハイフン)にする } func main() { // 変換元のJSONテキスト src := ` { "顧客ID":2, "顧客名称":"鈴木fuga子", "年齢":30, "-":2 } ` // 空の顧客情報構造体を生成 cst := new(Customer) // JSONエンコード err := json.Unmarshal([]byte(src), cst) if err != nil { fmt.Println("JSONデコードに失敗しました。") } // デコード結果を出力 fmt.Printf("%+v\n", cst) }
&{Id:2 Name:鈴木fuga子 Age:30 Gender:2}
変換元のテキスト側に記述された文字列と、構造体のタグに記述したフィールド名がマッピングされていることが確認できます。
なお、出力内容はあくまで構造体情報ですので構造体のフィールド名になっています。
終わりに
JSONテキストは非常に多くのシーンで利用されているため、他の多くの言語でもJSONテキストを取り扱うための手段が確立されています。
しかし、標準パッケージだけで簡単にJSONテキストのエンコード/デコードが行えるのは、Goユーザーには嬉しいところではないでしょうか。
次回は、この機能を応用してJSON形式の設定ファイルを使用する方法を解説したいと思います。