RapidJSONでは、rapidjson::StreamはJSONを読み書きするための概念です。まず、用意されているストリームの使い方を示し、次に独自のストリームの作り方を説明します。

メモリーストリーム

メモリーストリームは、JSONをメモリー内へ保存します。

StringStream(入力)

StringStreamは、最も基本的な入力ストリームです。メモリー内に保存された、完全な読み取り専用のJSONを表します。rapidjson/rapidjson.hに定義されています。

#include "rapidjson/document.h" // will include "rapidjson/rapidjson.h"
using namespace rapidjson;
// ...
const char json[] = "[1, 2, 3, 4]";
StringStream s(json);
GenericDocument & ParseStream(InputStream &is)
入力ストリームからJSONテキストを解析します(エンコーディング変換あり)。
Definition document.h:2155
RapidJSONの主な名前空間。
Definition rapidjson.h:374
GenericStringStream< UTF8< char > > StringStream
UTF8エンコーディングの文字列ストリーム。
Definition fwd.h:49
GenericDocument< UTF8<> > Document
UTF8エンコーディングのGenericDocument。
Definition document.h:2402

これは非常によくある使い方なので、上とまったく同じ処理を行うDocument::Parse(const char*)が用意されています。

// ...
const char json[] = "[1, 2, 3, 4]";
d.Parse(json);
GenericDocument & Parse(const typename SourceEncoding::Ch *str)
読み取り専用文字列からJSONテキストを解析します(エンコーディング変換あり)。
Definition document.h:2221

StringStreamはGenericStringStream<UTF8<> >のtypedefです。ストリームの文字集合を表すために、別のエンコーディングを使うこともできます。

StringBuffer(出力)

StringBufferは、簡単な出力ストリームです。JSON全体を書き込むためのメモリーバッファーを確保します。バッファーの取得にはGetString()を使います。

#include "rapidjson/stringbuffer.h"
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
const char* output = buffer.GetString();
bool Accept(Handler &handler) const
この値に対応するイベントをHandlerへ渡します。
Definition document.h:1768
JSONライター。
Definition writer.h:87
GenericStringBuffer< UTF8< char >, CrtAllocator > StringBuffer
UTF8エンコーディングの文字列バッファー。
Definition fwd.h:61

バッファーがいっぱいになると、容量を自動的に増やします。既定の容量は256文字です(UTF8では256バイト、UTF16では512バイトなど)。アロケーターと初期容量を指定できます。

StringBuffer buffer1(0, 1024); // Use its allocator, initial size = 1024
StringBuffer buffer2(allocator, 1024);

既定では、StringBufferは内部アロケーターのインスタンスを作成します。

同様に、StringBufferはGenericStringBuffer<UTF8<> >のtypedefです。

ファイルストリーム

ファイルからJSONを解析する場合は、JSON全体をメモリーへ読み込み、上のStringStreamを使えます。

ただし、JSONが大きい場合やメモリーが限られる場合には、FileReadStreamを使えます。ファイルからJSONの一部だけをバッファーへ読み込み、その部分を解析します。バッファー内の文字を使い切ると、ファイルから次の部分を読み込みます。

FileReadStream(入力)

FileReadStreamは、FILEポインターを通じてファイルを読みます。バッファーは利用者が用意する必要があります。

#include "rapidjson/filereadstream.h"
#include <cstdio>
using namespace rapidjson;
FILE* fp = fopen("big.json", "rb"); // non-Windows use "r"
char readBuffer[65536];
FileReadStream is(fp, readBuffer, sizeof(readBuffer));
fclose(fp);
fread()を使う入力用のファイルバイトストリーム。
Definition filereadstream.h:34

文字列ストリームと異なり、FileReadStreamはバイトストリームなので、エンコーディングは扱いません。ファイルがUTF-8でなければ、バイトストリームをEncodedInputStreamで包めます。これはすぐ後で説明します。

ファイルだけでなく、FileReadStreamでstdinも読み取れます。

FileWriteStream(出力)

FileWriteStreamは、バッファー付きの出力ストリームです。使い方はFileReadStreamとよく似ています。

