Check if a field has been set in protocol buffer 3

13

I am migrating a java application from protocol buffers 2 to protocol buffer 3.

In proto 2 to check if a field is set you have hasfield() method for which an example Java code generated is:

public boolean hasText() {
  return ((bitField0_ & 0x00000004) == 0x00000004);
}

However in proto 3 there it has been removed. How do you check if a field has been set in proto 3?

java
protocol-buffers
proto3
asked on Stack Overflow Aug 19, 2018 by user1798617 • edited Aug 19, 2018 by (unknown user)

5 Answers

7

One of the suggested approaches is given here:

# NOTE: As of proto3, HasField() only works for message fields, not for
#       singular (non-message) fields. First try to use HasField and
#       if it fails (with a ValueError) we manually consult the fields.
try:
    return message_pb.HasField(property_name)
except ValueError:
    all_fields = set([field.name for field in message_pb._fields])
    return property_name in all_fields

Also, from the same page:

In proto3, field presence for scalar fields simply doesn't exist. Your mental model for proto3 should be that it's a C++ or Go struct. For integers and strings, there is no such thing as being set or not, it always has a value. For submessages, it's a pointer to the submessage instance which can be NULL, that's why you can test presence for it.

answered on Stack Overflow Aug 20, 2018 by P.W
3

I think the recommended approach, which is not ideal because of design decisions made in proto3, is to check standard values. You cannot explicitly check whether a field is set or not. As access to msg._fields is not recommended as described here the only thing that is left is to check whether the field is set to its standard value:

if msg.textfield.isEmpty() {
    //assume textfield is not set
}
answered on Stack Overflow Mar 22, 2019 by Gigi
3

The best suggestion I've seen for this in proto3 is to wrap your field in a singleton oneof. This will allow you to check for presence / absence again, similar to proto2.

message blah
{
    oneof foo_ { sint32 foo = 1; }
}

In Python generated code this is extremely smooth as foo can be directly operated on as a scalar as if it wasn't inside a oneof.

Unfortunately, for Java I think support for oneof's is far uglier. Google also purposely removed the hasFoo() generated class function in proto3. So you would need to consult the getFooCase() of the oneof instead to check for presence or absence.

https://developers.google.com/protocol-buffers/docs/reference/java-generated#oneof-fields

Yes, I realize these means tons of oneof's and any attendant hassle they bring. On the plus side, there is no overhead on the wire.

The second best suggestion I've seen is to use a submessage to wrap your scalar because presence / absence of submessages is still supported. There are the Well Known Types (WKT) in google.protobuf.wrappers.proto. If you use those, then you may even get extra special treatment in your preferred language where the wrapped scalar can be easily operated on as if the containing submessage didn't exist (or so I've read, not entirely sure on this point myself).

answered on Stack Overflow Feb 21, 2020 by jschultz410 • edited Feb 21, 2020 by jschultz410
3

Protobuf 3.15.0 now has the support for optional fields. You can use hasField methods again.

answered on Stack Overflow Mar 9, 2021 by Lahiru Chandima
1

Use Proto Wrappers hasField()

I see some people are suggesting to use oneof to wrap your field in it but proto3 already provides built-in wrappers with hasField kind of methods for primitive data types which will allow you to check if the field has been set or not.

This is how you can declare it (see more Value types here):

syntax = "proto3";

import "google/protobuf/wrappers.proto";

message MyProtoMessage {
    google.protobuf.BoolValue enabled = 1;
    google.protobuf.StringValue name = 2;
    google.protobuf.Int32Value age = 3;
}

and this in Java code to check it:

MyProtoMessage myProtoMessage = getItFromSomewhere();
if (myProtoMessage.hasName()) {
    // field is set by client
    myProtoMessage.getName();
} else {
    // field is not set by the client
    // but you can still call `myProtoMessage.getName()` which will `return` default value ""
}
answered on Stack Overflow Feb 25, 2021 by UsamaAmjad • edited Mar 17, 2021 by UsamaAmjad

User contributions licensed under CC BY-SA 3.0