グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



GoのMarshal/Unmarshalの基本的な使い方とプライベートフィールドを持つ構造体での利用方法

アーティスではGoを使ったプロダクトの開発を進めています。
筆者も今年Goを始めたばかりの新参物です。
Goの文法はシンプルでわかりやすいのですが、開発を進めていくなかでjsonパッケージのMarshal/Unmarshalの使い方で悩んだので記事にまとめておきます。

Package json

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を構造体に変換します。

基本的なサンプル

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を行う例

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を行ってから値を詰め直す例

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

最新の情報をお届けします