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