【问题标题】:Google Protobuf handle null valuesGoogle Protobuf 处理空值
【发布时间】:2021-06-30 22:05:26
【问题描述】:

有人解释一下如何在 google protobuf 中处理空值。
我有这样的结构:

syntax = "proto3";
import "google/protobuf/wrappers.proto";
message Person {
  google.protobuf.DoubleValue age = 4;
}     

和java代码:

DoubleValue myAge = null;
Person build = Person.newBuilder()
                .setAge(myAge)  // <- NPE
                .build();

此代码导致.setAge(myAge) 行出现空指针异常。
那么 protobuf DoubleValue 的用例是什么?我以为它是用来管理空值的。但仍然收到 NullPointerException 。

你使用这样的代码来处理这个问题吗?

DoubleValue a = null;
Person.Builder builder = Person.newBuilder();
if (a != null) {
    builder.setAge(a);
}

Person build = builder.build();     

更新:
这是 protoc 命令生成的代码的一部分:

/**
* <code>.google.protobuf.DoubleValue age = 9;</code>
*/
public Builder setAge(com.google.protobuf.DoubleValue value) {
if (ageBuilder_ == null) {
    if (value == null) {        //<-- Additional statement
        throw new NullPointerException();
    }
    age_ = value;
    onChanged();
} else {
    ageBuilder_.setMessage(value);
}   

为什么要在生成的代码中设置额外的if 语句?
我认为这是 google 协议缓冲区的错误。
最后我写了这段代码:

DoubleValue.Builder builder = DoubleValue.newBuilder();
DoubleValue myAge = null;
Person build = Person.newBuilder()
                .setAge(myAge != null ? myAge : builder )
                .build();

【问题讨论】:

  • 您是否尝试过在您的原型架构中使用optional
  • 我使用 proto3 ,默认情况下所有字段都是可选的。 (不需要)
  • 如果是这种情况,教程需要更新developers.google.com/protocol-buffers/docs/javatutorial
  • 是的,您在那里编写的代码是如何使用 Protos 正确处理此问题的。

标签: java protocol-buffers


【解决方案1】:

DoubleValue 用于处理缺失值,不一定是null。 Protobuf 库旨在从不涉及空值 - 例如,它从不返回 null,即使对于缺少的字段也是如此。此外,正如您所发现的,setter 不接受 null。例如,要清除字段,您将使用 clearField 而不是 setField(null)

proto2 和 proto3 之间的一大区别是删除了原始字段(例如hasField)的 (initially) “hazzers”。也就是说,如果您有一个double my_field,则无法判断它是从未设置过,还是明确设置为零。 getMyField() 在这两种情况下都会返回 0。

请注意,消息仍然有 hazzers,因此如果您想要该功能,您可以将原始字段包装在消息中。这正是DoubleValue is。因此,如果您有一个字段DoubleValue age = 1,您可以使用hasAge() 来确定它是否已设置。在较新的 protobuf 版本中,您可以对 optional double age = 1 执行相同操作。

请注意,所有这些都与 Java 生成代码中的null 无关。 setter 确实不可能接受 null 值。

TL;DR:您的第一个建议很好而且很常见:

DoubleValue a = null;
Person.Builder builder = Person.newBuilder();
if (a != null) {
    builder.setAge(a);
}
Person build = builder.build();     

如果您有 Optional 而不是 null,您可以使用类似的变体:

OptionalDouble a = OptionalDouble.empty();
Person.Builder builder = Person.newBuilder();
a.ifPresent(value-> builder.getAgeBuilder().setValue(value));

Person build = builder.build();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    • 2020-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多