全部  命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义  
编码

根据ECMA-404

(in Introduction) JSON text is a sequence of Unicode code points.

翻译:JSON文本是Unicode码点的序列。

较早的RFC4627申明:

(in §3) JSON text SHALL be encoded in Unicode. The default encoding is UTF-8.

翻译:JSON文本应该以Unicode编码。缺省的编码为UTF-8。

(in §6) JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON is written in UTF-8, JSON is 8bit compatible. When JSON is written in UTF-16 or UTF-32, the binary content-transfer-encoding must be used.

翻译:JSON可使用UTF-8、UTF-16或UTF-18表示。当JSON以UTF-8写入,该JSON是8位兼容的。当JSON以UTF-16或UTF-32写入,就必须使用二进制的内容传送编码。

RapidJSON支持多种编码。它也能检查JSON的编码,以及在不同编码中进行转码。所有这些功能都是在内部实现,无需使用外部的程序库(如ICU)。

Unicode

根据 Unicode的官方网站: >Unicode给每个字符提供了一个唯一的数字, 不论是什么平台、 不论是什么程序、 不论是什么语言。

这些唯一数字称为码点(code point),其范围介乎0x00x10FFFF之间。

Unicode转换格式

存储Unicode码点有多种编码方式。这些称为Unicode转换格式(Unicode Transformation Format, UTF)。RapidJSON支持最常用的UTF,包括:

  • UTF-8:8位可变长度编码。它把一个码点映射至1至4个字节。
  • UTF-16:16位可变长度编码。它把一个码点映射至1至2个16位编码单元(即2至4个字节)。
  • UTF-32:32位固定长度编码。它直接把码点映射至单个32位编码单元(即4字节)。

对于UTF-16及UTF-32来说,字节序(endianness)是有影响的。在内存中,它们通常都是以该计算机的字节序来存储。然而,当要储存在文件中或在网上传输,我们需要指明字节序列的字节序,是小端(little endian, LE)还是大端(big-endian, BE)。

RapidJSON通过rapidjson/encodings.h中的struct去提供各种编码:

namespace rapidjson {
template<typename CharType = char>
struct UTF8;
template<typename CharType = wchar_t>
struct UTF16;
template<typename CharType = wchar_t>
struct UTF16LE;
template<typename CharType = wchar_t>
struct UTF16BE;
template<typename CharType = unsigned>
struct UTF32;
template<typename CharType = unsigned>
struct UTF32LE;
template<typename CharType = unsigned>
struct UTF32BE;
} // namespace rapidjson

对于在内存中的文本,我们正常会使用UTF8UTF16UTF32。对于处理经过I/O的文本,我们可使用UTF8UTF16LEUTF16BEUTF32LEUTF32BE

当使用DOM风格的API,GenericValue<Encoding>GenericDocument<Encoding>里的Encoding模板参数是用于指明内存中存储的JSON字符串使用哪种编码。因此通常我们会在此参数中使用UTF8UTF16UTF32。如何选择,视乎应用软件所使用的操作系统及其他程序库。例如,Windows API使用UTF-16表示Unicode字符,而多数的Linux发行版本及应用软件则更喜欢UTF-8。

使用UTF-16的DOM声明例子:

typedef GenericDocument<UTF16<> > WDocument;
typedef GenericValue<UTF16<> > WValue;

可以在DOM's Encoding一节看到更详细的使用例子。

字符类型

从之前的声明中可以看到,每个编码都有一个CharType模板参数。这可能比较容易混淆,实际上,每个CharType存储一个编码单元,而不是一个字符(码点)。如之前所谈及,在UTF-8中一个码点可能会编码成1至4个编码单元。

对于UTF16(LE|BE)UTF32(LE|BE)来说,CharType必须分别是一个至少2及4字节的整数类型。

注意C++11新添了char16_tchar32_t类型,也可分别用于UTF16UTF32

AutoUTF

上述所介绍的编码都是在编译期静态挷定的。换句话说,使用者必须知道内存或流之中使用了哪种编码。然而,有时候我们可能需要读写不同编码的文件,而且这些编码需要在运行时才能决定。

AutoUTF是为此而设计的编码。它根据输入或输出流来选择使用哪种编码。目前它应该与EncodedInputStreamEncodedOutputStream结合使用。

ASCII

虽然JSON标准并未提及ASCII,有时候我们希望写入7位的ASCII JSON,以供未能处理UTF-8的应用程序使用。由于任JSON都可以把Unicode字符表示为\uXXXX转义序列,JSON总是可用ASCII来编码。

以下的例子把UTF-8的DOM写成ASCII的JSON:

using namespace rapidjson;
Document d; // UTF8<>
// ...
StringBuffer buffer;
d.Accept(writer);
std::cout << buffer.GetString();

ASCII可用于输入流。当输入流包含大于127的字节,就会导致kParseErrorStringInvalidEncoding错误。

ASCII *不能* 用于内存(Document的编码,或Reader的目标编码),因为它不能表示Unicode码点。

校验及转码

当RapidJSON解析一个JSON时,它能校验输入JSON,判断它是否所标明编码的合法序列。要开启此选项,请把kParseValidateEncodingFlag加入parseFlags模板参数。

若输入编码和输出编码并不相同,ReaderWriter会算把文本转码。在这种情况下,并不需要kParseValidateEncodingFlag,因为它必须解码输入序列。若序列不能被解码,它必然是不合法的。

转码器

虽然RapidJSON的编码功能是为JSON解析/生成而设计,使用者也可以“滥用”它们来为非JSON字符串转码。

以下的例子把UTF-8字符串转码成UTF-16:

#include "rapidjson/encodings.h"
using namespace rapidjson;
const char* s = "..."; // UTF-8 string
StringStream source(s);
bool hasError = false;
while (source.Peak() != '\0')
if (!Transcoder::Transcode<UTF8<>, UTF16<> >(source, target)) {
hasError = true;
break;
}
if (!hasError) {
const wchar_t* t = target.GetString();
// ...
}

你也可以用AutoUTF及对应的流来在运行时设置内源/目的之编码。