python – 序列化时Protobuf默认与“缺少必填字段”

包含必需字段的ProtoBuf消息上的SerializeToString()方法(具有默认值)始终抛出EncodeError,指出消息缺少那些必需字段.但是,如果我检查字段的值,则会设置所有默认值.例如:

// mymessage.proto
message MyMessage {
    required int32 val = 1 [default=18];
}

然后在python中:

from mymessage_pb2.py import MyMessage
m = MyMessage()
print m.val # Shows m.val == 18
print m.SerializeToString() # EncodeError

另一方面,如果我这样做:

m.val = m.val
print m.SerializeToString() # No Error

很清楚,尽管在初始化时有一个默认值,但它只需要触摸每个字段.对我来说,默认的一个主要观点是只需要有人来更新非默认字段(或者他们需要更改的那些字段),所以这个set-it-to-own方法是一个非常令人遗憾的解决方案.

将字段标记为可选字段不是解决方案,因为根据我们的规范,这些字段是合法需要的.

更新:我尝试合作包括MergeFrom和CopyFrom,但都没有奏效.所以我写了这个:

def ActuallyInit(obj):
    err = []
    obj.IsInitialized(err)
    for field in err:
        attr = obj.__getattribute__(field)
        try:
            obj.__setattr__(field, attr)
        except:
            ActuallyInit(attr)

然后创建一个protobuf对象并将其传递给ActuallyInit,ActuallyInit以递归方式将每个字段设置为自身.这看起来像是一个丑陋的黑客,所以我将问题留在下面.

问题:有没有办法创建ProtoBuf消息实例并“说服”它每个已经初始化为默认的字段实际上不是错误?

最佳答案 这是按预期工作的. required表示“作者必须明确填写此字段,而不是使用默认值”.如果您想要一个允许编写者使用其默认值离开该字段的字段,那么您需要可选项.这实际上是必需和可选之间的唯一区别,因此没有理由使用默认值.可以说,如果使用默认值定义了必需字段,Protobuf编译器应该引发错误,但我当时并不认为实现该限制.

(FWIW,长期以来被认为是a misfeature并且已经在Protobuf v3中删除了.)

点赞