💻

Go: json パッケージ Marshaler/Unmarshaler の実装例

タグ
Go
json
Writer
エンジニア中川
Date
2021/8/31
LayerX インボイス を開発しているDX事業部の @yyoshiki41(中川佳希)です。
今回は、json パッケージにある MarshalerUnmarshaler インターフェイスを満たす構造体を用いたアプリケーション実装の例を紹介します。

MarshalerUnmarshaler インターフェイス

Go ではインターフェイスを命名する際、実装されたメソッド名や構造体名の末尾に er を付ける慣習があります。 MarshalerUnmarshaler も下記のようなメソッドを実装するインターフェイスとして定義されています。
Plain Text
Plain Text
UnmarshalJSON は、インプットの json データをハンドルして Go の型へ変換したい時など、MarshalJSON は、その構造体を json データとしてシリアライズして出力する際などに利用できます。

例. 文字列 "2021-08-31" を time.Time 型へ。json 出力時には "2021-Aug-31" へ。

Plain Text
Plain Text

アプリケーションでの実装例

店舗側画面での権限を管理するアプリケーションを想定してみましょう。 各ユーザー(店舗のスタッフ)に対して、画面ページ(やデータソース)毎に権限を与えるものです。

UI イメージ

UIクライアント側では、真偽値としてデータを解釈することが先に決まります。
サーバーサイドAPI、DB側ではどのようなデータの持ち方をするでしょうか? 今後もUIが必要とするAPIやデータリソースは変化するという前提とします。

DB

UI で必要となるデータ構造は上でも述べたように真偽値です。
クライアントからのリクエストを保存する際は、UI側と同じ json データでDBに保存しています。(UI側のフォームの部品などが変わった場合にはバージョニングなどで対応が必要です。)
Plain Text

サーバーサイドAPI

アプリケーションコード側ではどのようにこのデータを扱うか? 真偽値によって、アクションを制御できるよう検討してみます。
例えばショップ情報の閲覧権限(view)が、
  • true であれば、 GET /shops という RestAPI へのリクエストが可能
  • false であれば、リクエストに対してステータスコード 403 Forbidden を返す
という具合です。
今回は、json パッケージのカスタム Marshaler/Unmarshaler で解決する方法を考えてみたいと思います。
許可されているAPIエンドポイントを表す構造体をスライスで持つ PermissionShop 構造体を実装します。 (この構造体は、 Marshaler/Unmarshaler インターフェイスを満たしています。)
Plain Text
真偽値を用いてアクセスコントロールを行うミドルウェアなどでは、DB から取得してきた json を Unmarshal して、API エンドポイント構造体のスライスにしてハンドル出来るようになります。(真偽値で false の場合には、スライスの要素数が0として表現されます。)
Plain Text

まとめ

Marshaler/Unmarshaler を実装する構造体の実装例を作成していきました。 raw そのままでは扱いにくい json データも、アプリケーションコードで扱いやすい構造体に変換することが出来ます。 もしくは、構造体から json にシリアライズするなどが出来ると必要なコードをスッキリさせたり、ロジックの変化にも対応しやすくなります。
LayerX インボイスでは、 go generate と組み合わせてアクセスコントロールなどに使っています。
  • API エンドポイント一覧は、swagger 定義から生成
  • PermissionResource は、 UI コンポーネントのフォームから生成
  • UnmarshalJSONMarshalJSON メソッドの実装は、コード生成と相性の良い汎化できる分岐ロジック
開発スピードに耐えれる、変更に強い設計を今後も追求できればと考えています。
興味が湧いたという方は、ぜひ一度話を聞きに来てみてください。
エントリーはちょっとという方、こちらから中の話を聞くこともできます!