GoのMarshal/Unmarshalの基本的な使い方とプライベートフィールドを持つ構造体での利用方法
アーティスではGoを使ったプロダクトの開発を進めています。
筆者も今年Goを始めたばかりの新参物です。
Goの文法はシンプルでわかりやすいのですが、開発を進めていくなかでjsonパッケージのMarshal/Unmarshalの使い方で悩んだので記事にまとめておきます。
Marshalの基本
json.Marshalは構造体をjsonに変換します。
- json.Marshal
- func Marshal(v interface{}) ([]byte, error)
基本的なサンプル
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
p := Person{
Name: "hoge",
Age: 30,
}
s, err := json.Marshal(p)
if err != nil {
panic(err)
}
fmt.Println(string(s))
}
実行結果
{"name":"hoge","age":30}
構造体をjson文字列に変換できました。
構造体タグjsonについて
Marshalする際に構造体タグを利用することでMarshalの結果を一部制御できます
- 「 json:”×××” 」で指定します
- 「 json:”name” 」Marshalした時にjsonのキーを「name」にします
- 「 json:”-“ 」Marshal時に省略する
- 「 json:”omitempty” 」ゼロ値の時に省略
サンプル
type Person struct {
Name string `json:"name"`
Age int `json:"-"`
Height float64 `json:"height"`
Weight float64 `json:"weight,omitempty"`
}
func main() {
p := Person{
Name: "hoge",
Age: 30,
}
s, err := json.Marshal(p)
if err != nil {
panic(err)
}
fmt.Println(string(s))
}
実行結果
{"name":"hoge","height":0}
Unmarshalの基本
json.Unmarshalはjsonを構造体に変換します。
- json.Unmarshal
- func Unmarshal(data []byte, v interface{}) error
基本的なサンプル
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
b := []byte(`{"name":"hoge","age":30}`)
var p Person
if err := json.Unmarshal(b, &p); err != nil {
panic(err)
}
fmt.Printf("%+v\n", p)
}
実行結果
{Name:hoge Age:30}
jsonから構造体に変換できました。
プライベートなフィールドを持つ構造体について
上記の例ではフィールドはすべてpublicでした。
しかし、実際にプロダクトを開発していく場合に構造体のフィールドを すべてpublicにすることはあまりないかと思います。
ただ、フィールドをprivateにした場合にMarshalを行うと返り値が空になってしまいます。
ここで悩んだのですが、MarshalJSONメソッドを定義することで解決できました。
Marshal時に空が返る例
type Person struct {
name string `json:"name"`
age int `json:"age"`
}
func main() {
p := Person{
name: "hoge",
age: 30,
}
s, err := json.Marshal(p)
if err != nil {
panic(err)
}
fmt.Println(string(s))
}
実行結果
{}
MarshalJSONメソッドで一時的な構造体を定義し、値を移し替えてMarshalを行う例
- func (RawMessage) MarshalJSON
- func (m RawMessage) MarshalJSON() ([]byte, error)
type Person struct {
name string
age int
}
func (p Person) MarshalJSON() ([]byte, error) {
// ここでは新しく一時的な構造体を定義して、Marshalして返している
v, err := json.Marshal(&struct {
Name string `json:"name"`
Age int `json:"age"`
}{
Name: p.name,
Age: p.age,
})
return v, err
}
func main() {
p := Person{
name: "hoge",
age: 30,
}
s, err := json.Marshal(p)
if err != nil {
panic(err)
}
fmt.Println(string(s))
}
実行結果
{"name":"hoge","age":30}
またUnmarshalの時も構造体のフィールドがprivateだと値が入らずゼロ値になってしまいます。
この場合もUnmarshalJSONメソッドを定義して対処します。
Unmarshal時に値が入らない例
type Person struct {
name string
age int
}
func main() {
b := []byte(`{"name":"hoge","age":30}`)
var p Person
if err := json.Unmarshal(b, &p); err != nil {
panic(err)
}
fmt.Printf("%+v\n", p)
}
実行結果
{name: age:0}
UnmarshalJSONメソッドで一時的な構造体を定義し、Unmarshalを行ってから値を詰め直す例
- func (*RawMessage) UnmarshalJSON
- func (m *RawMessage) UnmarshalJSON(data []byte) error
type Person struct {
name string
age int
}
func (p *Person) UnmarshalJSON(b []byte) error {
// 自分で新しく定義した構造体
p2 := &struct {
Name string
Age int
}{}
err := json.Unmarshal(b, p2)
if err != nil {
panic(err)
}
// 新しく定義した構造体の結果をもとのpに詰める
p.name = p2.Name
p.age = p2.Age
return err
}
func main() {
b := []byte(`{"name":"hoge","age":30}`)
var p Person
if err := json.Unmarshal(b, &p); err != nil {
panic(err)
}
fmt.Printf("%+v\n", p)
}
実行結果
{name:hoge age:30}
まとめ
今回は基本的なMarshal/Unmarshalの方法を確認しました。
実際の開発ではもっと複雑なデータに対して処理を行うケースもあります。
次回は構造体を埋め込んだ場合のMarshal/Unmarshalなども紹介できればと思います。
この記事を書いた人
- 創造性を最大限に発揮するとともに、インターネットに代表されるITを活用し、みんなの生活が便利で、豊かで、楽しいものになるようなサービスやコンテンツを考え、創り出し提供しています。
この執筆者の最新記事
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー