Protobuf的默认值与序列化时“缺少必填字段”的区别

4

在一个包含必填字段和默认值的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消息实例并“说服”它,让每个已初始化为默认值的字段实际上不是错误?

1个回答

9
这正是预期的工作方式。required意味着“作者必须明确填写此字段,而不是使用默认值”。如果您想要一个允许作者保留其默认值的字段,则需要optional。实际上,这是requiredoptional之间唯一的区别,因此没有理由在默认值下使用required。可以说,如果使用默认值定义了required字段,则Protobuf编译器应该引发错误,但我当时没有考虑到实施该限制。
(顺便说一句,required长期以来一直被认为是一个设计缺陷,已在Protobuf v3中删除。)

不知怎么的,我从文档中理解到如果我将一个字段标记为可选的,它不一定会成为消息流的一部分。我绝对没有得出这意味着最终用户必须简单地触摸该字段的印象。感谢提供链接。 - Thomas
1
@Thomas 嗯,那并不远: "触及领域" 使其成为消息流的一部分,否则它实际上将不会被发送,在这种情况下,接收者使用默认值。 - Kenton Varda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接