この記事は Go Advent Calendar 2020 の20日目の記事です。

概要

  • Goはstructとjsonの変換を行う際、[]byteフィールドを base64 変換する
  • この仕様のため、多くの(Go以外で書かれた)外部システムのjsonを、[]byteフィールドを含むstructで受け取れない
  • そこで []byteフィールドをbase64変換しない、json.Marshal/Unmarshalライブラリ go-simple-jsonmarshaler を作った

なぜ必要なのか

大枠は概要に書いたとおりですが、通常Goでjsonの文字列フィールドを受け取る場合はstring型で受ければ問題ありません。

私のユースケースでは連携先システムのインターフェースが Protocol Buffersで定義されており、フィールドに文字列型として[]byteが採用されていました。

型ごとにjson.Marshaler/Unmarshalerを実装すれば受け取ることができますが、型の数が多く、アップデートに追従するのもコストがかかるため作成しました。

利用方法

encoding/jsonと互換性のある Marshal, Unmarshal, Decoder, Encoder を提供しています。

下記のように利用できます。

func ExampleMarshal() {
   in := struct {
      A []byte
   }{A: []byte("an string")}

   // json.Marshal:
   b, _ := json.Marshal(in)
   fmt.Printf("json.Marshal: res: %+v\n", string(b))

   // sjson.Marshal:
   b, _ = sjson.Marshal(in);
   fmt.Printf("sjson.Marshal: res: %+v\n", string(b))
   
   // Output:
   // json.Marshal: res: {"A":"YW4gc3RyaW5n"}
   // sjson.Marshal: res: {"A":"an string"}
}

どう実装したか

golang/go から encoding/json のみをチェックアウトし、[]byte フィールドと json との変換を行う下記 2 箇所において、base64 でなく utf8 のバイト列・文字列として変換するよう変更しました。

上記変更を行った encoding/json をリポジトリに含め、提供する関数のみをExportしています。

チェックアウトした Go のリビジョンで encoding/json の挙動が固定されてしまうため、Go の各マイナーバージョンに対応したものを公開しています。2020/12/20 時点で Go1.12 と 1.15 の最新のパッチバージョンをサポートしています。

まとめ

本記事では []byte フィールドを base64 変換しない、json.Marshal/Unmarshalライブラリ go-simple-jsonmarshaler とその実装方法について紹介しました。

ライブラリや、実装過程で行ったライブラリを一部変更して扱う方法が役に立てば幸いです。

Go Advent Calendar 2020 の21日目は sachaos さんです。お楽しみに!

参考