我正在将Java应用程序从协议缓冲区2迁移到协议缓冲区3。
在原型2中,如果您设置了字段,则可以使用hasfield()
方法
为此生成的示例Java代码为:
public boolean hasText() {
return ((bitField0_ & 0x00000004) == 0x00000004);
}
但是在proto 3中已将其删除。 如何检查原型3中是否设置了字段?
答案 0 :(得分:4)
给出了一种建议的方法here:
# NOTE: As of proto3, HasField() only works for message fields, not for
# singular (non-message) fields. First try to use HasField and
# if it fails (with a ValueError) we manually consult the fields.
try:
return message_pb.HasField(property_name)
except ValueError:
all_fields = set([field.name for field in message_pb._fields])
return property_name in all_fields
此外,在同一页面上:
在proto3中,标量字段的字段存在根本不存在。您对proto3的思维模型应该是C ++或Go结构。对于整数和字符串,没有设置或没有设置之类的东西,它总是有一个值。对于子消息,它是一个指向子消息实例的指针,该指针可以为NULL,这就是为什么您可以测试其存在性的原因。
答案 1 :(得分:3)
我在proto3中看到的最好的建议是将您的字段包装为单例之一。类似于proto2,这将允许您再次检查是否存在。
message blah
{
oneof foo_ { sint32 foo = 1; }
}
在Python生成的代码中,这非常平滑,因为foo
可以直接作为标量进行操作,就好像它不在一个之中一样。
不幸的是,对于Java来说,我认为对oneof的支持要难看得多。 Google还特意在proto3中删除了hasFoo()生成的类函数。因此,您需要查询oneof的getFooCase()来检查是否存在。
https://developers.google.com/protocol-buffers/docs/reference/java-generated#oneof-fields
是的,我意识到这意味着一个人的麻烦以及他们带来的任何麻烦。从好的方面来说,电线上没有开销。
我见过的第二个最佳建议是使用子消息来包装标量,因为仍然支持存在/不存在子消息。 google.protobuf.wrappers.proto中有众所周知的类型(WKT)。如果您使用它们,那么您甚至可能会以您喜欢的语言获得额外的特殊待遇,使包裹的标量可以轻松地操作,就好像不存在所包含的子消息(或者我已经读过,关于这一点我并不完全确定) )。
答案 2 :(得分:3)
Protobuf 3.15.0 现在支持可选字段。您可以再次使用 hasField
方法。
答案 3 :(得分:1)
hasField()
我看到有些人建议使用 oneof
来包装您的字段,但 proto3
已经为原始数据提供了内置 wrappers
和 hasField
类型的方法允许您检查该字段是否已设置的类型。
您可以这样声明它(查看更多Value types here):
syntax = "proto3";
import "google/protobuf/wrappers.proto";
message MyProtoMessage {
google.protobuf.BoolValue enabled = 1;
google.protobuf.StringValue name = 2;
google.protobuf.Int32Value age = 3;
}
并在Java代码中检查它:
MyProtoMessage myProtoMessage = getItFromSomewhere();
if (myProtoMessage.hasName()) {
// field is set by client
myProtoMessage.getName();
} else {
// field is not set by the client
// but you can still call `myProtoMessage.getName()` which will `return` default value ""
}
答案 4 :(得分:0)
我认为,推荐的方法是检查标准值,由于proto3中的设计决策,因此不理想。您无法显式检查是否设置了字段。由于不建议按照here的描述访问msg._fields
,因此剩下的唯一一件事就是检查该字段是否设置为其标准值:
if msg.textfield.isEmpty() {
//assume textfield is not set
}