检查是否在协议缓冲区3中设置了字段

时间:2018-08-19 14:52:02

标签: java protocol-buffers proto3

我正在将Java应用程序从协议缓冲区2迁移到协议缓冲区3。

在原型2中,如果您设置了字段,则可以使用hasfield()方法 为此生成的示例Java代码为:

public boolean hasText() {
  return ((bitField0_ & 0x00000004) == 0x00000004);
}

但是在proto 3中已将其删除。 如何检查原型3中是否设置了字段?

5 个答案:

答案 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 已经为原始数据提供了内置 wrappershasField 类型的方法允许您检查该字段是否已设置的类型。

您可以这样声明它(查看更多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
}