序列化时 Protobuf 默认与 "missing required fields"

Protobuf defaults vs. "missing required fields" when serializing

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

很明显,尽管在初始化时有默认值,但它只需要触及每个字段。对我来说,拥有默认值的要点之一是只需要有人更新非默认字段(或他们需要更改的那些字段),因此这种自行设置的方法是一个非常遗憾的解决方案。

将字段标记为 optional 不是解决方案,因为根据我们的规范,这些字段是合法必需的。

更新: 我尝试的变通方法包括 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,后者递归地将每个字段设置为自身。这似乎是一个丑陋的 hack,所以我将打开下面的问题。

问题:有没有办法创建 ProtoBuf 消息实例并且"convince"每个已经初始化为默认的字段实际上不是一个错误?

这是按预期工作的。 required 表示 "the writer must explicitly fill in this field, not use the default"。如果您想要一个允许作者保留其默认值的字段,那么您需要 optional。这实际上是 requiredoptional 之间的 区别,因此根本没有理由将 required 与默认值一起使用。可以说,如果 required 字段定义了默认值,Protobuf 编译器应该会引发错误,但我当时并没有考虑实施该限制。

(FWIW,required 长期以来一直被认为 a misfeature 并已在 Protobuf v3 中删除。)