J'essaie de sérialiser à long terme un groupe d'objets liés par une forte hiérarchie de classes en Java, et j'aimerais utiliser des tampons de protocole pour ce faire en raison de leur simplicité, de leurs performances et de la facilité de leur mise à niveau. Cependant, ils ne fournissent pas beaucoup de soutien pour le polymorphisme. À l'heure actuelle, je gère la situation en proposant une solution "un message pour tous les gouverner" qui comporte un champ chaîne obligatoire, qui me permet d'instancier le type correct par réflexion, puis un ensemble de champs facultatifs pour tous les paramètres. Je pourrais sérialiser d’autres classes possibles, dont une seule sera utilisée (en fonction de la valeur du champ uri). Y at-il une meilleure façon de gérer le polymorphisme, ou est-ce aussi bon que je vais obtenir?
Dans proto3, le mot clé extend
a été remplacé . À partir du docs : If you are already familiar with proto2 syntax, the Any type replaces extensions.
syntax = "proto3";
import "google/protobuf/any.proto";
message Foo {
google.protobuf.Any bar = 1;
}
Mais méfiez-vous: Any
est essentiellement un blob d'octets. La plupart du temps, il vaut mieux utiliser Oneof
:
syntax = "proto3";
message A {
string a = 1;
}
message B {
string b = 1;
}
message Foo {
oneof bar {
A a = 1;
B b = 2;
}
}
Il existe quelques techniques pour mettre en œuvre le polymorphisme. J'essaie de les couvrir tous ici: Polymorphisme du tampon de protocole
Mon approche préférée utilise les extensions imbriquées:
message Animal
{
extensions 100 to max;
enum Type
{
Cat = 1;
Dog = 2;
}
required Type type = 1;
}
message Cat
{
extend Animal
{
required Cat animal = 100; // Unique Animal extension number
}
// These fields can use the full number range.
optional bool declawed = 1;
}
message Dog
{
extend Animal
{
required Dog animal = 101; // Unique Animal extension number
}
// These fields can use the full number range.
optional uint32 bones_buried = 1;
}
Ce n’est pas une réponse à la question initiale, mais cela peut être utile aux autres utilisateurs de la v3 de Protocol Buffers La version 3 interdit le mot clé extensions
. L'exécution de protoc
sur le fichier suivant génère une erreur avec le message Extension ranges are not allowed in proto3
.
syntax = "proto3";
message BaseMessage {
extensions 100 to max;
}
La solution de Jon est correcte et fonctionne mais assez bizarre (pour moi). Mais les tampons de protocole sont assez simples, vous pouvez donc faire quelque chose comme ça:
enum Type {
FOO = 0;
BAR = 1;
}
message Foo {
required Type type = 1;
}
message Bar {
required Type type = 1;
required string text = 2;
}
En gros, message Bar étend le message Foo (du point de vue pratique bien sûr). L'implémentation en Java est aussi simple:
Bar bar = Bar.newBuilder().setType(Type.BAR).setText("example").build();
byte[] data = bar.toByteArray();
----
Foo foo = Foo.parseFrom(data);
if(foo.getType() == Type.BAR){
Bar bar = Bar.parseFrom(data);
System.out.println(bar.getText());
}
Je savais que ce n'était pas une solution élégante mais simple et logique.
Consultez Extensions et extensions imbriquées pour une méthode légèrement plus propre.
Une solution un peu mieux, pour moi, que la réponse de @ Łukasz Marciniak.
Si Bar rallonge Foo, écrivez simplement:
message Bar {
optional Foo foo = 1;
optional double aDouble = 2;
}
message Foo {
optional string aString = 1;
}
Donc, si Foo évolue, seul le message Foo est modifié.
Avez-vous envisagé d'utiliser extensions ? Vous pouvez demander à votre champ uri de déterminer le type à utiliser, puis de simplement charger les extensions appropriées. Si vous savez que vos champs sont mutuellement exclusifs, vous pouvez réutiliser l'identifiant de champ entre des extensions distinctes.
Vous devez gérer tout cela vous-même, car les tampons de protocole ne sont pas conçus pour se décrire eux-mêmes au-delà d'une simple liste de valeurs. Ceci est abordé dans la page des techniques de Google.