#include "rapidjson/filewritestream.h"
#include <cstdio>
using namespace rapidjson;
d.Parse(json);
// ...
FILE* fp = fopen("output.json", "wb"); // non-Windows use "w"
char writeBuffer[65536];
FileWriteStream os(fp, writeBuffer, sizeof(writeBuffer));
d.Accept(writer);
fclose(fp);
fread()を使う入力用のCファイルストリームのラッパー。
Definition filewritestream.h:32

出力をstdoutへ送ることもできます。

iostreamラッパー

利用者の要望に応え、RapidJSONはstd::basic_istreamとstd::basic_ostreamの公式ラッパーを提供しています。ただし、上記の他のストリームより性能が大幅に低くなることに注意してください。

IStreamWrapper

IStreamWrapperは、std::istringstream、std::stringstream、std::ifstream、std::fstreamなど、std::istreamから派生したクラスをRapidJSONの入力ストリームへ変換します。

#include <rapidjson/istreamwrapper.h>
#include <fstream>
using namespace rapidjson;
using namespace std;
ifstream ifs("test.json");
IStreamWrapper isw(ifs);
d.ParseStream(isw);

std::wistreamから派生したクラスには、WIStreamWrapperを使ってください。

OStreamWrapper

同様に、OStreamWrapperは、std::ostringstream、std::stringstream、std::ofstream、std::fstreamなど、std::ostreamから派生したクラスをRapidJSONの入力ストリームへ変換します。

#include <rapidjson/ostreamwrapper.h>
#include <rapidjson/writer.h>
#include <fstream>
using namespace rapidjson;
using namespace std;
d.Parse(json);
// ...
ofstream ofs("output.json");
OStreamWrapper osw(ofs);
d.Accept(writer);

std::wostreamから派生したクラスには、WOStreamWrapperを使ってください。

エンコーディング付きストリーム

エンコーディング付きストリーム自体はJSONを保持せず、バイトストリームを包んで基本的な符号化・復号機能を提供します。

前述のように、UTF-8のバイトストリームは直接読み取れます。ただし、UTF-16とUTF-32にはバイト順の問題があります。バイト順を正しく扱うには、読み取り時にバイトを文字(UTF-16ならwchar_tなど)へ変換し、書き込み時には文字をバイトへ変換する必要があります。

さらに、バイト順マーク(BOM)も扱う必要があります。バイトストリームから読み取るときは、BOMがあれば検出するか読み飛ばします。バイトストリームへ書き込むときは、任意でBOMを出力できます。

ストリームのエンコーディングがコンパイル時に分かる場合は、EncodedInputStreamとEncodedOutputStreamを使えます。JSONストリームがUTF-8、UTF-16LE、UTF-16BE、UTF-32LE、UTF-32BEのいずれかで、それが実行時にしか分からない場合は、AutoUTFInputStreamとAutoUTFOutputStreamを使えます。これらのストリームはrapidjson/encodedstream.hに定義されています。

これらのエンコーディング付きストリームは、ファイル以外のストリームにも適用できます。たとえば、メモリー内のファイルや独自のバイトストリームを包めます。

EncodedInputStream

EncodedInputStreamにはテンプレート引数が2つあります。第1引数は、rapidjson/encodings.hに定義されたUTF8やUTF16LEなどのEncodingクラスです。第2引数は、包むストリームのクラスです。

#include "rapidjson/filereadstream.h" // FileReadStream
#include "rapidjson/encodedstream.h" // EncodedInputStream
#include <cstdio>
using namespace rapidjson;
FILE* fp = fopen("utf16le.json", "rb"); // non-Windows use "r"
char readBuffer[256];
FileReadStream bis(fp, readBuffer, sizeof(readBuffer));
EncodedInputStream<UTF16LE<>, FileReadStream> eis(bis); // wraps bis into eis
Document d; // Document is GenericDocument<UTF8<> >
d.ParseStream<0, UTF16LE<> >(eis); // Parses UTF-16LE file into UTF-8 in memory
fclose(fp);
エンコーディングを静的に指定した、入力バイトストリームのラッパー。
Definition encodedstream.h:39
UTF-16リトルエンディアンのエンコーディング。
Definition encodings.h:342

