はじめに (対象読者・この記事でわかること)

この記事は、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)を保持する構造体です。

Go
type GeoPoint struct { Lat float64 Lng float64 }

この型はそのままでは encoding/json パッケージでシリアライズできません。理由は、フィールドに JSON タグが付与されていないためです。つまり、以下のように構造体に組み込んでも、JSON に変換できません。

Go
type Location struct { Point appengine.GeoPoint `json:"point"` // これはうまくいかない }

このまま JSON に変換しようとすると、フィールド名がそのまま使われてしまい、意図した形になりません。

カスタム型でラップしてJSONタグを付与する

そこで、カスタム型を定義してその型に JSON タグを付与することで、JSON シリアライズを可能にします。

ステップ1:カスタム型を定義する

まず、appengine.GeoPoint と同じフィールドを持つカスタム型を定義します。

Go
type GeoPointJSON struct { Lat float64 `json:"lat"` Lng float64 `json:"lng"` }

ステップ2:変換メソッドを実装する

次に、appengine.GeoPoint から GeoPointJSON へ、およびその逆への変換メソッドを実装します。

Go
func (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 型を使います。

Go
type Location struct { Name string `json:"name"` Point GeoPointJSON `json:"point"` }

JSON に変換する際は以下のようにします。

Go
loc := 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 のフィールド名は LatLng ですが、JSON では一般的に latitude/longitudelat/lng と略されることが多いです。そのため、単にタグを付けても名前が一致せず、意図した JSON 構造になりません。

解決策:カスタム型でラップする

前述の通り、カスタム型を定義して JSON タグを明示的に付与することで、任意のフィールド名でシリアライズできます。これにより、フロントエンドや外部 API とやり取りする際も、命名規則に沿った JSON を扱えます。

また、変換メソッドを用意することで、appengine.GeoPoint との相互変換も容易になり、データストアへの保存や取得もスムーズです。

まとめ

本記事では、appengine.GeoPoint に JSON タグを付与する方法を紹介しました。

  • appengine.GeoPoint は標準では JSON タグが付いていないため、そのままではシリアライズできない
  • カスタム型を定義して JSON タグを付与することで、意図した形で JSON 化できる
  • 変換メソッドを用意することで、元の GeoPoint との相互変換も簡単にできる

この手法は、GAE の GeoPoint を REST API などで返す際に非常に便利です。今後は、カスタム Marshal/Unmarshal を実装して、よりスマートな方法も検討していきたいと思います。

参考資料