はじめに (対象読者・この記事でわかること)
この記事は、Google App Engine (GAE) で Go を使って地理情報を扱いたい方、特に appengine.GeoPoint 型を JSON として扱いたい方を対象にしています。
この記事を読むことで、以下のことがわかります。
appengine.GeoPoint型がなぜ標準では JSON シリアライズできないのか- カスタム型を使って JSON タグを付与する方法
- 実装例と注意点(特に
encoding/jsonとの親和性)
GAE の GeoPoint は便利ですが、標準の JSON パッケージではシリアライズできないため、API レスポンスやデータストアのやり取りで困ったことがあります。この記事ではその解決策を紹介します。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。
- Go の基本的な文法と構造体の定義
encoding/jsonパッケージの使い方(Marshal/Unmarshal)- Google App Engine の
appengine.GeoPoint型の存在を知っていること
appengine.GeoPointとJSONの関係
appengine.GeoPoint は Google App Engine の Go ランタイムで提供される型で、緯度(Lat)と経度(Lng)を保持する構造体です。
Gotype GeoPoint struct { Lat float64 Lng float64 }
この型はそのままでは encoding/json パッケージでシリアライズできません。理由は、フィールドに JSON タグが付与されていないためです。つまり、以下のように構造体に組み込んでも、JSON に変換できません。
Gotype Location struct { Point appengine.GeoPoint `json:"point"` // これはうまくいかない }
このまま JSON に変換しようとすると、フィールド名がそのまま使われてしまい、意図した形になりません。
カスタム型でラップしてJSONタグを付与する
そこで、カスタム型を定義してその型に JSON タグを付与することで、JSON シリアライズを可能にします。
ステップ1:カスタム型を定義する
まず、appengine.GeoPoint と同じフィールドを持つカスタム型を定義します。
Gotype GeoPointJSON struct { Lat float64 `json:"lat"` Lng float64 `json:"lng"` }
ステップ2:変換メソッドを実装する
次に、appengine.GeoPoint から GeoPointJSON へ、およびその逆への変換メソッドを実装します。
Gofunc (g GeoPointJSON) ToAppEngine() appengine.GeoPoint { return appengine.GeoPoint{ Lat: g.Lat, Lng: g.Lng, } } func FromAppEngineGeoPoint(gp appengine.GeoPoint) GeoPointJSON { return GeoPointJSON{ Lat: gp.Lat, Lng: gp.Lng, } }
ステップ3:構造体で使う
これで、JSON タグを使いたい構造体では GeoPointJSON 型を使います。
Gotype Location struct { Name string `json:"name"` Point GeoPointJSON `json:"point"` }
JSON に変換する際は以下のようにします。
Goloc := Location{ Name: "東京タワー", Point: FromAppEngineGeoPoint(someGeoPoint), } b, err := json.Marshal(loc) if err != nil { log.Fatal(err) } fmt.Println(string(b)) // 出力: {"name":"東京タワー","point":{"lat":35.6586,"lng":139.7454}}
ハマった点と解決策
ハマった点:フィールド名が一致しない
appengine.GeoPoint のフィールド名は Lat と Lng ですが、JSON では一般的に latitude/longitude や lat/lng と略されることが多いです。そのため、単にタグを付けても名前が一致せず、意図した JSON 構造になりません。
解決策:カスタム型でラップする
前述の通り、カスタム型を定義して JSON タグを明示的に付与することで、任意のフィールド名でシリアライズできます。これにより、フロントエンドや外部 API とやり取りする際も、命名規則に沿った JSON を扱えます。
また、変換メソッドを用意することで、appengine.GeoPoint との相互変換も容易になり、データストアへの保存や取得もスムーズです。
まとめ
本記事では、appengine.GeoPoint に JSON タグを付与する方法を紹介しました。
appengine.GeoPointは標準では JSON タグが付いていないため、そのままではシリアライズできない- カスタム型を定義して JSON タグを付与することで、意図した形で JSON 化できる
- 変換メソッドを用意することで、元の
GeoPointとの相互変換も簡単にできる
この手法は、GAE の GeoPoint を REST API などで返す際に非常に便利です。今後は、カスタム Marshal/Unmarshal を実装して、よりスマートな方法も検討していきたいと思います。
参考資料
