web-dev-qa-db-fra.com

Protobuf 3.0 Any Type pack / unpack

Je voudrais savoir comment transformer un Protobuf Tout type en type de message Protobuf d'origine et vice versa. Dans Java de Message à Any est facile:

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

Mais comment puis-je analyser Any vers le message d'origine (par exemple vers le type de "protoMess")? Je pourrais probablement tout analyser sur un flux juste pour le relire, mais ce n'est pas ce que je veux. Je veux avoir une transformation comme celle-ci:

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

Comment puis-je y parvenir? Est-il déjà implémenté pour Java? Le Protobuf Language Guide indique qu'il y avait des méthodes de pack et unpack, mais il n'y en a pas en Java. Merci d'avance :)

18
Overholt

La réponse pourrait être un peu tardive, mais peut-être que cela aide toujours quelqu'un.

Dans la version actuelle de Protocol Buffers 3, pack et unpack sont disponibles en Java .

Dans votre exemple, l'emballage peut se faire comme:

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

Et déballer comme:

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

Voici également un exemple complet de gestion des messages de tampons de protocole avec des messages imbriqués Any:

Fichiers ProtocolBuffers

Un simple fichier de tampons de protocole avec un message Any imbriqué pourrait ressembler à:

syntax = "proto3";

import "google/protobuf/any.proto";

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

Un message imbriqué possible pourrait alors être:

syntax = "proto3";

message ChildMessage {
  string text = 1;
}

Emballage

Pour créer le message complet, la fonction suivante peut être utilisée:

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();
}

Déballage

Pour lire le message enfant du message parent, la fonction suivante peut être utilisée:

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

MODIFIER:

Si vos messages compressés peuvent avoir différents types, vous pouvez lire le typeUrl et utiliser la réflexion pour décompresser le message. En supposant que vous ayez les messages enfants ChildMessage1 et ChildMessage2 vous pouvez effectuer les opérations suivantes:

@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;
    }
}

Pour un traitement ultérieur, vous pouvez déterminer le type du message avec instanceof, ce qui n'est pas très efficace. Si vous souhaitez obtenir un message d'un certain type, vous devez comparer directement le 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

Je sais que cette question est très ancienne, mais elle est toujours venue lorsque je cherchais la réponse. En utilisant @sundance answer, j'ai dû répondre de façon un peu différente. Le problème étant que le message réel était une sous-classe de la classe réelle. Il fallait donc un $.

    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();
            }
        } 

Avec ceci étant la définition de mes messages proto


    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;
    }

et


    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

Juste pour ajouter des informations au cas où quelqu'un aurait le même problème ... Actuellement, pour déballer, vous devez le faire (c # .netcore 3.1 Google.Protobuf 3.11.4)

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