19

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

在 proto 2 中检查是否设置了一个字段,您有hasfield()一个生成示例 Java 代码的方法是:

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

但是在 proto 3 中,它已被删除。如何检查 proto 3 中是否设置了字段?

4

5 回答 5

9

这里给出了一种建议的方法:

# 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 的子消息实例的指针,这就是您可以测试它的存在的原因。

于 2018-08-20T05:45:50.020 回答
5

Protobuf 3.15.0现在支持可选字段。您可以hasField再次使用方法。

于 2021-03-09T06:12:09.227 回答
5

使用原型包装器hasField()

我看到有些人建议使用oneof将您的字段包装在其中,但已经为原始数据类型proto3提供了内置wrappers的方法,这些方法将允许您检查该字段是否已设置hasField

这是您可以声明它的方式(在此处查看更多值类型):

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 ""
}
于 2021-02-25T12:21:56.307 回答
4

我在 proto3 中看到的最好的建议是将你的字段包装在一个单例中。这将允许您再次检查存在/不存在,类似于 proto2。

message blah
{
    oneof foo_ { sint32 foo = 1; }
}

在 Python 生成的代码中,这是非常平滑的,因为foo可以直接将其作为标量进行操作,就好像它不在 oneof 中一样。

不幸的是,对于 Java,我认为对 oneof 的支持要丑得多。Google 还特意删除了 proto3 中 hasFoo() 生成的类函数。因此,您需要查阅 o​​neof 的 getFooCase() 来检查是否存在。

https://developers.google.com/protocol-buffers/docs/reference/java-generated#oneof-fields

是的,我意识到这意味着大量的 oneof 和他们带来的任何随之而来的麻烦。从好的方面来说,电线上没有开销。

我见过的第二个最佳建议是使用子消息来包装您的标量,因为仍然支持存在/不存在子消息。google.protobuf.wrappers.proto 中有众所周知的类型 (WKT)。如果您使用这些,那么您甚至可以在您喜欢的语言中获得额外的特殊处理,其中可以轻松操作包装的标量,就好像包含的子消息不存在一样(或者我已经阅读过,我自己对此并不完全确定)。

于 2020-02-21T20:01:07.220 回答
3

我认为推荐的方法是检查标准值,但由于 proto3 中的设计决策并不理想。您无法明确检查是否设置了字段。由于msg._fields不推荐访问,如此处所述唯一剩下的就是检查该字段是否设置为其标准值:

if msg.textfield.isEmpty() {
    //assume textfield is not set
}
于 2019-03-22T02:36:05.030 回答