protobufのStructがJSONデータの変換に便利な話
protobuf とても便利ですよね。主に gRPC を使う時に利用しますが、単にコンポーネント間の中間データの定義やフォーマットとして使うだけでも有用性が高いです。
型がしっかり決まる protobuf 、それでも Map や、なんなら JSON データをゆるく持たせたくなる時がありませんか?既存システムからの移行や、やんごとなき事情でそういうこともあるでしょう。
というわけでこの記事では、 protobuf のデータに JSON データを持たせる時に Struct を利用すると便利だよという紹介をします。
Struct はあらかじめ protobuf 公式に定義された型です。
大した話ではないですが GitHub に Java での使用例を用意したので見てみましょう。
まずは proto ファイルです。
TextData
の content
は string で JSON 文字列を持たせるつもりです、 StructData
の content
は Struct にしています。
次は main クラスを見ます。
適当に TextData
と StructData
を作って全体を JSON 文字列にして標準出力しています。
補足として、 JsonFormat.printer().print
は protobuf-java-util という便利なライブラリを使っていて protobuf のデータを一発で JSON にしてくれます、楽ちん。
で、実際の標準出力は
{
"name": "text",
"content": "{\"key1\":\"value1\",\"key2\":2.2}"
}
{
"name": "struct",
"content": {
"key1": "value1",
"key2": 2.2
}
}
こうなります。
当然 TextData
の content
の方はただの string なので JSON 文字列になります。一方 StructData
の content
の方は Struct を利用したので綺麗に JSON オブジェクトになっていますね。
ついでにテストコードを見ます。
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