Google Protobuf 处理空值
Google Protobuf handle null values
有人向我解释如何处理 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();
DoubleValue
用于处理 缺失的 值,不一定是 null
。 Protobuf 库旨在永远不涉及空值 - 例如,它永远不会 returns null
,即使对于缺失的字段也是如此。此外,正如您所发现的,setters 不接受 null。例如,要清除一个字段,您可以使用 clearField
而不是 setField(null)
.
proto2 和 proto3 之间的一大区别是原始字段(例如 hasField
)的 () 个“hazzers”被删除了。也就是说,如果您有一个 double my_field
,则无法判断它是否从未设置过或明确设置为零。 getMyField()
在这两种情况下都会 return 0。
请注意,消息仍然有 hazzers,因此如果您需要该功能,可以将原始字段包装在消息中。这正是 DoubleValue
is。因此,如果您有一个字段 DoubleValue age = 1
,您可以使用 hasAge()
来确定它是否已设置。在较新的 protobuf 版本中,您可以使用 optional double age = 1
.
执行相同的操作
请注意,所有这些都与 Java 生成的代码中的 null
无关。 setter 确实不可能接受空值。
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();
有人向我解释如何处理 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();
DoubleValue
用于处理 缺失的 值,不一定是 null
。 Protobuf 库旨在永远不涉及空值 - 例如,它永远不会 returns null
,即使对于缺失的字段也是如此。此外,正如您所发现的,setters 不接受 null。例如,要清除一个字段,您可以使用 clearField
而不是 setField(null)
.
proto2 和 proto3 之间的一大区别是原始字段(例如 hasField
)的 (double my_field
,则无法判断它是否从未设置过或明确设置为零。 getMyField()
在这两种情况下都会 return 0。
请注意,消息仍然有 hazzers,因此如果您需要该功能,可以将原始字段包装在消息中。这正是 DoubleValue
is。因此,如果您有一个字段 DoubleValue age = 1
,您可以使用 hasAge()
来确定它是否已设置。在较新的 protobuf 版本中,您可以使用 optional double age = 1
.
请注意,所有这些都与 Java 生成的代码中的 null
无关。 setter 确实不可能接受空值。
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();