Как сериализовать значения по умолчанию во вложенных сообщениях в Protobuf

Как гласит заголовок, у меня есть сообщение 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 что правильно.

Теперь я хочу преобразовать его в dict, где присутствуют все значения - даже значения по умолчанию. Для конвертации я использую метод google.protobuf.json_format.MessageToDict(), Но, как вы знаете, MessageToDict() не сериализует значения по умолчанию, пока я не скажу это сделать (как в следующем вопросе: Protobuf не сериализует значения по умолчанию). Поэтому я добавил аргумент 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, сериализуют только непустые поля. Эта опция не затрагивает поля единственного сообщения и одно из полей.

Итак, что я могу сделать, чтобы получить dict со всеми значениями, даже если они являются значениями по умолчанию во вложенных сообщениях?


Интересно когда мой example.json выглядит так:

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

Я получаю ожидаемый результат. Но я не могу убедиться, что example.json будут записаны все значения, так что это не вариант.

1 ответ

Решение

Основываясь на ответе атрибутов Looping over Protocol Buffers в 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}}
Другие вопросы по тегам