Java 处理 protobuffers 中的空值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21227924/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 07:27:37  来源:igfitidea点击:

Handling null values in protobuffers

javanullprotocol-buffers

提问by Aarish Ramesh

I am working on something which fetches data from database and constructs protobuff message. Given the possibility that null values can be fetched from the database for certain fields , I will get Null-pointer exception while trying to construct the protobuff message. Getting to know that null is not supported in protobuffs from the thread http://code.google.com/p/protobuf/issues/detail?id=57, I am wondering whether the only other way to handle NPE getting thrown is to insert manual checks into the java file corresponding to the proto like below!

我正在研究从数据库中获取数据并构建 protobuff 消息的东西。鉴于可以从数据库中获取某些字段的空值的可能性,我将在尝试构造 protobuff 消息时获得空指针异常。从线程http://code.google.com/p/protobuf/issues/detail?id=57了解到 protobuffs 不支持 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());
...

So can someone please clarify whether there is any other possible efficient way to handle the null values during protobuff construction??

那么有人可以澄清在protobuff构建过程中是否有任何其他可能的有效方法来处理空值?

采纳答案by Kenton Varda

There's no easy solution to this. I'd recommend just dealing with the null checks. But if you really want to get rid of them, here are a couple ideas:

对此没有简单的解决方案。我建议只处理空检查。但如果你真的想摆脱它们,这里有一些想法:

  • You could write a code generator pluginwhich adds setOrClearFoo()methods to each Java class. The Java code generator provides insertion pointsfor this (see the end of that page).
  • You could use Java reflection to iterate over the get*()methods of p, call each one, check for null, and then call the set*()method of builderif non-null. This will have the added advantage that you won't have to update your copy code every time you add a new field, but it will be much slower than writing code that copies each field explicitly.
  • 您可以编写一个代码生成器插件setOrClearFoo()为每个 Java 类添加方法。Java 代码生成器为此提供了插入点(请参阅该页的末尾)。
  • 您可以使用 Java 反射来迭代 的get*()方法p,调用每个方法,检查null,然后调用if 非空的set*()方法builder。这将具有额外的优势,即您不必每次添加新字段时都更新复制代码,但它比编写显式复制每个字段的代码要慢得多。

回答by Michael Xin Sun

Disclaimer: Answer from a Googler using protobufs on a daily basis. I'm by no means representing Google in any way.

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

  1. Name your proto Personinstead of PersonProtoor ProtoPerson. Compiled protobufs are just class definitions specified by the language you are using, with some improvements. Adding "Proto" is extra verbosity.
  2. Use YourMessage.hasYourField()instead of YourMessage.getYourField() != null. Default value for protobuf string is an empty string, which does NOTequal to null. Whereas, no matter whether your field is unset or cleared or empty string, .hasYourField()always returns false. See default values for common protobuf field types.
  3. You've probably known, but I wanna say explicitly: Don't programmatically set a protobuf field to null.Even for outside of protobuf, nullcauses all sorts of problems. Use .clearYourField()instead.
  4. Person.Builderclass does NOThave a .newBuilder()method. Personclass does. Understand the Builder Patternlike this: You create a new builder only if you do not have it yet.
  1. 命名您的 protoPerson而不是PersonProtoor ProtoPerson。编译后的 protobuf 只是您使用的语言指定的类定义,并进行了一些改进。添加“Proto”是额外的冗长。
  2. 使用YourMessage.hasYourField()代替YourMessage.getYourField() != null。protobuf 字符串的默认值为空字符串,等于 null。然而,无论您的字段是未设置、已清除还是空字符串,.hasYourField()都始终返回 false。查看常见 protobuf 字段类型的默认值
  3. 您可能已经知道,但我想明确地说:不要以编程方式将 protobuf 字段设置为null. 即使在protobuf之外,也会null导致各种问题。使用.clearYourField()来代替。
  4. Person.Builder类不.newBuilder()方法。Person类。像这样理解构建器模式:只有在您还没有新构建器时才创建它。

A rewrite of your protobuf:

重写你的protobuf:

message Person {
  optional firstName = 1;
  optional lastName = 2;
  optional address1 = 3;
}

A rewrite of your logic:

重写你的逻辑:

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();

And if thatPersonis a person object that you created that has attribute values that could be an empty string, empty spaces or null, then I'd recommend using Guava's Stringslibrary:

如果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();

回答by Serj-Tm

Proto 3

原型 3

wrappers.proto supports nullable values:

wrappers.proto 支持可空值:

  • string(StringValue),
  • int(Int32Value),
  • bool(BoolValue)
  • and etc
  • 字符串(字符串值),
  • int(Int32Value),
  • 布尔(布尔值)
  • 等等

Example

例子

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;
}