78

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!

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??

jordanbtucker
  • 5,768
  • 2
  • 30
  • 43
Aarish Ramesh
  • 6,745
  • 15
  • 60
  • 105
  • 7
    Shorter version of the same thing: `Optional.ofNullable(p.getFirstName()).ifPresent(builder::setFirstName);` – streetturtle Dec 02 '16 at 21:20
  • 3
    is the thread you linked the good one? I can't find anything related to null values there. Maybe these are more related https://github.com/protocolbuffers/protobuf/issues/1606 & https://github.com/protocolbuffers/protobuf/issues/5450 – Seba92 Jul 23 '19 at 08:15

3 Answers3

85

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

  1. Name your proto Person instead of PersonProto or 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 NOT equal 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, null causes all sorts of problems. Use .clearYourField() instead.
  4. Person.Builder class does NOT have a .newBuilder() method. Person class does. Understand the Builder Pattern like this: You create a new builder only if you do not have it yet.

A rewrite of your protobuf:

message Person {
  optional string first_name = 1;
  optional string last_name = 2;
  optional string address_1 = 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 thatPerson is 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 Strings library:

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();
Kurru
  • 14,180
  • 18
  • 64
  • 84
Michael Xin Sun
  • 1,174
  • 1
  • 10
  • 12
  • 45
    Your post may be full of good advice...but I'm not sure you understood his question? For instance, his data isn't coming from another protobuf object; it's coming from a database or some other source, and may have nulls. He wanted to know how to easily deal with the nulls, basically. – Erhannis Aug 11 '16 at 21:35
  • 2
    Good point Erhannis, thanks. I updated the answer by adding a section to deal with non-protocol-buffer objects that has empty string values, consecutive-space values, and null values. – Michael Xin Sun Sep 22 '17 at 02:08
  • 1
    'causes all sorts of problems' link is dead, and I'd really like to learn more – kinbiko Nov 02 '17 at 09:42
  • 1
    @kinbiko: Fixed the link. Guava has been migrated from Google Code to GitHub, thus the new link now. Thank you for letting me know! – Michael Xin Sun Nov 02 '17 at 18:37
  • I will name my proto as PersonMessage, to distinguish between my Person class (represent a person) and PersonMessage class (represent message about person). I see nothing wrong with his naming PersonProto, or ProtoPerson. Maybe this is just my personal opinion. – Anh Tuan May 11 '18 at 07:32
  • How should be dealed with NULL values using proto3? There are no "YourMessage.hasYourField()" Methods... – MadMG Apr 16 '19 at 11:34
  • 4
    Isn't this answer just the same as what's suggested in the question - just use null checks? The relevant part - the part that answers the question is only in the last paragraph. Confusingly the other part of the answer is answering questions that haven't been asked. – alex.p May 28 '19 at 10:31
  • Regarding 1) the reason people tend to have classes named to indicate it's a protobuf object is that for most cases the entity you're transferring is already an existing domain object. Using the same named class for the proto class as the domain object might make sense but becomes very confusing when you have both of these classes being reference in another class. I generally wouldn't prepend proto though but instead append DTO to indicate its purpose better: https://en.wikipedia.org/wiki/Data_transfer_object – alex.p May 28 '19 at 10:36
  • IDK this seems like a bug to me, the Protobuf generated code at first glance looks like it supports the fluent builder pattern, as this question illustrates it clearly does not. – mbonness Dec 09 '19 at 17:21
  • I using pb in my C++ program. I have a pb object which has a repeated string field. with a very rarely probability, the last string, will be changed to null. Then my program will be core. Do you have any idea about the issue? I am sure the pb object not in critical section. – zawdd Jan 08 '20 at 09:47
  • The problem calling the proto type Person instead of ProtoPerson, is you will already have an API or Domain or DB object called Person that you are mapping from. So if you don't give the protobuf object a unique name then you have to use fully qualified types in your code. – dan carter Sep 01 '20 at 22:47
  • 1
    This answer is specific to protobuf 2: protobuf `optional` as well as methods like `hasFirstName` on scalar are not available in proto3. – Svend Jan 15 '21 at 07:33
  • flawless tips @MichaelXinSun – Gaurav Jan 25 '22 at 12:49
48

Proto 3

wrappers.proto supports nullable values:

  • string(StringValue),
  • int(Int32Value),
  • bool(BoolValue)
  • and etc

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;
}
Community
  • 1
  • 1
Serj-Tm
  • 16,581
  • 4
  • 54
  • 61
13

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 plugin which adds setOrClearFoo() methods to each Java class. The Java code generator provides insertion points for 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 builder if 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.
Kenton Varda
  • 41,353
  • 8
  • 121
  • 105
  • Please take a look at this question http://stackoverflow.com/questions/21474024/how-to-accomplish-inheritance-using-proto-buffers – Aarish Ramesh Jan 31 '14 at 06:45
  • Has anyone actually created a code generator plugin that does the setOrClearFoo() or provide an example? – Erik Englund Dec 06 '18 at 16:51
  • I have tried reflection but the performance takes a steep dive as the proto becomes big and you need to map a large dataset to the proto objects. – Dharmvir Tiwari Oct 15 '21 at 18:55