序列化协议的答案是使用基于鉴别器的多态性。传统的面向对象继承是一种具有一些非常糟糕特征的形式。在 OpenAPI 等较新的协议中,这个概念更加清晰。
让我解释一下它是如何与 proto3 一起工作的
首先你需要声明你的多态类型。假设我们研究经典的动物物种问题,其中不同物种具有不同的属性。我们首先需要为所有能够识别物种的动物定义一个根类型。然后我们声明一个扩展基本类型的 Cat 和 Dog 消息。请注意,鉴别species
器投影在所有 3 个中:
message BaseAnimal {
string species = 1;
}
message Cat {
string species = 1;
string coloring = 10;
}
message Dog {
string species = 1;
int64 weight = 10;
}
这是一个简单的 Java 测试,用于演示实际情况如何
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
// Create a cat we want to persist or send over the wire
Cat cat = Cat.newBuilder().setSpecies("CAT").setColoring("spotted")
.build();
// Since our transport or database works for animals we need to "cast"
// or rather convert the cat to BaseAnimal
cat.writeTo(os);
byte[] catSerialized = os.toByteArray();
BaseAnimal forWire = BaseAnimal.parseFrom(catSerialized);
// Let's assert before we serialize that the species of the cat is
// preserved
assertEquals("CAT", forWire.getSpecies());
// Here is the BaseAnimal serialization code we can share for all
// animals
os = new ByteArrayOutputStream(1024);
forWire.writeTo(os);
byte[] wireData = os.toByteArray();
// Here we read back the animal from the wire data
BaseAnimal fromWire = BaseAnimal.parseFrom(wireData);
// If the animal is a cat then we need to read it again as a cat and
// process the cat going forward
assertEquals("CAT", fromWire.getSpecies());
Cat deserializedCat = Cat.parseFrom(wireData);
// Check that our cat has come in tact out of the serialization
// infrastructure
assertEquals("CAT", deserializedCat.getSpecies());
assertEquals("spotted", deserializedCat.getColoring());
整个技巧是 proto3 绑定保留他们不理解的属性并根据需要对其进行序列化。通过这种方式,可以实现一个 proto3 转换(转换),它可以在不丢失数据的情况下更改对象的类型。
请注意,“proto3 cast”是非常不安全的操作,只能在对鉴别器进行适当检查后应用。在我的示例中,您可以毫无问题地将猫扔给狗。下面的代码失败
try {
Dog d = Dog.parseFrom(wireData);
fail();
} catch(Exception e) {
// All is fine cat cannot be cast to dog
}
当同一索引处的属性类型匹配时,可能会出现语义错误。在示例中,我的索引 10 在 dog 中是 int64 或在 cat proto3 中是 string 将它们视为不同的字段,因为它们在线路上的类型代码不同。在某些类型可能是字符串并且结构 proto3 实际上可能会抛出一些异常或产生完全垃圾的情况下。