Как сериализовать значения по умолчанию во вложенных сообщениях в 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) подтверждает это поведение:

include_default_value_fields: если True, единичные примитивные поля, повторяющиеся поля и поля карты всегда будут сериализованы. Если False, сериализуются только непустые поля. Эта опция не влияет на отдельные поля сообщения и одно из полей.

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


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

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

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


person Marv    schedule 25.07.2019    source источник


Ответы (1)


На основе ответа зацикливания атрибутов буферов протокола в 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}}
person Marv    schedule 05.08.2019