如何序列化Protobuf中嵌套消息中的默认值

时间:2019-07-25 13:40:08

标签: python json dictionary google-protocol-buffer protobuf-3

如标题所示,我有一条protobuf消息,其中有另一条消息,如下所示:

syntax = "proto3";

message Message
{
    message SubMessage {
        int32 number = 1;
    }

    SubMessage subMessage = 1;
}

我的example.json为空(表示所有地方都为默认值):

{
}

在我的python脚本中,我读到以下消息:

with open("example.json", "r") as FH:
    exampleJSON = FH.read()

example_message = example.Message()
google.protobuf.json_format.Parse(exampleJSON, example_message)

如果我检查example_message.subMessage.number的值,则正确的是0

现在,我想将其转换为存在所有值的字典-甚至是默认值。 对于转换,我使用方法google.protobuf.json_format.MessageToDict()。 但是您可能知道MessageToDict()不会在没有我告诉默认值的情况下序列化默认值(例如,在这个问题中:Protobuf doesn't serialize default values)。 因此,我将参数including_default_value_fields=True添加到了MessageToDict()的调用中:

protobuf.MessageToDict(example_message, including_default_value_fields=True)

返回:

{}

而不是我所期望的:

{'subMessage': {'number': 0}}

protobuf代码中的注释(可在https://github.com/protocolbuffers/protobuf/blob/master/python/google/protobuf/json_format.py中找到)证实了这一行为:

  

includes_default_value_fields:如果为True(单数原始字段),   重复字段,并且映射字段将始终被序列化。如果   False,仅序列化非空字段。单个消息字段   并且其中一个字段不受此选项的影响。

那么即使嵌套消息中的默认值是 all ,我该怎么做呢?


有趣的是,如果我的example.json像这样:

{
    "subMessage" : {
        "number" : 0
    }
}

我得到了预期的输出。 但是我不能确保输入的json会写出所有值,所以这不是一个选择。

1 个答案:

答案 0 :(得分:0)

基于Looping over Protocol Buffers attributes in Python的答案,我创建了一个自定义MessageToDict函数:

def MessageToDict(message):
    messageDict = {}

    for descriptor in message.DESCRIPTOR.fields:
        key = descriptor.name
        value = getattr(message, descriptor.name)

        if descriptor.label == descriptor.LABEL_REPEATED:
            messageList = []

            for subMessage in value:
                if descriptor.type == descriptor.TYPE_MESSAGE:
                    messageList.append(MessageToDict(subMessage))
                else:
                    messageList.append(subMessage)

            messageDict[key] = messageList
        else:
            if descriptor.type == descriptor.TYPE_MESSAGE:
                messageDict[key] = MessageToDict(value)
            else:
                messageDict[key] = value

    return messageDict

鉴于从空example.json中读取的消息,此函数返回:

{'subMessage': {'number': 0}}