EncodedOutputStream

EncodedOutputStreamも同様ですが、コンストラクターにbool putBOM引数があり、出力バイトストリームへBOMを書き込むかどうかを制御します。

#include "rapidjson/filewritestream.h" // FileWriteStream
#include "rapidjson/encodedstream.h" // EncodedOutputStream
#include <cstdio>
Document d; // Document is GenericDocument<UTF8<> >
// ...
FILE* fp = fopen("output_utf32le.json", "wb"); // non-Windows use "w"
char writeBuffer[256];
FileWriteStream bos(fp, writeBuffer, sizeof(writeBuffer));
OutputStream eos(bos, true); // Write BOM
d.Accept(writer); // This generates UTF32-LE file from UTF-8 in memory
fclose(fp);
エンコーディングを静的に指定した、出力バイトストリームのラッパー。
Definition encodedstream.h:100
UTF-8エンコーディング。
Definition encodings.h:96

AutoUTFInputStream

対応するすべてのJSONエンコーディングを扱いたい場合があります。AutoUTFInputStreamは、まずBOMでエンコーディングを検出します。BOMがなければ、妥当なJSONの特徴を利用して検出します。どちらの方法でも検出できなければ、コンストラクターで指定したUTF型へフォールバックします。

文字(コード単位)は8ビット、16ビット、32ビットのいずれかになり得るため、AutoUTFInputStreamは少なくとも32ビットを保持できる文字型を必要とします。次のテンプレート引数のように、unsignedを使えます。

#include "rapidjson/filereadstream.h" // FileReadStream
#include "rapidjson/encodedstream.h" // AutoUTFInputStream
#include <cstdio>
using namespace rapidjson;
FILE* fp = fopen("any.json", "rb"); // non-Windows use "r"
char readBuffer[256];
FileReadStream bis(fp, readBuffer, sizeof(readBuffer));
AutoUTFInputStream<unsigned, FileReadStream> eis(bis); // wraps bis into eis
Document d; // Document is GenericDocument<UTF8<> >
d.ParseStream<0, AutoUTF<unsigned> >(eis); // This parses any UTF file into UTF-8 in memory
fclose(fp);
エンコーディングを動的に指定し、自動検出する入力ストリームのラッパー。
Definition encodedstream.h:135
ストリームの実行時指定のUTFエンコーディング型に応じ、エンコーディングを動的に選択します。
Definition encodings.h:615

ストリームのエンコーディングを指定するときは、上のParseStream()のようにAutoUTF<CharType>を使います。

UTFType GetType()でUTFの種類を取得できます。HasBOM()でBOMが見つかったかどうかを確認できます。

AutoUTFOutputStream

同様に、実行時に出力エンコーディングを選ぶには、AutoUTFOutputStreamを使えます。このクラスはそれ自体で自動的というわけではありません。UTFの種類とBOMを出力するかどうかを、実行時に指定する必要があります。

using namespace rapidjson;
void WriteJSONFile(FILE* fp, UTFType type, bool putBOM, const Document& d) {
char writeBuffer[256];
FileWriteStream bos(fp, writeBuffer, sizeof(writeBuffer));
OutputStream eos(bos, type, putBOM);
d.Accept(writer);
}
エンコーディングを動的に指定し、自動検出する出力ストリームのラッパー。
Definition encodedstream.h:233
UTFType
ストリームの実行時指定のUTFエンコーディング型。
Definition encodings.h:603

AutoUTFInputStreamとAutoUTFOutputStreamは、EncodedInputStreamとEncodedOutputStreamより便利で、実行時のオーバーヘッドも少量です。

独自のストリーム

メモリーストリームやファイルストリーム以外にも、RapidJSONのAPIに適合する独自のストリームクラスを作れます。ネットワークストリームや圧縮ファイル用ストリームなどが例です。

