Google Protobuf 处理空值

mah*_*454 5 java protocol-buffers

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

syntax = "proto3";
import "google/protobuf/wrappers.proto";
message Person {
  google.protobuf.DoubleValue age = 4;
}     
Run Code Online (Sandbox Code Playgroud)

和java代码:

DoubleValue myAge = null;
Person build = Person.newBuilder()
                .setAge(myAge)  // <- NPE
                .build();
Run Code Online (Sandbox Code Playgroud)

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

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

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

Person build = builder.build();     
Run Code Online (Sandbox Code Playgroud)

更新:
这是 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);
}   
Run Code Online (Sandbox Code Playgroud)

if为什么要在生成的代码中设置附加语句?
我认为这是谷歌协议缓冲区的一个错误。
最后我写了这段代码:

DoubleValue.Builder builder = DoubleValue.newBuilder();
DoubleValue myAge = null;
Person build = Person.newBuilder()
                .setAge(myAge != null ? myAge : builder )
                .build();
Run Code Online (Sandbox Code Playgroud)

Raf*_*erm 3

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

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

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

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

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

DoubleValue a = null;
Person.Builder builder = Person.newBuilder();
if (a != null) {
    builder.setAge(a);
}
Person build = builder.build();     
Run Code Online (Sandbox Code Playgroud)

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

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

Person build = builder.build();
Run Code Online (Sandbox Code Playgroud)