json.Unmarshalとreflectパッケージを使って構造体を生成する

json.Unmarshalとreflectパッケージを使って構造体を生成する
目次

今回は reflect パッケージと json パッケージも使って構造体の生成をしてみます。

json.Unmarshal を使った構造体の生成

以下の構造体を定義します。

1type Msg struct {
2	ID  int    `json:"id"`
3	Msg string `json:"msg"`
4}

通常、json文字列から構造体を作成するには json.Unmarshal 、または json.NewDecoder を使います。

1func main() {
2	str := `{"id":1,"msg":"some message here"}`
3	
4	var msg Msg
5	json.Unmarshal([]byte(str), &msg)
6
7	# main.Msg{ID:1, Msg:"some message here"} が表示される
8	log.Printf("%#v", msg)	
9}

reflect パッケージと json.Unmarshal を使った構造体の生成

Goのreflectパッケージを使ってインスタンスを生成する の記事で学んだ リフレクションと組み合わせて、JSON文字列から構造体のインスタンスを作成する関数 createInstance を作ります。

以下のようになりました。

 1func main() {
 2	str := `{"id":1,"msg":"some message here"}`
 3
 4	var msg Msg
 5	# *Msg型が格納される
 6	res := createInstance(str, reflect.TypeOf(msg))
 7
 8	# main.Msg{ID:1, Msg:"some message here"} が表示される
 9	log.Printf("%#v", res)
10
11	# 構造体の各フィールドにアクセスするには型アサーションが必要
12	r := res.(*Msg)
13	log.Printf("%#v", r)
14	log.Print("%s", r.ID)
15	log.Print("%s", r.Msg)
16}
17
18# Typeからオブジェクトを生成してinterfaceをUnmarshalに渡す
19func createInstance(str string, typ reflect.Type) interface{} {
20
21	obj := reflect.New(typ).Interface()	
22	json.Unmarshal([]byte(str), &obj)
23	return obj
24}

NGなパターン( Elem を使う)

実装の過程で失敗したコード片も載せておきます。

reflect.New で生成したインスタンスから Elem() で要素を取得した場合には map[string]interface{} 型のオブジェクトが返却されてしまいました。

1func createInstance(str string, typ reflect.Type) interface{} {
2	obj := reflect.New(typ).Elem().Interface()
3	json.Unmarshal([]byte(str), &obj)
4	# map[string]interface{} 型が返却される
5	return obj
6}

これは Unmarshalinterface{} 型の変数のアドレスを渡したときと同じ挙動です。

1var f interface{}
2err := json.Unmarshal(b, &f)

参考にさせていただいたサイト