🐕

[Golang] 構造体の型を変換する方法

2022/06/30に公開

結論

"github.com/jinzhu/copier" が便利です。
https://github.com/jinzhu/copier

このパッケージは、以下のことができます。

  • フィールドから同名のフィールドにコピーする
  • メソッドから同じ名前のフィールドにコピーする
  • フィールドから同じ名前のメソッドへコピー
  • スライスからスライスへのコピー
  • 構造体からスライスへコピー
  • マップからマップへのコピー
  • タグを持つフィールドのコピーを強制する
  • タグの付いたフィールドを無視する
  • ディープコピー

これを使うことで簡単に型変換が可能になります。

どう使うか

今回の場合は、 Person 型から、必要なフィールド値のみを CastPerson 型にコピーする方法を紹介します。
PersonCastPerson で同じフィールドは Name Age From なので、これをコピーしていきます。

package main

import (
	"fmt"
	"github.com/jinzhu/copier"
)

type Person struct {
	Name  string
	Age   int
	Hobby string
	From  string
}

type CastPerson struct {
	Name string
	Age  int
	From string
	HairColor string
}

func main() {
	p := Person{Name: "Yamamoto", Age: 25, Hobby: "plants", From: "Japan"}
	castPerson := CastPerson{HairColor: "red"}
	fmt.Println(castPerson)
	if err := copier.Copy(&castPerson, &p); err != nil {
		panic(err)
	}
	fmt.Println(castPerson)
}

main.go ファイルを実行します。

$ go run main.go

結果は以下の通り、Person型から必要なフィールドのみコピーすることができました🎉

{ 0  red} // コピー前
{Yamamoto 25 Japan red} // コピー後

もし CastPerson型 のフィールドに Person型 に存在しないフィールドがあった場合、
そのフィールドが上書きされる心配もないです。
上のコード例でいくと、 HairColor は上書きされていません。

いつ使ったのか?

構造体の型をレスポンスに必要な型に変換する必要がありました。

初めは以下のようにfor文を使って data型 の値をそれぞれ Person型 のインスタンスに値を入れていくということをしていました。

package main

type Friend struct {
	Name string
	Age  int
}

type Person struct {
	From    string
	Friends []Friend
}

func main() {
	persons := make([]Person, 0)
	friends := make([]Friend, 0)
	
	// dataはDBなどから取得した値と仮定
	for i, v := range data.Friends {
		if len(v.Friends) > 0 {
			friends = append(friends, Friend{
				Name: v.Friends[i].Name,
				Age:  v.Friends[i].Age,
			})
		}
		persons = append(persons, Person{
			From:    v.From,
			Friends: friends,
		})
	}
}

👆 これが

👇 こうなる。

package main

import (
	"github.com/jinzhu/copier"
)

type Friend struct {
	Name string
	Age  int
}

type Person struct {
	From    string
	Friends []Friend
}

func main() {
	persons := make([]Person, 0)
	if err := copier.Copy(&persons, &data); err != nil {
		panic(err)
	}
}

Discussion