处理原始缓冲区中的空值



我正在研究从数据库中获取数据并构造protobuff消息的东西。鉴于可以从数据库中获取某些字段的空值的可能性,我将在尝试构造 protobuff 消息时得到 Null 指针异常。从线程 http://code.google.com/p/protobuf/issues/detail?id=57 知道 protobuff 不支持 null,我想知道处理 NPE 被抛出的唯一其他方法是将手动检查插入与原型对应的 java 文件中,如下所示!

message ProtoPerson{
optional string firstName = 1;
optional string lastName = 2;
optional string address1 = 3;
}
ProtoPerson.Builder builder = ProtoPerson.Builder.newBuilder();
if (p.getFirstName() != null) builder.setFirstName(p.getFirstName());
if (p.getLastName() != null) builder.setLastName(p.getLastName());
if (p.getAddress1() != null) builder.setAddress1(p.getAddress1());
...

那么有人可以澄清一下是否有其他可能的有效方法来处理 protobuff 构造期间的空值?

免责声明:来自Google员工每天使用protobufs的答案。我绝不以任何方式代表谷歌。

  1. 将您的原型命名为Person而不是PersonProtoProtoPerson。编译的 protobufs 只是由您使用的语言指定的类定义,并进行了一些改进。添加"Proto"是额外的冗长。
  2. 使用YourMessage.hasYourField()而不是YourMessage.getYourField() != null。protobuf 字符串的默认值为空字符串,不等于 null。然而,无论您的字段是未设置还是清除或空字符串,.hasYourField()始终返回 false。请参阅常见 protobuf 字段类型的默认值。
  3. 您可能已经知道,但我想明确地说:不要以编程方式将 protobuf 字段设置为null即使在protobuf之外,null也会引起各种问题。请改用.clearYourField()
  4. Person.Builder类没有.newBuilder()方法。Person类。像这样理解构建器模式:仅当您还没有新的构建器时,您才创建一个新的构建器

重写你的原型:

message Person {
optional string first_name = 1;
optional string last_name = 2;
optional string address_1 = 3;
}

重写您的逻辑:

Person thatPerson = Person.newBuilder()
.setFirstName("Aaa")
.setLastName("Bbb")
.setAddress1("Ccc")
.build();
Person.Builder thisPersonBuilder = Person.newBuilder()
if (thatPerson.hasFirstName()) {
thisPersonBuilder.setFirstName(thatPerson.getFirstName());
}
if (thatPerson.hasLastName()) {
thisPersonBuilder.setLastName(thatPerson.getLastName());
}
if (thatPerson.hasAddress1()) {
thisPersonBuilder.setAddress1(thatPerson.getAddress1());
}
Person thisPerson = thisPersonBuilder.build();

如果thatPerson是您创建的 person 对象,其属性值可以是空字符串、空格或 null,那么我建议使用 Guava 的Strings库:

import static com.google.common.base.Strings.nullToEmpty;
Person.Builder thisPersonBuilder = Person.newBuilder()
if (!nullToEmpty(thatPerson.getFirstName()).trim().isEmpty()) {
thisPersonBuilder.setFirstName(thatPerson.getFirstName());
}
if (!nullToEmpty(thatPerson.hasLastName()).trim().isEmpty()) {
thisPersonBuilder.setLastName(thatPerson.getLastName());
}
if (!nullToEmpty(thatPerson.hasAddress1()).trim().isEmpty()) {
thisPersonBuilder.setAddress1(thatPerson.getAddress1());
}
Person thisPerson = thisPersonBuilder.build();

Proto 3

wrappers.proto 支持可为空的值:

  • string(StringValue),
  • int(Int32Value),
  • bool(boolValue)

syntax = "proto3";
import "google/protobuf/wrappers.proto";
message ProtoPerson {
google.protobuf.StringValue firstName = 1;
google.protobuf.StringValue lastName = 2;
google.protobuf.StringValue address1 = 3;
google.protobuf.Int32Value age = 4;
}

这个问题没有简单的解决方案。 我建议只处理空检查。 但是,如果您真的想摆脱它们,这里有几个想法:

  • 您可以编写一个代码生成器插件,为每个 Java 类添加setOrClearFoo()方法。 Java 代码生成器为此提供了插入点(请参阅该页末尾)。
  • 你可以使用 Java 反射来迭代pget*()方法,调用每个方法,检查null,然后调用set*()方法,如果非空则调用builder方法。 这将具有额外的优势,即您不必在每次添加新字段时都更新复制代码,但它比编写显式复制每个字段的代码要慢得多。

最新更新