【问题标题】:Check if a field has been set in protocol buffer 3检查是否在协议缓冲区 3 中设置了字段
【发布时间】:2018-08-19 14:52:01
【问题描述】:

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

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

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

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

【问题讨论】:

  • AFAIK,在默认的 proto3 实现中根本没有跟踪。字段要么为零(未发送),要么为非零(已发送),就是这样
  • 是的,proto3 搞砸了他们的优化,即没有序列化具有默认值的标量。现在,读者无法区分“未指定字段”和“指定字段但具有默认值”。您可以通过将标量包装在 oneof 或子消息中来收回此功能,并伴随着以您的首选语言引起的笨拙。

标签: java protocol-buffers proto3


【解决方案1】:

建议的方法之一是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 的子消息实例的指针,这就是为什么您可以测试它的存在。

【讨论】:

  • 是的,如果您希望标量存在/不存在,那么您需要将它们包装在 oneof 或消息中(例如 - google.protobuf.wrappers.proto)。
【解决方案2】:

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

【讨论】:

  • 是的。有趣的是,这基本上只是包装标量的隐式 oneof 的语法糖。
【解决方案3】:

使用 Proto Wrappers 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】:

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

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

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

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

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

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

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

    【讨论】:

      【解决方案5】:

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

      if msg.textfield.isEmpty() {
          //assume textfield is not set
      }
      

      【讨论】:

      • 这可以工作。您也可以将其包装在oneof 中。 oneof test_string { string name = 1; }。这也有一些陷阱。
      猜你喜欢
      • 2014-09-13
      • 1970-01-01
      • 2017-02-12
      • 1970-01-01
      • 2011-05-10
      • 2017-03-27
      • 1970-01-01
      • 1970-01-01
      • 2011-09-18
      相关资源
      最近更新 更多