.protoファイルを使用せずに、バイナリprotobufメッセージを含まれているデータの人間が読める形式の説明に変換する方法を見つけたいと思います。
背景には、Androidのパーサーによって拒否されたという.protoメッセージがありますが、その理由は完全には明らかではありません。私は手でメッセージを読むことができましたが、それはかなり退屈です。
私は試した protoc --decode_raw
ですが、「入力の解析に失敗しました。」というエラーが表示されるだけです。私は誰かがこれを行うかもしれない素敵なウェブユーティリティをしただろうと期待/期待してグーグルしましたが、明白なものは何も見つかりませんでした。
次のような出力が得られることを望んでいます。
field 1: varint: 128
field 4: string: "foo"
正しい方向へのポインタは大歓迎です!
Wiresharkプラグインを介して強制するか、一部の実装の「リーダー」部分を借用することができます(たとえば、C#でこれを行う方法は知っていますが、それが意図したことではないかと思います)。
ただし、注意が必要です。プロトコルバッファ内の文字列は、実際には「文字列」を意味するわけではありません。次のようになります。
後世のために:Googleのプロトコルバッファ tools rawバッファをデコードする機能があります。
不明なバッファを送信して、--decode_raw
フラグを渡すだけです。
$ cat has_no_proto.buff | protoc --decode_raw
2 {
2: "Error retrieving information from server. [RH-02]"
}
これは、フィールド2が埋め込みメッセージに設定されているメッセージです。2番目のフィールドは文字列に設定されており、GooglePlayに腹を立てていることを示しています。
型情報は明確ではありません(すべてのバイナリデータを文字列として表示しようとするようですが、varint/string/submessageを区別するための要件は満たされています)。
Michel de Ruiterの回答 に記載されているように、protobufメッセージに length-prefix が含まれている可能性があります。そうだとすれば、この答えが役立つはずです。
(注:以下のほとんどのコマンドでは、protobufメッセージがinput
というファイルに保存されていると想定しています。)
protoc --decode_raw
+ dd
:単一のメッセージの場合は、実際にprotoc --decode_raw
を活用できますが、最初に長さプレフィックスヘッダーを削除する必要があります。ヘッダーの長さが4バイトであるとすると、dd
を使用してヘッダーをinput
から取り除き、出力をprotoc
にフィードできます。
dd bs=1 skip=4 if=input 2>/dev/null | protoc --decode_raw
protoc-decode-lenprefix --decode_raw
:また、ヘッダーストリッピングを自動的に処理する script も作成しました。
protoc-decode-lenprefix --decode_raw < input
このスクリプトは、protoc --decode_raw
の上にある単なるラッパーですが、長さプレフィックスヘッダーを解析してからprotoc
を呼び出す処理を行います。
さて、この場合、このスクリプトはそれほど有用ではありません。上記のdd
トリックを使用して、ヘッダーを取り除くことができるからです。ただし、長さプレフィックスヘッダーで囲まれた複数のメッセージを含むデータストリーム(ファイルまたはTCPストリーム)など)があるとします。
protoc-decode-lenprefix --decode_raw
:入力ファイル内の単一のprotobufメッセージの代わりに、input
に長さプレフィックスヘッダーで囲まれた複数のprotobufメッセージが含まれているとします。この場合、長さプレフィックスヘッダーの内容を実際に読み取る必要があるため、justでdd
トリックを使用することはできません。ストリーム内の後続のメッセージの長さ、つまり次のヘッダー+メッセージの何バイト先にあるかを判別します。したがって、そのすべてを心配する代わりに、protoc-decode-lenprefix
をもう一度使用するだけです。
protoc-decode-lenprefix --decode_raw < input
protoc-decode-lenprefix --decode ... foo.proto
メッセージのストリームの場合このスクリプトを使用して、長さのプレフィックスが付いたメッセージを完全にデコードすることもできます(メッセージを「生でデコード」するだけではありません)。ラップされたprotoc
コマンドと同様に、protobufメッセージを定義する.proto
ファイルにアクセスできることを前提としています。呼び出し構文はprotoc --decode
と同じです。たとえば、protoc --decode
でdd
トリックを使用し、入力が Mesostask.info ファイルである場合、構文は次のようになります。
dd bs=1 skip=4 if=task.info 2>/dev/null | \
protoc --decode mesos.internal.Task \
-I MESOS_CODE/src -I MESOS_CODE/include \
MESOS_CODE/src/messages/messages.proto
また、protoc-decode-lenprefix
を使用する場合のパラメーターは同じです。
cat task.info | \
protoc-decode-lenprefix --decode mesos.internal.Task \
-I MESOS_CODE/src -I MESOS_CODE/include \
MESOS_CODE/src/messages/messages.proto
(複数?)長さプレフィックス protobufメッセージを含むバイナリファイルがある場合、protoc ‒‒decode_raw < file
プレフィックスが長さのため、解析できません。これを回避する簡単な方法は、ファイルを連続するメッセージに分割し、その後それぞれをprotoc
で変換することです。
私の見解:
var fs = File.OpenRead(filename));
var buffer = new byte[4096];
int size;
for (int part = 1; Serializer.TryReadLengthPrefix(fs, PrefixStyle.Base128, out size); part++) {
long startPosition = fs.Position;
using (var writer = File.OpenWrite(string.Format("{0}[{1}].pb", filename, part))) {
for (int bytesToRead = size; bytesToRead > 0; ) {
int bytesRead = fs.Read(buffer, 0, Math.Min(bytesToRead, buffer.Length));
bytesToRead -= bytesRead;
if (bytesRead <= 0) // End of file.
break;
writer.Write(buffer, 0, bytesRead);
}
}
}