RapidJSONはテンプレートでさまざまな型を組み合わせます。必要なインターフェースをすべて備えるクラスはストリームとして使えます。Streamのインターフェースは、rapidjson/rapidjson.hのコメントに定義されています。

concept Stream {
typename Ch; //!< Character type of the stream.
//! Read the current character from stream without moving the read cursor.
Ch Peek() const;
//! Read the current character from stream and moving the read cursor to next character.
Ch Take();
//! Get the current read cursor.
//! \return Number of characters read from start.
size_t Tell();
//! Begin writing operation at the current read pointer.
//! \return The begin writer pointer.
Ch* PutBegin();
//! Write a character.
void Put(Ch c);
//! Flush the buffer.
void Flush();
//! End the writing operation.
//! \param begin The begin write pointer returned by PutBegin().
//! \return Number of characters written.
size_t PutEnd(Ch* begin);
}
文字の読み書きの概念。

入力ストリームにはPeek()、Take()、Tell()、出力ストリームにはPut()とFlush()の実装が必要です。in situ解析だけに使う特別なインターフェースとして、PutBegin()とPutEnd()があります。通常のストリームはこれらを実装しません。ただし、特定のストリームでそのインターフェースを使わなくても、ダミーの実装が必要です。そうしなければコンパイルエラーになります。

例:istreamラッパー

以下は、3つの関数だけを実装する、簡単なstd::istreamのラッパーの例です。

class MyIStreamWrapper {
public:
typedef char Ch;
MyIStreamWrapper(std::istream& is) : is_(is) {
}
Ch Peek() const { // 1
int c = is_.peek();
return c == std::char_traits<char>::eof() ? '\0' : (Ch)c;
}
Ch Take() { // 2
int c = is_.get();
return c == std::char_traits<char>::eof() ? '\0' : (Ch)c;
}
size_t Tell() const { return (size_t)is_.tellg(); } // 3
Ch* PutBegin() { assert(false); return 0; }
void Put(Ch) { assert(false); }
void Flush() { assert(false); }
size_t PutEnd(Ch*) { assert(false); return 0; }
private:
MyIStreamWrapper(const MyIStreamWrapper&);
MyIStreamWrapper& operator=(const MyIStreamWrapper&);
std::istream& is_;
};

std::stringstreamやstd::ifstreamのインスタンスを包むために使えます。

const char* json = "[1,2,3,4]";
std::stringstream ss(json);
MyIStreamWrapper is(ss);

標準ライブラリー内部のオーバーヘッドにより、この実装はRapidJSONのメモリーストリームやファイルストリームほど効率的でない場合があります。

例:ostreamラッパー

以下は、2つの関数だけを実装する、簡単なstd::istreamのラッパーの例です。

class MyOStreamWrapper {
public:
typedef char Ch;
MyOStreamWrapper(std::ostream& os) : os_(os) {
}
Ch Peek() const { assert(false); return '\0'; }
Ch Take() { assert(false); return '\0'; }
size_t Tell() const { }
Ch* PutBegin() { assert(false); return 0; }
void Put(Ch c) { os_.put(c); } // 1
void Flush() { os_.flush(); } // 2
size_t PutEnd(Ch*) { assert(false); return 0; }
private:
MyOStreamWrapper(const MyOStreamWrapper&);
MyOStreamWrapper& operator=(const MyOStreamWrapper&);
std::ostream& os_;
};

std::stringstreamやstd::ofstreamのインスタンスを包むために使えます。

// ...
std::stringstream ss;
MyOStreamWrapper os(ss);
d.Accept(writer);

標準ライブラリー内部のオーバーヘッドにより、この実装はRapidJSONのメモリーストリームやファイルストリームほど効率的でない場合があります。

まとめ

この節では、RapidJSONで利用できるストリームクラスを説明しました。メモリーストリームは単純です。JSONをファイルシステムに保存する場合、ファイルストリームによって解析・生成中に必要なメモリーを減らせます。エンコーディング付きストリームは、バイトストリームと文字ストリームの間で変換します。最後に、簡単なインターフェースで独自のストリームを作れます。