JSON格式本身不支持二进制数据。二进制数据必须转义,以便可以将其放在JSON中的字符串元素中(即使用反斜杠转义的双引号中的零或多个Unicode字符)。

转义二进制数据的一个明显方法是使用Base64。然而,Base64有很高的处理开销。此外,它将3个字节扩展为4个字符,导致数据大小增加约33%。

其中一个用例是CDMI云存储API规范的0.8版草案。您可以使用JSON通过REST-Webservice创建数据对象,例如:

PUT /MyContainer/BinaryObject HTTP/1.1
Host: cloud.example.com
Accept: application/vnd.org.snia.cdmi.dataobject+json
Content-Type: application/vnd.org.snia.cdmi.dataobject+json
X-CDMI-Specification-Version: 1.0
{
    "mimetype" : "application/octet-stream",
    "metadata" : [ ],
    "value" :   "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
    IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
    dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
    dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
    ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=",
}

是否有更好的方法和标准方法将二进制数据编码为JSON字符串?


当前回答

参见:http://snia.org/sites/default/files/Multi-part%20MIME%20Extension%20v1.0g.pdf

它描述了一种使用“CDMI内容类型”操作在CDMI客户机和服务器之间传输二进制数据的方法,而不需要对二进制数据进行base64转换。

如果您可以使用“非cdmi内容类型”操作,那么理想的情况是将“数据”传输到对象或从对象传输到对象。然后,元数据可以作为后续的“CDMI内容类型”操作添加/从对象中检索。

其他回答

另一个更新颖的想法是通过uuencode对数据进行编码。大多数情况下都不推荐使用,但它仍然可以作为一种替代方案。(虽然可能不是很严重。)

微笑的格式

它的编码、解码和压缩非常快

速度比较(基于java,但仍有意义):https://github.com/eishay/jvm-serializers/wiki/

此外,它也是JSON的一个扩展,允许您跳过字节数组的base64编码

Smile编码的字符串可以在空间紧缺时进行gzip压缩

UTF-8的问题在于它不是空间利用率最高的编码。另外,一些随机二进制字节序列是无效的UTF-8编码。因此,您不能将随机二进制字节序列解释为一些UTF-8数据,因为它将是无效的UTF-8编码。这种约束对UTF-8编码的好处是,它使其健壮,并且可以定位我们开始查看的任何字节的开始和结束的多字节字符。

因此,如果在[0..]范围内对字节值进行编码。127]在UTF-8编码中只需要一个字节,编码范围为[128..]255]需要2个字节! 比这更糟。在JSON中,控制字符“和\不允许出现在字符串中。因此二进制数据需要进行一些转换才能正确编码。

我们看到的。如果我们假设在二进制数据中均匀分布随机字节值,那么平均而言,一半字节将被编码为一个字节,另一半字节将被编码为两个字节。UTF-8编码的二进制数据将是初始大小的150%。

Base64编码只增长到初始大小的133%。所以Base64编码更有效。

What about using another Base encoding ? In UTF-8, encoding the 128 ASCII values is the most space efficient. In 8 bits you can store 7 bits. So if we cut the binary data in 7 bit chunks to store them in each byte of an UTF-8 encoded string, the encoded data would grow only to 114% of the initial size. Better than Base64. Unfortunately we can't use this easy trick because JSON doesn't allow some ASCII chars. The 33 control characters of ASCII ( [0..31] and 127) and the " and \ must be excluded. This leaves us only 128-35 = 93 chars.

因此,理论上我们可以定义Base93编码,将编码的大小增加到8/log2(93) = 8*log10(2)/log10(93) = 122%。但是Base93编码不像Base64编码那么方便。Base64需要将输入字节序列切割成6位块,因此简单的逐位操作就可以很好地工作。133%比122%高不了多少。

这就是为什么我独立地得出了一个共同的结论,即Base64确实是在JSON中编码二进制数据的最佳选择。我的回答为它提供了一个理由。我同意从性能的角度来看,它不是很吸引人,但也考虑到使用JSON的好处,它的人类可读的字符串表示在所有编程语言中都很容易操作。

如果性能比较关键,则应该考虑使用纯二进制编码来替代JSON。但是对于JSON,我的结论是Base64是最好的。

在Node.js中,你可以在不做任何改变的情况下将Buffer转换成字符串:

const serialized = buffer.toString("binary")
const deserialized = Buffer.from(serialized, "binary")

如果你想通过牺牲大小来获得更高的可靠性,请将"binary"替换为"base64"

参见:http://snia.org/sites/default/files/Multi-part%20MIME%20Extension%20v1.0g.pdf

它描述了一种使用“CDMI内容类型”操作在CDMI客户机和服务器之间传输二进制数据的方法,而不需要对二进制数据进行base64转换。

如果您可以使用“非cdmi内容类型”操作,那么理想的情况是将“数据”传输到对象或从对象传输到对象。然后,元数据可以作为后续的“CDMI内容类型”操作添加/从对象中检索。