↔️

protobufのStructがJSONデータの変換に便利な話

2024/05/29に公開

protobuf とても便利ですよね。主に gRPC を使う時に利用しますが、単にコンポーネント間の中間データの定義やフォーマットとして使うだけでも有用性が高いです。

型がしっかり決まる protobuf 、それでも Map や、なんなら JSON データをゆるく持たせたくなる時がありませんか?既存システムからの移行や、やんごとなき事情でそういうこともあるでしょう。

というわけでこの記事では、 protobuf のデータに JSON データを持たせる時に Struct を利用すると便利だよという紹介をします。
Struct はあらかじめ protobuf 公式に定義された型です。
https://protobuf.dev/reference/protobuf/google.protobuf/#struct

大した話ではないですが GitHub に Java での使用例を用意したので見てみましょう。
まずは proto ファイルです。

https://github.com/innossh/proto-java-example/blob/struct-0.1.0/app/src/main/proto/struct.proto#L9-L17

TextDatacontent は string で JSON 文字列を持たせるつもりです、 StructDatacontent は Struct にしています。

次は main クラスを見ます。

https://github.com/innossh/proto-java-example/blob/struct-0.1.0/app/src/main/java/innossh/proto/example/struct/StructExample.java#L8-L32

適当に TextDataStructData を作って全体を JSON 文字列にして標準出力しています。
補足として、 JsonFormat.printer().print は protobuf-java-util という便利なライブラリを使っていて protobuf のデータを一発で JSON にしてくれます、楽ちん。

https://github.com/protocolbuffers/protobuf/tree/main/java/util

で、実際の標準出力は

{
  "name": "text",
  "content": "{\"key1\":\"value1\",\"key2\":2.2}"
}
{
  "name": "struct",
  "content": {
    "key1": "value1",
    "key2": 2.2
  }
}

こうなります。
当然 TextDatacontent の方はただの string なので JSON 文字列になります。一方 StructDatacontent の方は Struct を利用したので綺麗に JSON オブジェクトになっていますね。

ついでにテストコードを見ます。

https://github.com/innossh/proto-java-example/blob/struct-0.1.0/app/src/test/java/innossh/proto/example/StructExampleTest.java#L16-L50

TextData の方も JSON オブジェクトとして出力したければ、以下のように自前で変換してセットし直すことになります。

JsonElement content = JsonParser.parseString(jsonObject.get("content").getAsString());
jsonObject.add("content", content);

自前での変換が普通だと思っていたので、逆に Struct の便利さには驚きました。
Struct はそれ自体のインスタンスを Java コードから作ろうとすると結構面倒なので、さきほどの main クラスのように protobuf-java-util の Structs.of を使うのが良いです。
テストコードにもありますが以下のように、 Struct を使用していれば JSON 文字列から protobuf のデータへの変換も簡単にできます。

final StructData.Builder original = StructData.newBuilder();
JsonFormat.parser().merge(actual, original);

以上で説明は終わりです。
protobuf に JSON データを持たせたくなった際には Struct を試してみてください。

https://medium.com/@innossh/struct-protobuf-type-is-useful-to-convert-json-objects-8c3b4475193d (en)

Discussion