[json] JSON 문자열의 이진 데이터. Base64보다 나은 것

JSON 형식은 기본적으로 바이너리 데이터를 지원하지 않습니다. 바이너리 데이터는 JSON에서 문자열 요소 (백 슬래시 이스케이프를 사용하여 큰 따옴표로 묶은 0 개 이상의 유니 코드 문자)에 배치 될 수 있도록 이스케이프되어야합니다.

이진 데이터를 이스케이프 처리하는 확실한 방법은 Base64를 사용하는 것입니다. 그러나 Base64는 처리 오버 헤드가 높습니다. 또한 3 바이트를 4 자로 확장하여 데이터 크기를 약 33 % 증가시킵니다.

이에 대한 한 가지 사용 사례는 CDMI 클라우드 스토리지 API 사양 의 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 문자열로 인코딩하는 더 좋은 방법과 표준 방법이 있습니까?



답변

JSON 사양에 따라 1 바이트로 표시 될 수있는 94 개의 유니 코드 문자가 있습니다 (JSON이 UTF-8로 전송 된 경우). 이를 염두에두고 공간적으로 할 수있는 최선의 방법 은 4 바이트를 5 문자로 나타내는 base85 입니다. 그러나 이것은 base64에 비해 7 % 개선 된 것이며 계산 비용이 더 비싸며 base64보다 구현이 덜 일반적이므로 승리하지 않을 것입니다.

모든 입력 바이트를 U + 0000-U + 00FF의 해당 문자에 간단히 매핑 한 다음 JSON 표준에 필요한 최소 인코딩을 수행하여 해당 문자를 전달할 수도 있습니다. 여기서 장점은 필요한 디코딩이 내장 함수를 능가하는 것이 아니라 공간 효율성이 나쁘다는 것입니다 .105 % 확장 (모든 입력 바이트가 동일 할 가능성이있는 경우) 대 base85의 경우 25 % 또는 base64의 경우 33 %입니다.

최종 판결 : base64는 대체적 으로 보증 하기에 충분 하고 일반적이며 쉽고, 나쁘지 않다는 근거에서 승리 합니다.

참조 : Base91Base122


답변

같은 문제가 발생하여 multipart / form-data 라는 솔루션을 공유한다고 생각했습니다 .

여러 부분으로 된 양식을 보내면 먼저 JSON 메타 데이터 를 문자열로 보낸 다음 Content-Disposition 이름으로 인덱싱 된 원시 이진 (이미지, wav 등)으로 별도로 보냅니다 .

다음 은 obj-c 에서이 작업을 수행하는 방법에 대한 유용한 자습서 이며 문자열 데이터를 양식 경계로 분할하고 이진 데이터와 구분하는 방법을 설명하는 블로그 기사 입니다.

실제로해야 할 유일한 변경 사항은 서버 쪽입니다. POST의 바이너리 데이터를 적절하게 참조 해야하는 메타 데이터를 캡처해야합니다 (Content-Disposition 경계를 사용하여).

서버 측에서 추가 작업이 필요하지만 많은 이미지 또는 큰 이미지를 보내는 경우 그만한 가치가 있습니다. 원하는 경우 이것을 gzip 압축과 결합하십시오.

base64로 인코딩 된 데이터를 보내는 IMHO는 해킹입니다. RFC multipart / form-data는 텍스트 또는 메타 데이터와 함께 이진 데이터를 보내는 것과 같은 문제를 위해 만들어졌습니다.


답변

UTF-8의 문제점은 공간 효율적인 인코딩이 아니라는 것입니다. 또한 일부 임의의 이진 바이트 시퀀스는 잘못된 UTF-8 인코딩입니다. 따라서 임의의 이진 바이트 시퀀스를 UTF-8 데이터로 해석 할 수는 없습니다. UTF-8 인코딩이 유효하지 않기 때문입니다. UTF-8 인코딩에 대한 이러한 제약의 이점은 견고하고 멀티 바이트 문자를 찾아서 시작하는 모든 바이트를 찾고 종료 할 수 있다는 것입니다.

결과적으로 [0..127] 범위의 바이트 값을 UTF-8 인코딩으로 인코딩하는 데 1 바이트 만 필요한 경우 [128..255] 범위의 바이트 값을 인코딩하면 2 바이트가 필요합니다! 그보다 더 나쁘다. JSON에서 제어 문자 “및 \는 문자열에 표시 할 수 없으므로 이진 데이터를 올바르게 인코딩하려면 일부 변환이 필요합니다.

보자. 바이너리 데이터에 균일하게 분포 된 랜덤 바이트 값을 가정하면 평균적으로 바이트의 절반이 1 바이트로 인코딩되고 나머지 절반은 2 바이트로 인코딩됩니다. UTF-8로 인코딩 된 이진 데이터의 초기 크기는 150 %입니다.

Base64 인코딩은 초기 크기의 133 %로만 증가합니다. 따라서 Base64 인코딩이 더 효율적입니다.

