Protocol BuffersとJSONの扱い in OCaml
以前の記事で、ProtocolBuffersでOCaml/TypeScript間の定義を作成して色々やっていたのですが、いくつか問題が出てきたので書いてみます。 <!–more–> ocaml-protoc-pluginに乗り換えた 前回は ocaml-protocを使いましたが、ocaml-protoc-pluginに乗り換えました。理由としては以下となります。 標準のprotocにおけるpluginという形での提供 自前で解析していてもいいとは思いますが、他のproductではprotocのplugin形式が多かったので 型定義にannotationを付けられる yojsonとかに変換するのが簡単です ts-protoc-gen + protoc標準から、pbjs(protobufjs)に乗り換えた 前はprotocに標準添付されているJavaScript実装と、ts-protoc-genを組み合わせていたのですが、以下のような問題があったので乗り換えました。 Jsonからの変換とかが出来ない protocの標準では、 protocolbuffers以外に対するserialize/deserializeがありません JSON-RPCを利用しているので、軽く絶望しながら切り替えました(先に調べろと 発生した問題 今作っているapplicationでは、Electronとbackend server間をWebsocketでつなぎ、RPCとしてJSON-RPCを利用しています。この構成、面倒ではありますが、割と使い勝手がよいのです。しかし、protoファイルから生成した型を使っていると、困る割にいい解決策が無い問題にあたりました。 その問題とは、 OCamlでの型定義とProtocolBufferでの型定義との互換性 です。ナンノコッチャですが、要はocaml-protoc-pluginが生成してくれる型と、protocol_conv_jsonのでのJSON変換が一筋縄では行かない、ということです。 私は大きく2つの問題にあたりましたが、そのうちの一つを例に上げます。 enum Types { Unknown = 0; String = 1; Number = 2; } message Foo { Types value = 0 } こんな感じのprotoファイルからOCamlの定義を作成すると、以下のような感じになります(moduleの定義は省略しています)。 open Ocaml_protoc_plugin.Runtime [@@warning "-33"] module rec Types : sig type t = Unknown | String | Number val to_int: t -> int val from_int: int -> (t, [> Runtime'.Result.error]) result end and Foo : sig val name': unit -> string type t = Types.t val to_proto: t -> Runtime'.Writer.t val from_proto: Runtime'.Reader.t -> (t, [> Runtime'.Result.error]) result end ここでポイントとなるのが、protoファイルにおいてenumと定義した部分と、OCaml版におけるTypes moduleです。OCamlにおいては、Enumを代数的データ型として表すのはごく自然だと思うのですが、問題はProtocol Buffersにおいては、enumは 単なる数値 でしかありません。 to_int とかがそれを物語っています。 ...