TypeError: требуется байтоподобный объект, а не 'str', но у меня есть байтовый объект - PullRequest
0 голосов
/ 21 мая 2019

Я пытаюсь декодировать XML, который выплевывает draw.io. Согласно их документации , это "сжато с использованием стандартного дефлята".

Я использую код, указанный в этом вопросе , для определения инфляции.

import zlib
import base64

def decode_base64_and_inflate( b64string ):
    decoded_data = base64.b64decode( b64string )
    return zlib.decompress( decoded_data , -15)

Пример входного файла:

<mxlibrary>[{"xml":"rVLJboMwEP0aH1t5EUg5Bmhy6ilfQMsULBlMbRNIv77jhSAOSD1UwnjmvdmsN0SU/XI19di96wYUEW9ElEZrF61+KUEpwqlsiKgI5xQP4ZcDlgWWjrWBwf0lgceEe60miAjhucLUopF3JKx7qEjk35MfqvjSg3ux8gfRMwacxgXBjUar9ffZWug/FJi1Hs4QSkb6n7qwDMkFD6NHffiuPjd6Ghrwr6dIz510cBvrT8/OqAJinetRhoqlKW5hiOqE7qjl4KyvkxX4YcuSvgqBFyMZFi+fYJ7vQBbAbB8Y/PT3aFYlLcA4WA71DFAS8wq6B2ceGDLLxnUpIoua0w5k261pNIG1jUD7zN3WA420Iau7bWLgdov6Cw==","w":150,"h":100,"aspect":"fixed"}]</mxlibrary>

Я читаю это так:

from xml.dom import minidom
from urllib.parse import unquote
xmldoc = minidom.parse('samplescratchpad.xml')
buildings = xmldoc.getElementsByTagName('mxlibrary')
# I know eval is bad, but this was being returned as '[...]' instead of 
# just a list.
all_buildings = eval(buildings[0].firstChild.nodeValue)
for building in all_buildings:
    print(type(decode_base64_and_inflate(building['xml'])))
    print(decode_base64_and_inflate(building['xml']))
    print(unquote(decode_base64_and_inflate(building['xml'])))

Вывод первых двух операторов вывода:

<class 'bytes'>
b'%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20value%3D%22%26lt%3Bdiv%20style%3D%26quot%3Bfont-size%3A%209px%3B%26quot%3B%26gt%3BAssembler%26lt%3B%2Fdiv%26gt%3B%26lt%3Bdiv%20style%3D%26quot%3Bfont-size%3A%209px%3B%26quot%3B%26gt%3B15%20x%2010%26lt%3B%2Fdiv%26gt%3B%22%20style%3D%22rounded%3D0%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfontSize%3D9%3Bpoints%3D%5B%5B0%2C0.33%2C1%5D%2C%5B0%2C0.66%2C1%5D%2C%5B1%2C0.5%2C1%5D%2C%5B0.5%2C0.5%2C0%5D%5D%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20width%3D%22150%22%20height%3D%22100%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphModel%3E'

Последняя распечатка, где я пытаюсь преобразовать вышеупомянутое в более стандартный XML, терпит неудачу:

  File "test_deflate.py", line 36, in <module>
    print(unquote(decode_base64_and_inflate(building['xml'])))
  File "/usr/lib/python3.5/urllib/parse.py", line 537, in unquote
    if '%' not in string:
TypeError: a bytes-like object is required, not 'str'

Как это исправить, чтобы у меня работал объект байтов (см. Первые два вывода на печать), когда я пытаюсь unquote его?

Бонус: действительно ли нужно eval на моей линии all_buildings =?

1 Ответ

1 голос
/ 21 мая 2019

У вас проблема, на самом деле, потому что сообщение об ошибке вводит в заблуждение.

Ваш аргумент является a bytes -подобным объектом, но функция unquote использует выражение '%' in string, а '%' является , а не a bytes -подобным объект, который не работает. Либо оба операнда должны быть bytes, либо оба должны быть str.

Python вводит в заблуждение, изменяя первый операнд ('%') на bytes, но поскольку это жестко запрограммированная часть функции, это невозможно. Вместо этого вам нужно превратить другой аргумент в str.

Попробуйте заменить

print(
    unquote(
        decode_base64_and_inflate(building['xml'])
    )
)

с

print(
    unquote(
        decode_base64_and_inflate(
            building['xml']
        ).decode('utf8')
    )
)

Это будет декодировать bytes как строку Unicode в кодировке UTF8 (скорее всего, правильное кодирование) и даст str, который можно передать в unquote().


Edit: причина, по которой Python использует это сообщение об ошибке, заключается в том, что оператор in внутренне является вызовом метода для второго операнда; то есть a in b оценивается как b.__contains__(a). Следовательно, b определяет, какой тип a разрешен иметь, а не наоборот - что означает, что Python скажет вам изменить тип первого операнда, а не сообщит вам об изменении второго.

...