다른 Base 인코딩을 사용하는 것은 어떻습니까? UTF-8에서는 128 ASCII 값을 인코딩하는 것이 가장 공간 효율적입니다. 8 비트에서는 7 비트를 저장할 수 있습니다. 따라서 바이너리 데이터를 7 비트 청크로 잘라 UTF-8 인코딩 문자열의 각 바이트에 저장하면 인코딩 된 데이터는 초기 크기의 114 %로만 커집니다. Base64보다 낫습니다. 불행히도 JSON은 ASCII 문자를 허용하지 않기 때문에이 쉬운 트릭을 사용할 수 없습니다. ASCII의 33 개 제어 문자 ([0..31] 및 127)와 “및 \는 제외해야합니다.이 경우 128-35 = 93 자만 남습니다.

이론적으로 우리는 인코딩 된 크기를 8 / log2 (93) = 8 * log10 (2) / log10 (93) = 122 %로 증가시키는 Base93 인코딩을 정의 할 수 있습니다. 그러나 Base93 인코딩은 Base64 인코딩만큼 편리하지 않습니다. Base64는 간단한 비트 단위 연산이 잘 작동하는 입력 바이트 시퀀스를 6 비트 청크로 잘라 내야합니다. 133 % 외에는 122 %를 넘지 않습니다.

그렇기 때문에 Base64가 바이너리 데이터를 JSON으로 인코딩하는 가장 좋은 선택이라는 일반적인 결론에 독립적으로 도달했습니다. 내 대답은 그것에 대한 정당성을 제시합니다. 성능 측면에서 그다지 매력적이지는 않지만 모든 프로그래밍 언어에서 조작하기 쉬운 사람이 읽을 수있는 문자열 표현으로 JSON을 사용하는 이점도 고려하십시오.

순수한 이진 인코딩보다 성능이 중요한 경우 JSON을 대체하는 것으로 간주해야합니다. 그러나 JSON을 사용하면 Base64가 가장 좋습니다.


답변

BSON (Binary JSON)이 도움이 될 수 있습니다.
http://en.wikipedia.org/wiki/BSON

편집 : 참고로 .NET 라이브러리 json.net 은 C # 서버 쪽 사랑을 찾고 있다면 bson 읽기 및 쓰기를 지원합니다.


답변

대역폭 문제를 해결하려면 먼저 클라이언트 측에서 데이터를 압축 한 다음 base64-it로 압축하십시오.

이러한 마술의 좋은 예는 http://jszip.stuartk.co.uk/있으며이 주제에 대한 자세한 설명 은 Gzip의 JavaScript 구현에 있습니다.


답변

yEnc가 당신을 위해 일할 수 있습니다 :

http://en.wikipedia.org/wiki/Yenc

“yEnc는 [텍스트]로 이진 파일을 전송하기위한 이진-텍스트 인코딩 체계입니다. 8 비트 확장 ASCII 인코딩 방법을 사용하여 이전 US-ASCII 기반 인코딩 방법보다 오버 헤드를 줄입니다. yEnc의 오버 헤드는 종종 uuencode 및 Base64와 같은 6 비트 인코딩 방법의 33 % –40 % 오버 헤드와 비교할 때 각 바이트 값은 평균적으로 거의 동일한 빈도로 1 ~ 2 %로 나타납니다. … 2003 년까지 yEnc는 사실상 표준이되었습니다. Usenet의 바이너리 파일 인코딩 시스템. “

그러나 yEnc는 8 비트 인코딩이므로 JSON 문자열에 저장하면 원래 이진 데이터를 저장하는 것과 동일한 문제가 있습니다. 순진한 방법은 약 100 % 확장을 의미하므로 base64보다 나쁩니다.


답변

base64의 확장 률이 ~ 33 % 인 것은 사실이지만 처리 오버 헤드가 이보다 훨씬 높다는 것은 아닙니다. 실제로 사용중인 JSON 라이브러리 / 툴킷에 따라 다릅니다. 인코딩 및 디코딩은 간단한 작업이며 JSON 문자 인코딩 (JSON이 UTF-8 / 16 / 32 만 지원)으로 최적화 할 수 있습니다. base64 문자는 항상 JSON 문자열 항목의 단일 바이트입니다. 예를 들어 Java 플랫폼에는 작업을보다 효율적으로 수행 할 수있는 라이브러리가 있으므로 오버 헤드는 주로 확장 된 크기 때문입니다.

나는 두 가지 이전 답변에 동의합니다.

  • base64는 간단하고 일반적으로 사용되는 표준이므로 JSON과 함께 사용하기에 더 나은 것을 찾을 수는 없습니다 (base-85는 포스트 스크립트 등에서 사용되지만 이점에 대해서는 생각할 때 이점이 거의 없습니다)
  • 인코딩 전 (및 디코딩 후) 압축은 사용하는 데이터에 따라 많은 의미가있을 수 있습니다.