msgpack десериализация строк ключа dict в байты

У меня проблемы с msgpack в питоне. Кажется, что при сериализации dict, если ключи являются строками strони не несериализованы должным образом и вызывают KeyError исключения, которые будут подняты.

Пример:

>>> import msgpack
>>> d = dict()
>>> value = 1234
>>> d['key'] = value
>>> binary = msgpack.dumps(d)
>>> new_d = msgpack.loads(binary)
>>> new_d['key']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'key'

Это потому, что ключи не являются строками после вызова loads() но не сериализованы bytes объекты.

>>> d.keys()
dict_keys(['key'])
>>> new_d.keys()
dict_keys([b'key'])

Кажется, это связано с нереализованной функцией, упомянутой в github.

У меня вопрос: есть ли способ исправить эту проблему или обойти, чтобы гарантировать, что те же ключи могут быть использованы при десериализации?

Я хотел бы использовать msgpack но если я не могу построить dict объект с str ключи и ожидают, что смогут использовать один и тот же ключ при десерилизации, он становится бесполезным.

2 ответа

Решение

Кодировка по умолчанию устанавливается при вызове dumps или же packb

:param str encoding:
 |      Convert unicode to bytes with this encoding. (default: 'utf-8')

но это не установлено по умолчанию при вызове loads или же unpackb как видно в:

Help on built-in function unpackb in module msgpack._unpacker:

unpackb(...)
    unpackb(... encoding=None, ... )

Поэтому изменение кодировки при десериализации устраняет проблему, например:

>>> d['key'] = 1234
>>> binary = msgpack.dumps(d)
>>> msgpack.loads(binary, encoding = "utf-8")
{'key': 1234}
>>> msgpack.loads(binary, encoding = "utf-8") == d
True

С использованием raw=False флаг как таковой работал у меня на вашем примере:

msgpack.unpackb(binary, raw=False)
# or
msgpack.loads(binary, raw=False)

См. https://msgpack-python.readthedocs.io/en/latest/api.html:

raw (bool) - если true, распаковать msgpack raw в байты Python. В противном случае распакуйте в Python str путем декодирования с кодировкой UTF-8 (по умолчанию).

Попробуйте следующее:

def c_msgpackloads(bin):
    new_d = msgpack.loads(bin)
    new_d = {key.decode('utf-8') if isinstance(key, bytes) else key: new_d[key].decode('utf-8') if isinstance(new_d[key], bytes) else new_d[key] for key in new_d}
    return new_d

Это пользовательская функция загрузки, которая загружает dict и автоматически кодирует bytes ключи и значения для строк utf-8.

Другие вопросы по тегам