web-dev-qa-db-ja.com

Protobuf 3.0 Any Typeパック/アンパック

ProtobufAnyタイプを元のProtobufメッセージタイプに変換する方法、およびその逆の方法を知りたいです。 Java Message from Anyは簡単です:

Any.Builder anyBuilder = Any.newBuilder().mergeFrom(protoMess.build());

しかし、どのようにしてAnyを解析して元のメッセージ(たとえば、「protoMess」のタイプ)に戻すことができますか?おそらく、それを読み取るためにストリーム上のすべてを解析することはできますが、それは私が望んでいることではありません。私はこのようないくつかの変換をしたいです:

ProtoMess.MessData.Builder protoMessBuilder = (ProtoMess.MessData.Builder) transformToMessageBuilder(anyBuilder)

どうすればそれを達成できますか? Java用にすでに実装されていますか? Protobuf言語ガイド には、packメソッドとunpackメソッドがあったとありますが、Javaにはありません。前もって感謝します :)

18
Overholt

答えは少し遅いかもしれませんが、多分これはまだ誰かを助けます。

現在のバージョンのプロトコルバッファ3では、packおよびunpackJavaで使用可能 です。

あなたの例では、パッキングは次のように行うことができます:

Any anyMessage = Any.pack(protoMess.build()));

そして、次のように解凍します:

ProtoMess protoMess = anyMessage.unpack(ProtoMess.class);

ネストされたAnyメッセージを含むプロトコルバッファメッセージを処理する完全な例も以下に示します。

ProtocolBuffersファイル

ネストされたAnyメッセージを含む単純なプロトコルバッファファイルは次のようになります。

syntax = "proto3";

import "google/protobuf/any.proto";

message ParentMessage {
  string text = 1;
  google.protobuf.Any childMessage = 2;
}

ネストされる可能性のあるメッセージは次のようになります。

syntax = "proto3";

message ChildMessage {
  string text = 1;
}

梱包

メッセージ全体を作成するには、次の関数を使用できます。

public ParentMessage createMessage() {
    // Create child message
    ChildMessage.Builder childMessageBuilder = ChildMessage.newBuilder();
    childMessageBuilder.setText("Child Text");
    // Create parent message
    ParentMessage.Builder parentMessageBuilder = ParentMessage.newBuilder();
    parentMessageBuilder.setText("Parent Text");
    parentMessageBuilder.setChildMessage(Any.pack(childMessageBuilder.build()));
    // Return message
    return parentMessageBuilder.build();
}

開梱

親メッセージから子メッセージを読み取るには、次の関数を使用できます。

public ChildMessage readChildMessage(ParentMessage parentMessage) {
    try {
        return parentMessage.getChildMessage().unpack(ChildMessage.class);
    } catch (InvalidProtocolBufferException e) {
        e.printStackTrace();
        return null;
    }
}

編集:

パックされたメッセージのタイプが異なる場合は、typeUrlを読み取り、リフレクションを使用してメッセージを解凍できます。子メッセージがあると仮定ChildMessage1およびChildMessage2次のことができます。

@SuppressWarnings("unchecked")
public Message readChildMessage(ParentMessage parentMessage) {
    try {
        Any childMessage = parentMessage.getChildMessage();
        String clazzName = childMessage.getTypeUrl().split("/")[1];
        String clazzPackage = String.format("package.%s", clazzName);
        Class<Message> clazz = (Class<Message>) Class.forName(clazzPackage);
        return childMessage.unpack(clazz);
    } catch (ClassNotFoundException | InvalidProtocolBufferException e) {
        e.printStackTrace();
        return null;
    }
}

さらに処理するには、instanceofを使用してメッセージのタイプを判別できますが、これはあまり効率的ではありません。特定のタイプのメッセージを取得したい場合は、typeUrlを直接比較する必要があります。

public ChildMessage1 readChildMessage(ParentMessage parentMessage) {
    try {
        Any childMessage = parentMessage.getChildMessage();
        String clazzName = childMessage.getTypeUrl().split("/")[1];
        if (clazzName.equals("ChildMessage1")) {
            return childMessage.unpack("ChildMessage1.class");
        }
        return null
    } catch (InvalidProtocolBufferException e) {
        e.printStackTrace();
        return null;
    }
}
18
sundance

私はこの質問が非常に古いことを知っていますが、私が答えを探していたときにまだ出てきました。 @sundanceの回答を使用すると、少し異なる方法で回答する必要がありました。問題は、実際のメッセージが実際のクラスのサブクラスであったことです。したがって、$が必要でした。

    for(Any x : in.getDetailsList()){
            try{
                String clazzName = x.getTypeUrl().split("/")[1];
                String[] split_name = clazzName.split("\\.");
                String nameClass = String.join(".", Arrays.copyOfRange(split_name, 0, split_name.length - 1)) + "$" + split_name[split_name.length-1];
                Class<Message> clazz = (Class<Message>) Class.forName(nameClass);

                System.out.println(x.unpack(clazz));

            } catch (Exception e){
                e.printStackTrace();
            }
        } 

これが私のプロトメッセージの定義です


    syntax = "proto3";
    package cb_grpc.msg.Main;

    service QueryService {
        rpc anyService (AnyID) returns (QueryResponse) {}
    }

    enum Buckets {
        main = 0;
        txn = 1;
        hxn = 2;
       }

    message QueryResponse{
        string content = 1;
        string code = 2;
    }

    message AnyID {
        Buckets bucket = 1;
        string docID = 2;
        repeated google.protobuf.Any details = 3;
    }

そして


    syntax = "proto3";
    package org.querc.cb_grpc.msg.database;

    option Java_package = "org.querc.cb_grpc.msg";
    option Java_outer_classname = "database";

    message TxnLog {
        string doc_id = 1;
        repeated string changes = 2;
    } 

1
Tyler Denton

誰かが同じ問題を抱えている場合に備えて情報を追加するだけです...現在、解凍する必要があります(c#.netcore 3.1 Google.Protobuf 3.11.4)

Foo myobject = anyMessage.Unpack<Foo>();
0
Vonkel.