JSON format本身不支持二进制数据 . 必须对二进制数据进行转义,以便可以将它放入JSON中的字符串元素(即使用反斜杠转义的双引号中的零个或多个Unicode字符) .
转义二进制数据的一个明显方法是使用Base64 . 但是,Base64具有很高的处理开销 . 它还将3个字节扩展为4个字符,从而使数据量增加了大约33% .
一个用例就是CDMI cloud storage API specification的v0.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字符串?
16 回答
根据JSON规范,有94个Unicode字符可以表示为一个字节(如果您的JSON以UTF-8格式传输) . 考虑到这一点,我认为你可以做的最好的空间是base85,它代表四个字节为五个字符 . 然而,这比base64仅提高了7%,它可能不是一场胜利 .
您也可以简单地将每个输入字节映射到U 0000-U 00FF中的相应字符,然后执行JSON标准所需的最小编码以传递这些字符;这里的优点是所需的解码不超过内置函数,但空间效率很差 - 105%的扩展(如果所有输入字节都相同),base85为25%,base64为33% .
最终判决:在我看来,base64胜利,理由是它是常见的,简单的,并且不足以保证更换 .
另见:Base91
我知道这是一个将近6年的问题,但我遇到了同样的问题,并认为我会分享一个解决方案: multipart/form-data.
通过发送多部分表单,您首先将您的JSON元数据作为字符串发送,然后分别发送为 Content-Disposition 名称索引的原始二进制文件(图像,wavs等) .
关于如何在obj-c中执行此操作,这是一个很好的tutorial,这里是a blog article,它解释了如何使用表单边界对字符串数据进行分区,并将其与二进制数据分开 .
您真正需要做的唯一改变是在服务器端;您必须捕获应该适当引用POST的二进制数据的元数据(通过使用Content-Disposition边界) .
当然,它需要在服务器端进行额外的工作,但如果您要发送许多图像或大图像,这是值得的 . 如果需要,可以将其与gzip压缩相结合 .
恕我直言,发送base64编码数据是一个黑客;为这样的问题创建了RFC multipart / form-data:结合文本或元数据发送二进制数据 .
BSON(二进制JSON)可能适合您 . http://en.wikipedia.org/wiki/BSON
编辑:仅供参考.NET库json.net支持读写bson,如果你正在寻找一些C#服务器端爱 .
UTF-8的问题在于它不是最节省空间的编码 . 此外,一些随机二进制字节序列是无效的UTF-8编码 . 因此,您不能将随机二进制字节序列解释为某些UTF-8数据,因为它将是无效的UTF-8编码 . 这种对UTF-8编码的约束的好处在于它使得它很可靠并且可以定位多字节字符开始和结束我们开始查看的任何字节 .
因此,如果在[0..127]范围内编码字节值只需要UTF-8编码中的一个字节,则编码[128..255]范围内的字节值将需要2个字节!比那更糟糕 . 在JSON中,控制字符“和\”不允许出现在字符串中 . 因此二进制数据需要进行一些转换才能正确编码 .
让我们看看 . 如果我们在二进制数据中假设均匀分布的随机字节值,那么平均来说,一半字节将在一个字节中编码,另一半在两个字节中编码 . UTF-8编码的二进制数据将具有初始大小的150% .
Base64编码仅增长到初始大小的133% . 所以Base64编码效率更高 .
那么使用其他Base编码呢?在UTF-8中,对128个ASCII值进行编码是最节省空间的 . 在8位中,您可以存储7位 . 因此,如果我们以7位块的形式剪切二进制数据以将它们存储在UTF-8编码字符串的每个字节中,则编码数据将仅增长到初始大小的114% . 比Base64好 . 不幸的是,我们不能使用这个简单的技巧,因为JSON不允许一些ASCII字符 . ASCII([0..31]和127)的33个控制字符和“和\”必须被排除 . 这使得我们只有128-35 = 93个字符 .
因此理论上我们可以定义一个Base93编码,它将编码大小增加到8 / log2(93)= 8 * log10(2)/ log10(93)= 122% . 但Base93编码不如Base64编码方便 . Base64需要以6位块的形式剪切输入字节序列,以便简单的按位运算 . 除133%外,不超过122% .
这就是我独立得出的结论,即Base64确实是用JSON编码二进制数据的最佳选择 . 我的回答是一个为它辩护 . 我同意从性能的角度来看它并不是很有吸引力,但也要考虑使用JSON的好处,它的人类可读字符串表示易于在所有编程语言中操作 .
如果性能至关重要,则应将纯二进制编码视为JSON的替代 . 但是对于JSON,我的结论是Base64是最好的 .
如果您处理带宽问题,请先尝试在客户端压缩数据,然后再使用base64-it .
这种魔法的好例子是在http://jszip.stuartk.co.uk/,对这个主题的更多讨论是在JavaScript implementation of Gzip
yEnc可能适合你:
http://en.wikipedia.org/wiki/Yenc
Smile format
编码,解码和压缩速度非常快
速度比较(基于java但有意义):https://github.com/eishay/jvm-serializers/wiki/
它也是JSON的扩展,允许您跳过字节数组的base64编码
当空间很重要时,可以对微笑编码的字符串进行压缩
虽然base64确实具有~33%的扩展速率,但是处理开销并不一定比这更重要:它实际上取决于您正在使用的JSON库/工具包 . 编码和解码是简单的直接操作,甚至可以通过字符编码进行优化(因为JSON仅支持UTF-8/16/32) - 对于JSON字符串条目,base64字符始终是单字节 . 例如,在Java平台上,有些库可以相当有效地完成工作,因此开销主要是由于扩展的大小 .
我同意两个早先的答案:
编码前(和解码后)的
由于您正在寻找能够将二进制数据机制化为严格基于文本且格式非常有限的能力,因此我认为与您期望使用JSON维护的便利性相比,Base64的开销很小 . 如果需要考虑处理能力和吞吐量,那么您可能需要重新考虑文件格式 .
( Edit 7 years later: Google Gears已经不见了 . 忽略这个答案 . )
Google Gears团队遇到了缺乏二进制数据类型的问题,并试图解决它:
也许你可以以某种方式编织它 .
只是为了讨论增加资源和复杂性的立场 . 由于做PUT / POST和PATCH用于存储新资源并改变它们,因此应该记住,内容传输是存储的内容的精确表示,并且通过发出GET操作来接收 .
多部分消息通常用作救世主,但出于简单原因和更复杂的任务,我更喜欢将内容作为一个整体给出的想法 . 这是自我解释,很简单 .
是的,JSON是一种瘫痪的东西,但最终JSON本身就是冗长的 . 映射到BASE64的开销很小 .
正确使用多部分消息,必须要么拆除要发送的对象,使用属性路径作为自动组合的参数名称,要么需要创建另一个协议/格式来表示有效负载 .
也喜欢BSON方法,这并不像人们希望的那样广泛和容易地支持 .
基本上,我们只是错过了一些东西,但嵌入二进制数据作为base64是很好的 Build 和方式去除非你真的已经确定需要进行真正的二进制传输(这通常不是这种情况) .
数据类型确实令人担忧我已经测试了从RESTful资源发送有效负载的不同场景 . 对于编码,我使用了Base64(Apache)和压缩GZIP(java.utils.zip . *) . 有效载荷包含有关电影,图像和音频文件的信息 . 我压缩并编码了图像和音频文件,这大大降低了性能 . 压缩前的编码结果很好 . 图像和音频内容以编码和压缩字节[]的形式发送 .
参考:http://snia.org/sites/default/files/Multi-part%20MIME%20Extension%20v1.0g.pdf
它描述了一种使用“CDMI内容类型”操作在CDMI客户端和服务器之间传输二进制数据的方法,而无需对二进制数据进行base64转换 .
如果您可以使用“非CDMI内容类型”操作,则最好将“数据”传输到对象或从对象传输 . 随后可以将元数据作为后续的“CDMI内容类型”操作添加到对象或从对象中检索元数据 .
如果你正在使用Node,我认为最有效和最简单的方法是转换为UTF16:
您可以通过以下方式获取数据:
使用 base128 实际上可以产生有效的JSON字符串 - HERE is EVERYTHING - 所有细节和JS程序进行编码/解码(输出base128数组比输入字节大15%)阵列)
我的解决方案现在,XHR2正在使用ArrayBuffer . ArrayBuffer作为二进制序列包含多部分内容,视频,音频,图形,文本等,具有多种内容类型 . 一体化响应 .
在现代浏览器中,为不同的组件提供DataView,StringView和Blob . 另请参阅:http://rolfrost.de/video.html了解更多详情 .