python 3.6解码dict字节到json不工作

时间:2018-01-24 17:04:27

标签: python json utf-8

我有以下字节格式的数据,

{'command': 'MESSAGE', 'body': b'\x04\x08{\x0b:\tbody"n\x04\x08{\x08:\tdata{\n:\x0bstdout"\x14output-data\n:\rexitcodei\x00:\x0bstderr"\x00:\x0boutput0:\nerror0:\x0estatusmsg"\x07OK:\x0fstatuscodei\x00:\rsenderid"\x13server1:\thash"%903ff3bf7e9212105df23c92dd8f718a:\x10senderagent"\ntoktok:\x0cmsgtimel+\x07\xf6\xb9hZ:\x0erequestid"%7a358c34f8f9544sd2350c99953d0eec', 'rawHeaders': [('content-length', '264'), ('expires', '1516812860547'), ('destination', '/queue/test.queue'), ('priority', '4'), ('message-id', '12345678'), ('content-type', 'text/plain; charset=UTF-8'), ('timestamp', '1516812790347')]}

并尝试解码并将其转换为JSON格式的数据,但它无法正常工作。我尝试使用data.decode()data.decode('utf-8')并尝试了json.loads,但没有任何效果。

当我尝试data.decode('utf-8')时出现以下错误,

'utf-8' codec can't decode byte 0xf6 in position 215: invalid start byte

当我尝试使用data.decode('ascii')时出现以下错误,

'ascii' codec can't decode byte 0xa9 in position 215: ordinal not in range(128)

我很困惑自己是在做这种数据转换和解析是否正确或缺少任何东西。

更新1:

刚才发现这个数据是使用Ruby和PSK安全插件生成的,而且这个消息对象有.decode! public_method。那么有没有办法在python中使用相同的public_method来解码它,或者如果可能的话使用PSK也没关系。

2 个答案:

答案 0 :(得分:3)

JSON是一种Unicode格式,它不能(轻松透明地)容纳任意字节字符串。你可以轻松做的是以某种文本格式保存blob - base64是一种常见的选择。但是,当然,所有制作人和消费者都需要分享对如何解码blob的理解,而不是仅仅将其用作文本。

Python 3.5.1 (default, Dec 26 2015, 18:08:53)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> d = {'json': True, 'data': b'\xff\xff\xff'}
>>> json.dumps(d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  ... yada yada yada ...
TypeError: b'\xff\xff\xff' is not JSON serializable
>>> import base64
>>> base64.b64encode(d['data'])
b'////'
>>> base64.b64encode(d['data']).decode('ascii')
'////'
>>> d['data_base64'] = base64.b64encode(d['data']).decode('ascii')
>>> del d['data']
>>> json.dumps(d)
'{"json": true, "data_base64": "////"}'

我非常专门为编码字段使用了不同的名称,以避免让任何消费者认为base64 blob是data成员的实际值。

随机二进制数据绝对不是有效的UTF-8,因此显然无法使用该编解码器进行解码。 UTF-8是一种非常特殊的Unicode文本编码,它不能真正用于那些不完全相同的数据。您通常编码,而不是解码,二进制数据用于传输,并且需要在另一端将某些内容解码回字节。在这里,该编码是base64,但任何可以透明地将二进制文本嵌入为文本的内容都可以。

答案 1 :(得分:1)

如果这是您的数据并且您尝试通过JSON可序列化格式对其进行往返,则可以执行此操作:

import json
import base64

data = {'command': 'MESSAGE',
        'body': b'\x04\x08{\x0b:\tbody"n\x04\x08{\x08:\tdata{\n:\x0bstdout"\x14output-data\n:\rexitcodei\x00:\x0bstderr"\x00:\x0boutput0:\nerror0:\x0estatusmsg"\x07OK:\x0fstatuscodei\x00:\rsenderid"\x13server1:\thash"%903ff3bf7e9212105df23c92dd8f718a:\x10senderagent"\ntoktok:\x0cmsgtimel+\x07\xf6\xb9hZ:\x0erequestid"%7a358c34f8f9544sd2350c99953d0eec',
        'rawHeaders': [('content-length', '264'), ('expires', '1516812860547'), ('destination', '/queue/test.queue'), ('priority', '4'), ('message-id', '12345678'), ('content-type', 'text/plain; charset=UTF-8'), ('timestamp', '1516812790347')]}

# Make a copy of the original data and base64 for bytes content.
datat = data.copy()
datat['body'] = base64.encodebytes(datat['body']).decode('ascii')

# Now it serializes
jsondata = json.dumps(datat)
print(jsondata)

# Read it back and decode the base64 field back to its original bytes value
data2 = json.loads(jsondata)
data2['body'] = base64.decodebytes(data2['body'].encode('ascii'))

# For comparison, since the tuples in 'rawHeaders' are read back as lists by JSON,
# convert the list entries back to tuples.
data2['rawHeaders'] = [tuple(x) for x in data2['rawHeaders']]

# Did the data restore correctly?
print(data == data2)

输出:

{"command": "MESSAGE", "body": "BAh7CzoJYm9keSJuBAh7CDoJZGF0YXsKOgtzdGRvdXQiFG91dHB1dC1kYXRhCjoNZXhpdGNvZGVp\nADoLc3RkZXJyIgA6C291dHB1dDA6CmVycm9yMDoOc3RhdHVzbXNnIgdPSzoPc3RhdHVzY29kZWkA\nOg1zZW5kZXJpZCITc2VydmVyMToJaGFzaCIlOTAzZmYzYmY3ZTkyMTIxMDVkZjIzYzkyZGQ4Zjcx\nOGE6EHNlbmRlcmFnZW50Igp0b2t0b2s6DG1zZ3RpbWVsKwf2uWhaOg5yZXF1ZXN0aWQiJTdhMzU4\nYzM0ZjhmOTU0NHNkMjM1MGM5OTk1M2QwZWVj\n", "rawHeaders": [["content-length", "264"], ["expires", "1516812860547"], ["destination", "/queue/test.queue"], ["priority", "4"], ["message-id", "12345678"], ["content-type", "text/plain; charset=UTF-8"], ["timestamp", "1516812790347"]]}
True