VC2005 setup

This commit is contained in:
James Shiffer 2020-08-22 14:28:54 -07:00
commit 0293297161
175 changed files with 47748 additions and 0 deletions

362
.gitignore vendored Normal file
View File

@ -0,0 +1,362 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd

20
FemboyBanking.sln Normal file
View File

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual C++ Express 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FemboyBanking", "FemboyBanking\FemboyBanking.vcproj", "{5AA6F715-318F-4E71-B40C-3BEB22AE71AF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5AA6F715-318F-4E71-B40C-3BEB22AE71AF}.Debug|Win32.ActiveCfg = Debug|Win32
{5AA6F715-318F-4E71-B40C-3BEB22AE71AF}.Debug|Win32.Build.0 = Debug|Win32
{5AA6F715-318F-4E71-B40C-3BEB22AE71AF}.Release|Win32.ActiveCfg = Release|Win32
{5AA6F715-318F-4E71-B40C-3BEB22AE71AF}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,40 @@
#include "stdafx.h"
using namespace System;
using namespace System::Reflection;
using namespace System::Runtime::CompilerServices;
using namespace System::Runtime::InteropServices;
using namespace System::Security::Permissions;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly:AssemblyTitleAttribute("FemboyBanking")];
[assembly:AssemblyDescriptionAttribute("")];
[assembly:AssemblyConfigurationAttribute("")];
[assembly:AssemblyCompanyAttribute("FemboyFinancial Bank Corp. NA")];
[assembly:AssemblyProductAttribute("FemboyBanking")];
[assembly:AssemblyCopyrightAttribute("Copyright (c) FemboyFinancial Bank Corp. NA 2020")];
[assembly:AssemblyTrademarkAttribute("")];
[assembly:AssemblyCultureAttribute("")];
//
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the value or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly:AssemblyVersionAttribute("1.0.*")];
[assembly:ComVisible(false)];
[assembly:CLSCompliantAttribute(true)];
[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)];

View File

@ -0,0 +1,237 @@
#pragma once
#include <stdio.h>
#include "Etherscan.h"
#include "Main.h"
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
namespace FemboyBanking {
/// <summary>
/// Summary for EthAddressPrompt
///
/// WARNING: If you change the name of this class, you will need to change the
/// 'Resource File Name' property for the managed resource compiler tool
/// associated with all .resx files this class depends on. Otherwise,
/// the designers will not be able to interact properly with localized
/// resources associated with this form.
/// </summary>
public ref class EthAddressPrompt : public System::Windows::Forms::Form
{
public:
EthAddressPrompt(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
InitializeBackgoundWorker();
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~EthAddressPrompt()
{
if (components)
{
delete components;
}
}
private:
System::Windows::Forms::Button^ doneBtn;
System::Windows::Forms::Button^ cancelBtn;
System::Windows::Forms::TextBox^ ethTextBox;
System::Windows::Forms::CheckBox^ privacyCheckBox;
System::Windows::Forms::Label^ label1;
System::Windows::Forms::Label^ label2;
System::ComponentModel::BackgroundWorker^ etherscanWorker;
System::Windows::Forms::ProgressBar^ progressBar;
private: System::Windows::Forms::Label^ label3;
private:
/// <summary>
/// Required designer variable.
/// </summary>
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
System::ComponentModel::ComponentResourceManager^ resources = (gcnew System::ComponentModel::ComponentResourceManager(EthAddressPrompt::typeid));
this->doneBtn = (gcnew System::Windows::Forms::Button());
this->cancelBtn = (gcnew System::Windows::Forms::Button());
this->ethTextBox = (gcnew System::Windows::Forms::TextBox());
this->privacyCheckBox = (gcnew System::Windows::Forms::CheckBox());
this->label1 = (gcnew System::Windows::Forms::Label());
this->label2 = (gcnew System::Windows::Forms::Label());
this->etherscanWorker = (gcnew System::ComponentModel::BackgroundWorker());
this->progressBar = (gcnew System::Windows::Forms::ProgressBar());
this->label3 = (gcnew System::Windows::Forms::Label());
this->SuspendLayout();
//
// doneBtn
//
this->doneBtn->Location = System::Drawing::Point(235, 70);
this->doneBtn->Name = L"doneBtn";
this->doneBtn->Size = System::Drawing::Size(75, 23);
this->doneBtn->TabIndex = 0;
this->doneBtn->Text = L"Done";
this->doneBtn->UseVisualStyleBackColor = true;
this->doneBtn->Click += gcnew System::EventHandler(this, &EthAddressPrompt::doneBtn_Click);
//
// cancelBtn
//
this->cancelBtn->Location = System::Drawing::Point(154, 70);
this->cancelBtn->Name = L"cancelBtn";
this->cancelBtn->Size = System::Drawing::Size(75, 23);
this->cancelBtn->TabIndex = 1;
this->cancelBtn->Text = L"Cancel";
this->cancelBtn->UseVisualStyleBackColor = true;
this->cancelBtn->Click += gcnew System::EventHandler(this, &EthAddressPrompt::cancelBtn_Click);
//
// ethTextBox
//
this->ethTextBox->AllowDrop = true;
this->ethTextBox->Location = System::Drawing::Point(12, 42);
this->ethTextBox->MaxLength = 42;
this->ethTextBox->Name = L"ethTextBox";
this->ethTextBox->Size = System::Drawing::Size(298, 20);
this->ethTextBox->TabIndex = 2;
//
// privacyCheckBox
//
this->privacyCheckBox->AutoSize = true;
this->privacyCheckBox->Location = System::Drawing::Point(12, 74);
this->privacyCheckBox->Name = L"privacyCheckBox";
this->privacyCheckBox->Size = System::Drawing::Size(78, 17);
this->privacyCheckBox->TabIndex = 3;
this->privacyCheckBox->Text = L"Hard mode";
this->privacyCheckBox->UseVisualStyleBackColor = true;
this->privacyCheckBox->CheckedChanged += gcnew System::EventHandler(this, &EthAddressPrompt::privacyCheckBox_CheckedChanged);
//
// label1
//
this->label1->AutoSize = true;
this->label1->Location = System::Drawing::Point(9, 9);
this->label1->Name = L"label1";
this->label1->Size = System::Drawing::Size(146, 13);
this->label1->TabIndex = 4;
this->label1->Text = L"Enter your Ethereum address.";
//
// label2
//
this->label2->AutoSize = true;
this->label2->Font = (gcnew System::Drawing::Font(L"Microsoft Sans Serif", 8, System::Drawing::FontStyle::Regular, System::Drawing::GraphicsUnit::Point,
static_cast<System::Byte>(0)));
this->label2->Location = System::Drawing::Point(9, 26);
this->label2->Name = L"label2";
this->label2->Size = System::Drawing::Size(207, 13);
this->label2->TabIndex = 5;
this->label2->Text = L"NOTE: The only token we accept is LINK.";
//
// etherscanWorker
//
this->etherscanWorker->WorkerReportsProgress = true;
//
// progressBar
//
this->progressBar->Location = System::Drawing::Point(12, 112);
this->progressBar->Name = L"progressBar";
this->progressBar->Size = System::Drawing::Size(298, 18);
this->progressBar->TabIndex = 6;
this->progressBar->UseWaitCursor = true;
//
// label3
//
this->label3->AutoSize = true;
this->label3->Location = System::Drawing::Point(9, 133);
this->label3->Name = L"label3";
this->label3->Size = System::Drawing::Size(243, 13);
this->label3->TabIndex = 7;
this->label3->Text = L"Connecting to the Femboy Network... Please wait.";
//
// EthAddressPrompt
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(322, 103);
this->Controls->Add(this->label3);
this->Controls->Add(this->progressBar);
this->Controls->Add(this->label2);
this->Controls->Add(this->label1);
this->Controls->Add(this->privacyCheckBox);
this->Controls->Add(this->ethTextBox);
this->Controls->Add(this->cancelBtn);
this->Controls->Add(this->doneBtn);
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog;
this->Icon = (cli::safe_cast<System::Drawing::Icon^ >(resources->GetObject(L"$this.Icon")));
this->MaximizeBox = false;
this->Name = L"EthAddressPrompt";
this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
this->Text = L"Connect Ethereum Wallet";
this->ResumeLayout(false);
this->PerformLayout();
}
#pragma endregion
private:
void InitializeBackgoundWorker()
{
this->etherscanWorker->DoWork += gcnew System::ComponentModel::DoWorkEventHandler( this, &EthAddressPrompt::etherscanWorker_DoWork );
this->etherscanWorker->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler( this, &EthAddressPrompt::etherscanWorker_RunWorkerCompleted );
this->etherscanWorker->ProgressChanged += gcnew ProgressChangedEventHandler( this, &EthAddressPrompt::etherscanWorker_ProgressChanged );
}
System::Void privacyCheckBox_CheckedChanged(System::Object^ sender, System::EventArgs^ e) {
this->ethTextBox->UseSystemPasswordChar = this->privacyCheckBox->Checked;
}
System::Void cancelBtn_Click(System::Object^ sender, System::EventArgs^ e) {
Application::Exit();
}
System::Void doneBtn_Click(System::Object^ sender, System::EventArgs^ e) {
this->ethTextBox->Enabled = false;
this->Height += 50;
etherscanWorker->RunWorkerAsync( this->ethTextBox->Text );
}
System::Void etherscanWorker_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
// Get the BackgroundWorker that raised this event.
BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
// Assign the result of the computation
// to the Result property of the DoWorkEventArgs
// object. This is will be available to the
// RunWorkerCompleted eventhandler.
e->Result = Etherscan::FetchLinkBalance( e->Argument->ToString(), worker, e );
}
System::Void etherscanWorker_RunWorkerCompleted(System::Object^ sender, System::ComponentModel::RunWorkerCompletedEventArgs^ e) {
if ((UInt64) e->Result != -1)
{
Double balance = (UInt64) e->Result / 1e18;
String^ balanceMsg = String::Format("You have {0} LINK", balance);
Main^ form = gcnew Main(this->ethTextBox->Text, balance);
this->Owner = form;
this->Owner->Show();
this->Hide();
}
this->ethTextBox->Enabled = true;
this->Height -= 50;
}
System::Void etherscanWorker_ProgressChanged(System::Object^ sender, System::ComponentModel::ProgressChangedEventArgs^ e) {
this->progressBar->Value = e->ProgressPercentage;
}
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
#include "StdAfx.h"
#include "Etherscan.h"
#pragma comment (lib, "wininet.lib")
using namespace FemboyBanking;
using namespace rapidjson;
using namespace System;
using namespace System::ComponentModel;
using namespace System::Windows::Forms;
UInt64 Etherscan::FetchLinkBalance(String^ address, BackgroundWorker^ worker, DoWorkEventArgs^ e)
{
HINTERNET connect = InternetOpenA("EtherscanQuery", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (!connect)
{
MessageBox::Show( dynamic_cast<Form^>(worker->Container), "Failed to open Internet handle", "Network Error", MessageBoxButtons::OK, MessageBoxIcon::Error);
return -1;
}
worker->ReportProgress(25);
char url[256];
sprintf_s(url, sizeof(url), Etherscan::BALANCE_URL_FMT, Etherscan::LINK_CONTRACT, address);
HINTERNET OpenAddress = InternetOpenUrlA(connect, url, NULL, 0, INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_KEEP_CONNECTION, 0);
if (!OpenAddress)
{
DWORD err = GetLastError();
MessageBox::Show( dynamic_cast<Form^>(worker->Container), String::Format("Failed to open URL, error {0}", err), "Network Error", MessageBoxButtons::OK, MessageBoxIcon::Error );
InternetCloseHandle(connect);
return -1;
}
worker->ReportProgress(50);
char DataReceived[4096];
char* temp = DataReceived;
DWORD NumberOfBytesRead = 0;
while ( InternetReadFile(OpenAddress, DataReceived, sizeof(DataReceived), &NumberOfBytesRead) && NumberOfBytesRead )
{
temp += NumberOfBytesRead;
}
*temp = '\0';
worker->ReportProgress(75);
Document response;
response.Parse(DataReceived);
worker->ReportProgress(90);
if (strcmp(response["status"].GetString(), "1") != 0)
{
if (response.HasMember("result"))
{
const char* error = response["result"].GetString();
MessageBox::Show( dynamic_cast<Form^>(worker->Container), gcnew String(error), "API Error", MessageBoxButtons::OK, MessageBoxIcon::Error);
}
else
{
MessageBox::Show( dynamic_cast<Form^>(worker->Container), "Unknown error", "API Error", MessageBoxButtons::OK, MessageBoxIcon::Error);
}
return -1;
}
InternetCloseHandle(OpenAddress);
InternetCloseHandle(connect);
const char* result = response["result"].GetString();
String^ balanceStr = gcnew String(result);
UInt64 balance = UInt64::Parse(balanceStr);
worker->ReportProgress(100);
return balance;
}

13
FemboyBanking/Etherscan.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
namespace FemboyBanking
{
public ref class Etherscan
{
public:
static System::UInt64 FetchLinkBalance( System::String^, System::ComponentModel::BackgroundWorker^, System::ComponentModel::DoWorkEventArgs^ );
private:
static const char* BALANCE_URL_FMT = "http://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=%s&address=%s&tag=latest";
static const char* LINK_CONTRACT = "0x514910771af9ca656af840dff83e8264ecf986ca";
};
}

View File

@ -0,0 +1,18 @@
// FemboyBanking.cpp : main project file.
#include "stdafx.h"
#include "EthAddressPrompt.h"
using namespace FemboyBanking;
[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
// Enabling Windows XP visual effects before any controls are created
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
// Create the main window and run it
Application::Run(gcnew EthAddressPrompt());
return 0;
}

View File

@ -0,0 +1,290 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="FemboyBanking"
ProjectGUID="{5AA6F715-318F-4E71-B40C-3BEB22AE71AF}"
RootNamespace="FemboyBanking"
Keyword="ManagedCProj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
ManagedExtensions="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="&quot;C:\Program Files\Microsoft SDKs\Windows\v7.1\Include&quot;;&quot;$(SolutionDir)thirdparty\rapidjson-1.1.0\include&quot;"
PreprocessorDefinitions="WIN32;_DEBUG"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="$(NOINHERIT)"
LinkIncremental="2"
AdditionalLibraryDirectories="&quot;C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib&quot;"
GenerateDebugInformation="true"
AssemblyDebug="1"
SubSystem="2"
EntryPointSymbol="main"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
ManagedExtensions="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG"
RuntimeLibrary="2"
UsePrecompiledHeader="2"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="$(NoInherit)"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="2"
EntryPointSymbol="main"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
<AssemblyReference
RelativePath="System.dll"
AssemblyName="System, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
/>
<AssemblyReference
RelativePath="System.Data.dll"
AssemblyName="System.Data, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=x86"
/>
<AssemblyReference
RelativePath="System.Drawing.dll"
AssemblyName="System.Drawing, Version=2.0.0.0, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
/>
<AssemblyReference
RelativePath="System.Windows.Forms.dll"
AssemblyName="System.Windows.Forms, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
/>
<AssemblyReference
RelativePath="System.XML.dll"
AssemblyName="System.Xml, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
/>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\AssemblyInfo.cpp"
>
</File>
<File
RelativePath=".\Etherscan.cpp"
>
</File>
<File
RelativePath=".\FemboyBanking.cpp"
>
</File>
<File
RelativePath=".\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\EthAddressPrompt.h"
>
</File>
<File
RelativePath=".\Etherscan.h"
>
</File>
<File
RelativePath=".\Main.h"
>
</File>
<File
RelativePath=".\resource.h"
>
</File>
<File
RelativePath=".\stdafx.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
<File
RelativePath=".\app.ico"
>
</File>
<File
RelativePath=".\app.rc"
>
</File>
<File
RelativePath=".\EthAddressPrompt.resx"
SubType="Designer"
>
</File>
<File
RelativePath=".\Main.resX"
SubType="Designer"
>
</File>
</Filter>
<File
RelativePath=".\ReadMe.txt"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

161
FemboyBanking/Main.h Normal file
View File

@ -0,0 +1,161 @@
#pragma once
namespace FemboyBanking {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
/// <summary>
/// Summary for Main
///
/// WARNING: If you change the name of this class, you will need to change the
/// 'Resource File Name' property for the managed resource compiler tool
/// associated with all .resx files this class depends on. Otherwise,
/// the designers will not be able to interact properly with localized
/// resources associated with this form.
/// </summary>
public ref class Main : public System::Windows::Forms::Form
{
public:
Main(String^ ethAddress, Double linkBalance)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
this->ethAddress = ethAddress;
this->linkBalance = linkBalance;
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~Main()
{
if (components)
{
delete components;
}
}
private: System::Windows::Forms::Label^ label1;
private: System::Windows::Forms::Label^ balLbl;
private: System::Windows::Forms::Button^ depBtn;
private: System::Windows::Forms::Button^ withBtn;
private: System::Windows::Forms::Button^ logBtn;
private:
double linkBalance;
String^ ethAddress;
private:
/// <summary>
/// Required designer variable.
/// </summary>
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
System::ComponentModel::ComponentResourceManager^ resources = (gcnew System::ComponentModel::ComponentResourceManager(Main::typeid));
this->label1 = (gcnew System::Windows::Forms::Label());
this->balLbl = (gcnew System::Windows::Forms::Label());
this->depBtn = (gcnew System::Windows::Forms::Button());
this->withBtn = (gcnew System::Windows::Forms::Button());
this->logBtn = (gcnew System::Windows::Forms::Button());
this->SuspendLayout();
//
// label1
//
this->label1->AutoSize = true;
this->label1->Location = System::Drawing::Point(36, 26);
this->label1->Name = L"label1";
this->label1->Size = System::Drawing::Size(220, 13);
this->label1->TabIndex = 0;
this->label1->Text = L"Welcome to FemboyFinancial Bank Corp. NA";
//
// balLbl
//
this->balLbl->AutoSize = true;
this->balLbl->Font = (gcnew System::Drawing::Font(L"Microsoft Sans Serif", 8.25F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point,
static_cast<System::Byte>(0)));
this->balLbl->Location = System::Drawing::Point(36, 40);
this->balLbl->Name = L"balLbl";
this->balLbl->Size = System::Drawing::Size(67, 13);
this->balLbl->TabIndex = 1;
this->balLbl->Text = L"BALANCE:";
//
// depBtn
//
this->depBtn->Location = System::Drawing::Point(88, 91);
this->depBtn->Name = L"depBtn";
this->depBtn->Size = System::Drawing::Size(117, 23);
this->depBtn->TabIndex = 2;
this->depBtn->Text = L"Deposit";
this->depBtn->UseVisualStyleBackColor = true;
this->depBtn->Click += gcnew System::EventHandler(this, &Main::depBtn_Click);
//
// withBtn
//
this->withBtn->Location = System::Drawing::Point(88, 139);
this->withBtn->Name = L"withBtn";
this->withBtn->Size = System::Drawing::Size(117, 23);
this->withBtn->TabIndex = 3;
this->withBtn->Text = L"Withdraw";
this->withBtn->UseVisualStyleBackColor = true;
this->withBtn->Click += gcnew System::EventHandler(this, &Main::withBtn_Click);
//
// logBtn
//
this->logBtn->Location = System::Drawing::Point(88, 187);
this->logBtn->Name = L"logBtn";
this->logBtn->Size = System::Drawing::Size(117, 23);
this->logBtn->TabIndex = 4;
this->logBtn->Text = L"Transaction Log";
this->logBtn->UseVisualStyleBackColor = true;
this->logBtn->Click += gcnew System::EventHandler(this, &Main::logBtn_Click);
//
// Main
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(292, 266);
this->Controls->Add(this->logBtn);
this->Controls->Add(this->withBtn);
this->Controls->Add(this->depBtn);
this->Controls->Add(this->balLbl);
this->Controls->Add(this->label1);
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedSingle;
this->Icon = (cli::safe_cast<System::Drawing::Icon^ >(resources->GetObject(L"$this.Icon")));
this->MaximizeBox = false;
this->Name = L"Main";
this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
this->Text = L"FemboyBanking";
this->Load += gcnew System::EventHandler(this, &Main::Main_Load);
this->ResumeLayout(false);
this->PerformLayout();
}
#pragma endregion
private: System::Void depBtn_Click(System::Object^ sender, System::EventArgs^ e) {
MessageBox::Show( this, "You selected DEPOSIT", "FemboyBanking Alert", MessageBoxButtons::OK, MessageBoxIcon::Information );
}
private: System::Void withBtn_Click(System::Object^ sender, System::EventArgs^ e) {
MessageBox::Show( this, "You selected WITHDRAW", "FemboyBanking Alert", MessageBoxButtons::OK, MessageBoxIcon::Information );
}
private: System::Void logBtn_Click(System::Object^ sender, System::EventArgs^ e) {
MessageBox::Show( this, "You selected TRANSACTION LOG", "FemboyBanking Alert", MessageBoxButtons::OK, MessageBoxIcon::Information );
}
private: System::Void Main_Load(System::Object^ sender, System::EventArgs^ e) {
this->balLbl->Text = String::Format("BALANCE: {0} LINK", this->linkBalance);
}
};
}

1677
FemboyBanking/Main.resX Normal file

File diff suppressed because it is too large Load Diff

33
FemboyBanking/ReadMe.txt Normal file
View File

@ -0,0 +1,33 @@
========================================================================
APPLICATION : FemboyBanking Project Overview
========================================================================
AppWizard has created this FemboyBanking Application for you.
This file contains a summary of what you will find in each of the files that
make up your FemboyBanking application.
FemboyBanking.vcproj
This is the main project file for VC++ projects generated using an Application Wizard.
It contains information about the version of Visual C++ that generated the file, and
information about the platforms, configurations, and project features selected with the
Application Wizard.
FemboyBanking.cpp
This is the main application source file.
Contains the code to display the form.
Main.h
Contains the implementation of your form class and InitializeComponent() function.
AssemblyInfo.cpp
Contains custom attributes for modifying assembly metadata.
/////////////////////////////////////////////////////////////////////////////
Other standard files:
StdAfx.h, StdAfx.cpp
These files are used to build a precompiled header (PCH) file
named FemboyBanking.pch and a precompiled types file named StdAfx.obj.
/////////////////////////////////////////////////////////////////////////////

BIN
FemboyBanking/Thumbs.db Normal file

Binary file not shown.

BIN
FemboyBanking/app.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

63
FemboyBanking/app.rc Normal file
View File

@ -0,0 +1,63 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon placed first or with lowest ID value becomes application icon
LANGUAGE 9, 1
#pragma code_page(1252)
1 ICON "app.ico"
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
"\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

3
FemboyBanking/resource.h Normal file
View File

@ -0,0 +1,3 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by app.rc

7
FemboyBanking/stdafx.cpp Normal file
View File

@ -0,0 +1,7 @@
// stdafx.cpp : source file that includes just the standard includes
// FemboyBanking.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"

10
FemboyBanking/stdafx.h Normal file
View File

@ -0,0 +1,10 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
#pragma once
// TODO: reference additional headers your program requires here
#include <stdio.h>
#include <windows.h>
#include <wininet.h>
#include "rapidjson/document.h"

View File

@ -0,0 +1,22 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.cpp text
*.h text
*.txt text
*.md text
*.cmake text
*.svg text
*.dot text
*.yml text
*.in text
*.sh text
*.autopkg text
Dockerfile text
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.json binary

25
thirdparty/rapidjson-1.1.0/.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
/bin/*
!/bin/data
!/bin/encodings
!/bin/jsonchecker
!/bin/types
/build
/doc/html
/doc/doxygen_*.db
*.a
# Temporary files created during CMake build
CMakeCache.txt
CMakeFiles
cmake_install.cmake
CTestTestfile.cmake
Makefile
RapidJSON*.cmake
RapidJSON.pc
Testing
/googletest
install_manifest.txt
Doxyfile
Doxyfile.zh-cn
DartConfiguration.tcl
*.nupkg

View File

@ -0,0 +1,3 @@
[submodule "thirdparty/gtest"]
path = thirdparty/gtest
url = https://github.com/google/googletest.git

98
thirdparty/rapidjson-1.1.0/.travis.yml vendored Normal file
View File

@ -0,0 +1,98 @@
sudo: required
dist: precise
language: cpp
cache:
- ccache
env:
global:
- USE_CCACHE=1
- CCACHE_SLOPPINESS=pch_defines,time_macros
- CCACHE_COMPRESS=1
- CCACHE_MAXSIZE=100M
- ARCH_FLAGS_x86='-m32' # #266: don't use SSE on 32-bit
- ARCH_FLAGS_x86_64='-msse4.2' # use SSE4.2 on 64-bit
- GITHUB_REPO='miloyip/rapidjson'
- secure: "HrsaCb+N66EG1HR+LWH1u51SjaJyRwJEDzqJGYMB7LJ/bfqb9mWKF1fLvZGk46W5t7TVaXRDD5KHFx9DPWvKn4gRUVkwTHEy262ah5ORh8M6n/6VVVajeV/AYt2C0sswdkDBDO4Xq+xy5gdw3G8s1A4Inbm73pUh+6vx+7ltBbk="
before_install:
- sudo apt-add-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update -qq
- sudo apt-get install -y cmake valgrind g++-multilib libc6-dbg:i386
matrix:
include:
# gcc
- env: CONF=release ARCH=x86 CXX11=ON
compiler: gcc
- env: CONF=release ARCH=x86_64 CXX11=ON
compiler: gcc
- env: CONF=debug ARCH=x86 CXX11=OFF
compiler: gcc
- env: CONF=debug ARCH=x86_64 CXX11=OFF
compiler: gcc
# clang
- env: CONF=debug ARCH=x86 CXX11=ON CCACHE_CPP2=yes
compiler: clang
- env: CONF=debug ARCH=x86_64 CXX11=ON CCACHE_CPP2=yes
compiler: clang
- env: CONF=debug ARCH=x86 CXX11=OFF CCACHE_CPP2=yes
compiler: clang
- env: CONF=debug ARCH=x86_64 CXX11=OFF CCACHE_CPP2=yes
compiler: clang
- env: CONF=release ARCH=x86 CXX11=ON CCACHE_CPP2=yes
compiler: clang
- env: CONF=release ARCH=x86_64 CXX11=ON CCACHE_CPP2=yes
compiler: clang
# coverage report
- env: CONF=debug ARCH=x86 CXX11=ON GCOV_FLAGS='--coverage'
compiler: gcc
cache:
- ccache
- pip
after_success:
- pip install --user cpp-coveralls
- coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes -e include/rapidjson/internal/meta.h -e include/rapidjson/error/en.h
- env: CONF=debug ARCH=x86_64 GCOV_FLAGS='--coverage'
compiler: gcc
cache:
- ccache
- pip
after_success:
- pip install --user cpp-coveralls
- coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes -e include/rapidjson/internal/meta.h -e include/rapidjson/error/en.h
- script: # Documentation task
- cd build
- cmake .. -DRAPIDJSON_HAS_STDSTRING=ON -DCMAKE_VERBOSE_MAKEFILE=ON
- make travis_doc
cache: false
addons:
apt:
packages:
- doxygen
before_script:
- ccache -s
# hack to avoid Valgrind bug (https://bugs.kde.org/show_bug.cgi?id=326469),
# exposed by merging PR#163 (using -march=native)
# TODO: Since this bug is already fixed. Remove this when valgrind can be upgraded.
- sed -i "s/-march=native//" CMakeLists.txt
- mkdir build
script:
- if [ "$CXX" = "clang++" ]; then export CXXFLAGS="-stdlib=libc++ ${CXXFLAGS}"; fi
- >
eval "ARCH_FLAGS=\${ARCH_FLAGS_${ARCH}}" ;
(cd build && cmake
-DRAPIDJSON_HAS_STDSTRING=ON
-DRAPIDJSON_BUILD_CXX11=$CXX11
-DCMAKE_VERBOSE_MAKEFILE=ON
-DCMAKE_BUILD_TYPE=$CONF
-DCMAKE_CXX_FLAGS="$ARCH_FLAGS $GCOV_FLAGS"
-DCMAKE_EXE_LINKER_FLAGS=$GCOV_FLAGS
..)
- cd build
- make tests -j 2
- make examples -j 2
- ctest -j 2 -V `[ "$CONF" = "release" ] || echo "-E perftest"`

158
thirdparty/rapidjson-1.1.0/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,158 @@
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
## 1.1.0 - 2016-08-25
### Added
* Add GenericDocument ctor overload to specify JSON type (#369)
* Add FAQ (#372, #373, #374, #376)
* Add forward declaration header `fwd.h`
* Add @PlatformIO Library Registry manifest file (#400)
* Implement assignment operator for BigInteger (#404)
* Add comments support (#443)
* Adding coapp definition (#460)
* documenttest.cpp: EXPECT_THROW when checking empty allocator (470)
* GenericDocument: add implicit conversion to ParseResult (#480)
* Use <wchar.h> with C++ linkage on Windows ARM (#485)
* Detect little endian for Microsoft ARM targets
* Check Nan/Inf when writing a double (#510)
* Add JSON Schema Implementation (#522)
* Add iostream wrapper (#530)
* Add Jsonx example for converting JSON into JSONx (a XML format) (#531)
* Add optional unresolvedTokenIndex parameter to Pointer::Get() (#532)
* Add encoding validation option for Writer/PrettyWriter (#534)
* Add Writer::SetMaxDecimalPlaces() (#536)
* Support {0, } and {0, m} in Regex (#539)
* Add Value::Get/SetFloat(), Value::IsLossLessFloat/Double() (#540)
* Add stream position check to reader unit tests (#541)
* Add Templated accessors and range-based for (#542)
* Add (Pretty)Writer::RawValue() (#543)
* Add Document::Parse(std::string), Document::Parse(const char*, size_t length) and related APIs. (#553)
* Add move constructor for GenericSchemaDocument (#554)
* Add VS2010 and VS2015 to AppVeyor CI (#555)
* Add parse-by-parts example (#556, #562)
* Support parse number as string (#564, #589)
* Add kFormatSingleLineArray for PrettyWriter (#577)
* Added optional support for trailing commas (#584)
* Added filterkey and filterkeydom examples (#615)
* Added npm docs (#639)
* Allow options for writing and parsing NaN/Infinity (#641)
* Add std::string overload to PrettyWriter::Key() when RAPIDJSON_HAS_STDSTRING is defined (#698)
### Fixed
* Fix gcc/clang/vc warnings (#350, #394, #397, #444, #447, #473, #515, #582, #589, #595, #667)
* Fix documentation (#482, #511, #550, #557, #614, #635, #660)
* Fix emscripten alignment issue (#535)
* Fix missing allocator to uses of AddMember in document (#365)
* CMake will no longer complain that the minimum CMake version is not specified (#501)
* Make it usable with old VC8 (VS2005) (#383)
* Prohibit C++11 move from Document to Value (#391)
* Try to fix incorrect 64-bit alignment (#419)
* Check return of fwrite to avoid warn_unused_result build failures (#421)
* Fix UB in GenericDocument::ParseStream (#426)
* Keep Document value unchanged on parse error (#439)
* Add missing return statement (#450)
* Fix Document::Parse(const Ch*) for transcoding (#478)
* encodings.h: fix typo in preprocessor condition (#495)
* Custom Microsoft headers are necessary only for Visual Studio 2012 and lower (#559)
* Fix memory leak for invalid regex (26e69ffde95ba4773ab06db6457b78f308716f4b)
* Fix a bug in schema minimum/maximum keywords for 64-bit integer (e7149d665941068ccf8c565e77495521331cf390)
* Fix a crash bug in regex (#605)
* Fix schema "required" keyword cannot handle duplicated keys (#609)
* Fix cmake CMP0054 warning (#612)
* Added missing include guards in istreamwrapper.h and ostreamwrapper.h (#634)
* Fix undefined behaviour (#646)
* Fix buffer overrun using PutN (#673)
* Fix rapidjson::value::Get<std::string>() may returns wrong data (#681)
* Add Flush() for all value types (#689)
* Handle malloc() fail in PoolAllocator (#691)
* Fix builds on x32 platform. #703
### Changed
* Clarify problematic JSON license (#392)
* Move Travis to container based infrastructure (#504, #558)
* Make whitespace array more compact (#513)
* Optimize Writer::WriteString() with SIMD (#544)
* x86-64 48-bit pointer optimization for GenericValue (#546)
* Define RAPIDJSON_HAS_CXX11_RVALUE_REFS directly in clang (#617)
* Make GenericSchemaDocument constructor explicit (#674)
* Optimize FindMember when use std::string (#690)
## [1.0.2] - 2015-05-14
### Added
* Add Value::XXXMember(...) overloads for std::string (#335)
### Fixed
* Include rapidjson.h for all internal/error headers.
* Parsing some numbers incorrectly in full-precision mode (`kFullPrecisionParseFlag`) (#342)
* Fix some numbers parsed incorrectly (#336)
* Fix alignment of 64bit platforms (#328)
* Fix MemoryPoolAllocator::Clear() to clear user-buffer (0691502573f1afd3341073dd24b12c3db20fbde4)
### Changed
* CMakeLists for include as a thirdparty in projects (#334, #337)
* Change Document::ParseStream() to use stack allocator for Reader (ffbe38614732af8e0b3abdc8b50071f386a4a685)
## [1.0.1] - 2015-04-25
### Added
* Changelog following [Keep a CHANGELOG](https://github.com/olivierlacan/keep-a-changelog) suggestions.
### Fixed
* Parsing of some numbers (e.g. "1e-00011111111111") causing assertion (#314).
* Visual C++ 32-bit compilation error in `diyfp.h` (#317).
## [1.0.0] - 2015-04-22
### Added
* 100% [Coverall](https://coveralls.io/r/miloyip/rapidjson?branch=master) coverage.
* Version macros (#311)
### Fixed
* A bug in trimming long number sequence (4824f12efbf01af72b8cb6fc96fae7b097b73015).
* Double quote in unicode escape (#288).
* Negative zero roundtrip (double only) (#289).
* Standardize behavior of `memcpy()` and `malloc()` (0c5c1538dcfc7f160e5a4aa208ddf092c787be5a, #305, 0e8bbe5e3ef375e7f052f556878be0bd79e9062d).
### Removed
* Remove an invalid `Document::ParseInsitu()` API (e7f1c6dd08b522cfcf9aed58a333bd9a0c0ccbeb).
## 1.0-beta - 2015-04-8
### Added
* RFC 7159 (#101)
* Optional Iterative Parser (#76)
* Deep-copy values (#20)
* Error code and message (#27)
* ASCII Encoding (#70)
* `kParseStopWhenDoneFlag` (#83)
* `kParseFullPrecisionFlag` (881c91d696f06b7f302af6d04ec14dd08db66ceb)
* Add `Key()` to handler concept (#134)
* C++11 compatibility and support (#128)
* Optimized number-to-string and vice versa conversions (#137, #80)
* Short-String Optimization (#131)
* Local stream optimization by traits (#32)
* Travis & Appveyor Continuous Integration, with Valgrind verification (#24, #242)
* Redo all documentation (English, Simplified Chinese)
### Changed
* Copyright ownership transfered to THL A29 Limited (a Tencent company).
* Migrating from Premake to CMAKE (#192)
* Resolve all warning reports
### Removed
* Remove other JSON libraries for performance comparison (#180)
## 0.11 - 2012-11-16
## 0.1 - 2011-11-18
[Unreleased]: https://github.com/miloyip/rapidjson/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/miloyip/rapidjson/compare/v1.0.2...v1.1.0
[1.0.2]: https://github.com/miloyip/rapidjson/compare/v1.0.1...v1.0.2
[1.0.1]: https://github.com/miloyip/rapidjson/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/miloyip/rapidjson/compare/v1.0-beta...v1.0.0

View File

@ -0,0 +1,173 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
if(POLICY CMP0025)
# detect Apple's Clang
cmake_policy(SET CMP0025 NEW)
endif()
if(POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif()
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules)
PROJECT(RapidJSON CXX)
set(LIB_MAJOR_VERSION "1")
set(LIB_MINOR_VERSION "1")
set(LIB_PATCH_VERSION "0")
set(LIB_VERSION_STRING "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_PATCH_VERSION}")
# compile in release with debug info mode by default
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif()
# Build all binaries in a separate directory
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." ON)
option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." ON)
option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." ON)
option(RAPIDJSON_BUILD_THIRDPARTY_GTEST
"Use gtest installation in `thirdparty/gtest` by default if available" OFF)
option(RAPIDJSON_BUILD_CXX11 "Build rapidjson with C++11 (gcc/clang)" ON)
option(RAPIDJSON_BUILD_ASAN "Build rapidjson with address sanitizer (gcc/clang)" OFF)
option(RAPIDJSON_BUILD_UBSAN "Build rapidjson with undefined behavior sanitizer (gcc/clang)" OFF)
option(RAPIDJSON_HAS_STDSTRING "" OFF)
if(RAPIDJSON_HAS_STDSTRING)
add_definitions(-DRAPIDJSON_HAS_STDSTRING)
endif()
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments -fcolor-diagnostics")
endif()
endif(CCACHE_FOUND)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror")
if (RAPIDJSON_BUILD_CXX11)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
endif()
if (RAPIDJSON_BUILD_ASAN)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.0")
message(FATAL_ERROR "GCC < 4.8 doesn't support the address sanitizer")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
endif()
endif()
if (RAPIDJSON_BUILD_UBSAN)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9.0")
message(FATAL_ERROR "GCC < 4.9 doesn't support the undefined behavior sanitizer")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
endif()
endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror -Wno-missing-field-initializers")
if (RAPIDJSON_BUILD_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
if (RAPIDJSON_BUILD_ASAN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
endif()
if (RAPIDJSON_BUILD_UBSAN)
if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
endif()
endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_definitions(-D_CRT_SECURE_NO_WARNINGS=1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
endif()
#add extra search paths for libraries and includes
SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in")
SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE STRING "Directory where lib will install")
SET(DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}" CACHE PATH "Path to the documentation")
IF(UNIX OR CYGWIN)
SET(_CMAKE_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME}")
ELSEIF(WIN32)
SET(_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/cmake")
ENDIF()
SET(CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" CACHE PATH "The directory cmake fiels are installed in")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
if(RAPIDJSON_BUILD_DOC)
add_subdirectory(doc)
endif()
add_custom_target(travis_doc)
add_custom_command(TARGET travis_doc
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/travis-doxygen.sh)
if(RAPIDJSON_BUILD_EXAMPLES)
add_subdirectory(example)
endif()
if(RAPIDJSON_BUILD_TESTS)
if(MSVC11)
# required for VS2012 due to missing support for variadic templates
add_definitions(-D_VARIADIC_MAX=10)
endif(MSVC11)
add_subdirectory(test)
include(CTest)
endif()
# pkg-config
IF (UNIX OR CYGWIN)
CONFIGURE_FILE (${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.pc.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
@ONLY)
INSTALL (FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
DESTINATION "${LIB_INSTALL_DIR}/pkgconfig"
COMPONENT pkgconfig)
ENDIF()
install(FILES readme.md
DESTINATION "${DOC_INSTALL_DIR}"
COMPONENT doc)
install(DIRECTORY include/rapidjson
DESTINATION "${INCLUDE_INSTALL_DIR}"
COMPONENT dev)
install(DIRECTORY example/
DESTINATION "${DOC_INSTALL_DIR}/examples"
COMPONENT examples
# Following patterns are for excluding the intermediate/object files
# from an install of in-source CMake build.
PATTERN "CMakeFiles" EXCLUDE
PATTERN "Makefile" EXCLUDE
PATTERN "cmake_install.cmake" EXCLUDE)
# Provide config and version files to be used by other applications
# ===============================
export(PACKAGE ${PROJECT_NAME})
# cmake-modules
CONFIGURE_FILE(${PROJECT_NAME}Config.cmake.in
${PROJECT_NAME}Config.cmake
@ONLY)
CONFIGURE_FILE(${PROJECT_NAME}ConfigVersion.cmake.in
${PROJECT_NAME}ConfigVersion.cmake
@ONLY)
INSTALL(FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION "${CMAKE_INSTALL_DIR}"
COMPONENT dev)

View File

@ -0,0 +1,30 @@
SET(GTEST_SEARCH_PATH
"${GTEST_SOURCE_DIR}"
"${CMAKE_CURRENT_LIST_DIR}/../thirdparty/gtest/googletest")
IF(UNIX)
IF(RAPIDJSON_BUILD_THIRDPARTY_GTEST)
LIST(APPEND GTEST_SEARCH_PATH "/usr/src/gtest")
ELSE()
LIST(INSERT GTEST_SEARCH_PATH 1 "/usr/src/gtest")
ENDIF()
ENDIF()
FIND_PATH(GTEST_SOURCE_DIR
NAMES CMakeLists.txt src/gtest_main.cc
PATHS ${GTEST_SEARCH_PATH})
# Debian installs gtest include directory in /usr/include, thus need to look
# for include directory separately from source directory.
FIND_PATH(GTEST_INCLUDE_DIR
NAMES gtest/gtest.h
PATH_SUFFIXES include
HINTS ${GTEST_SOURCE_DIR}
PATHS ${GTEST_SEARCH_PATH})
INCLUDE(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GTestSrc DEFAULT_MSG
GTEST_SOURCE_DIR
GTEST_INCLUDE_DIR)

View File

@ -0,0 +1,7 @@
includedir=@INCLUDE_INSTALL_DIR@
Name: @PROJECT_NAME@
Description: A fast JSON parser/generator for C++ with both SAX/DOM style API
Version: @LIB_VERSION_STRING@
URL: https://github.com/miloyip/rapidjson
Cflags: -I${includedir}

View File

@ -0,0 +1,3 @@
get_filename_component(RAPIDJSON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
set(RAPIDJSON_INCLUDE_DIRS "@INCLUDE_INSTALL_DIR@")
message(STATUS "RapidJSON found. Headers: ${RAPIDJSON_INCLUDE_DIRS}")

View File

@ -0,0 +1,10 @@
SET(PACKAGE_VERSION "@LIB_VERSION_STRING@")
IF (PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)
SET(PACKAGE_VERSION_EXACT "true")
ENDIF (PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)
IF (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION)
SET(PACKAGE_VERSION_COMPATIBLE "true")
ELSE (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION)
SET(PACKAGE_VERSION_UNSUITABLE "true")
ENDIF (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION)

41
thirdparty/rapidjson-1.1.0/appveyor.yml vendored Normal file
View File

@ -0,0 +1,41 @@
os: Visual Studio 2015 CTP
version: 1.1.0.{build}
configuration:
- Debug
- Release
environment:
matrix:
# - VS_VERSION: 9 2008
# VS_PLATFORM: win32
# - VS_VERSION: 9 2008
# VS_PLATFORM: x64
- VS_VERSION: 10 2010
VS_PLATFORM: win32
- VS_VERSION: 10 2010
VS_PLATFORM: x64
- VS_VERSION: 11 2012
VS_PLATFORM: win32
- VS_VERSION: 11 2012
VS_PLATFORM: x64
- VS_VERSION: 12 2013
VS_PLATFORM: win32
- VS_VERSION: 12 2013
VS_PLATFORM: x64
- VS_VERSION: 14 2015
VS_PLATFORM: win32
- VS_VERSION: 14 2015
VS_PLATFORM: x64
before_build:
- git submodule update --init --recursive
- cmake -H. -BBuild/VS -G "Visual Studio %VS_VERSION%" -DCMAKE_GENERATOR_PLATFORM=%VS_PLATFORM% -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_SHARED_LIBS=true -Wno-dev
build:
project: Build\VS\RapidJSON.sln
parallel: true
verbosity: minimal
test_script:
- cd Build\VS && if %CONFIGURATION%==Debug (ctest --verbose -E perftest --build-config %CONFIGURATION%) else (ctest --verbose --build-config %CONFIGURATION%)

View File

@ -0,0 +1,25 @@
find_package(Doxygen)
IF(NOT DOXYGEN_FOUND)
MESSAGE(STATUS "No Doxygen found. Documentation won't be built")
ELSE()
file(GLOB SOURCES ${CMAKE_CURRENT_LIST_DIR}/../include/*)
file(GLOB MARKDOWN_DOC ${CMAKE_CURRENT_LIST_DIR}/../doc/*.md)
list(APPEND MARKDOWN_DOC ${CMAKE_CURRENT_LIST_DIR}/../readme.md)
CONFIGURE_FILE(Doxyfile.in Doxyfile @ONLY)
CONFIGURE_FILE(Doxyfile.zh-cn.in Doxyfile.zh-cn @ONLY)
add_custom_command(OUTPUT html
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.zh-cn
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/html
DEPENDS ${MARKDOWN_DOC} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile*
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../
)
add_custom_target(doc ALL DEPENDS html)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
DESTINATION ${DOC_INSTALL_DIR}
COMPONENT doc)
ENDIF()

2369
thirdparty/rapidjson-1.1.0/doc/Doxyfile.in vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
digraph {
compound=true
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
ranksep=0.2
nodesep=0.5
penwidth=0.5
colorscheme=spectral7
node [shape=box, fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5, style=filled, fillcolor=white]
edge [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
subgraph cluster1 {
margin="10,10"
labeljust="left"
label = "SAX"
style=filled
fillcolor=6
Reader -> Writer [style=invis]
}
subgraph cluster2 {
margin="10,10"
labeljust="left"
label = "DOM"
style=filled
fillcolor=7
Value
Document
}
Handler [label="<<concept>>\nHandler"]
{
edge [arrowtail=onormal, dir=back]
Value -> Document
Handler -> Document
Handler -> Writer
}
{
edge [arrowhead=vee, style=dashed, constraint=false]
Reader -> Handler [label="calls"]
Value -> Handler [label="calls"]
Document -> Reader [label="uses"]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,65 @@
digraph {
compound=true
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
ranksep=0.2
penwidth=0.5
node [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
edge [fontname="Inconsolata, Consolas", fontsize=10, arrowhead=normal]
{
node [shape=record, fontsize="8", margin="0.04", height=0.2, color=gray]
oldjson [label="\{|\"|m|s|g|\"|:|\"|H|e|l|l|o|\\|n|W|o|r|l|d|!|\"|,|\"|\\|u|0|0|7|3|t|a|r|s|\"|:|1|0|\}", xlabel="Before Parsing"]
//newjson [label="\{|\"|<a>m|s|g|\\0|:|\"|<b>H|e|l|l|o|\\n|W|o|r|l|d|!|\\0|\"|,|\"|<c>s|t|a|r|s|\\0|t|a|r|s|:|1|0|\}", xlabel="After Parsing"]
newjson [shape=plaintext, label=<
<table BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="2"><tr>
<td>{</td>
<td>"</td><td port="a">m</td><td>s</td><td>g</td><td bgcolor="yellow">\\0</td>
<td>:</td>
<td>"</td><td port="b">H</td><td>e</td><td>l</td><td>l</td><td>o</td><td bgcolor="yellow">\\n</td><td bgcolor="yellow">W</td><td bgcolor="yellow">o</td><td bgcolor="yellow">r</td><td bgcolor="yellow">l</td><td bgcolor="yellow">d</td><td bgcolor="yellow">!</td><td bgcolor="yellow">\\0</td><td>"</td>
<td>,</td>
<td>"</td><td port="c" bgcolor="yellow">s</td><td bgcolor="yellow">t</td><td bgcolor="yellow">a</td><td bgcolor="yellow">r</td><td bgcolor="yellow">s</td><td bgcolor="yellow">\\0</td><td>t</td><td>a</td><td>r</td><td>s</td>
<td>:</td>
<td>1</td><td>0</td>
<td>}</td>
</tr></table>
>, xlabel="After Parsing"]
}
subgraph cluster1 {
margin="10,10"
labeljust="left"
label = "Document by In situ Parsing"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
root [label="{object|}", fillcolor=3]
{
msg [label="{string|<a>}", fillcolor=5]
helloworld [label="{string|<a>}", fillcolor=5]
stars [label="{string|<a>}", fillcolor=5]
ten [label="{number|10}", fillcolor=6]
}
}
oldjson -> root [label=" ParseInsitu()" lhead="cluster1"]
edge [arrowhead=vee]
root -> { msg; stars }
edge [arrowhead="none"]
msg -> helloworld
stars -> ten
{
edge [arrowhead=vee, arrowtail=dot, arrowsize=0.5, dir=both, tailclip=false]
msg:a:c -> newjson:a
helloworld:a:c -> newjson:b
stars:a:c -> newjson:c
}
//oldjson -> newjson [style=invis]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,62 @@
digraph {
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
penwidth=0.0
node [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
edge [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
node [shape = doublecircle]; Start; Finish;
node [shape = box; style = "rounded, filled"; fillcolor=white ];
Start -> ArrayInitial [label=" ["];
Start -> ObjectInitial [label=" {"];
subgraph clusterArray {
margin="10,10"
style=filled
fillcolor=gray95
label = "Array"
ArrayInitial; Element; ElementDelimiter; ArrayFinish;
}
subgraph clusterObject {
margin="10,10"
style=filled
fillcolor=gray95
label = "Object"
ObjectInitial; MemberKey; KeyValueDelimiter; MemberValue; MemberDelimiter; ObjectFinish;
}
ArrayInitial -> ArrayInitial [label="["];
ArrayInitial -> ArrayFinish [label=" ]"];
ArrayInitial -> ObjectInitial [label="{", constraint=false];
ArrayInitial -> Element [label="string\nfalse\ntrue\nnull\nnumber"];
Element -> ArrayFinish [label="]"];
Element -> ElementDelimiter [label=","];
ElementDelimiter -> ArrayInitial [label=" ["];
ElementDelimiter -> ObjectInitial [label="{"];
ElementDelimiter -> Element [label="string\nfalse\ntrue\nnull\nnumber"];
ObjectInitial -> ObjectFinish [label=" }"];
ObjectInitial -> MemberKey [label=" string "];
MemberKey -> KeyValueDelimiter [label=":"];
KeyValueDelimiter -> ArrayInitial [label="["];
KeyValueDelimiter -> ObjectInitial [label=" {"];
KeyValueDelimiter -> MemberValue [label=" string\n false\n true\n null\n number"];
MemberValue -> ObjectFinish [label="}"];
MemberValue -> MemberDelimiter [label=","];
MemberDelimiter -> MemberKey [label=" string "];
ArrayFinish -> Finish;
ObjectFinish -> Finish;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@ -0,0 +1,47 @@
digraph {
compound=true
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
ranksep=0.2
penwidth=0.5
node [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
edge [fontname="Inconsolata, Consolas", fontsize=10, arrowhead=normal]
subgraph cluster1 {
margin="10,10"
labeljust="left"
label = "Before"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
{
rank = same
b1 [label="{b:number|456}", fillcolor=6]
a1 [label="{a:number|123}", fillcolor=6]
}
a1 -> b1 [style="dashed", label="Move", dir=back]
}
subgraph cluster2 {
margin="10,10"
labeljust="left"
label = "After"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
{
rank = same
b2 [label="{b:null|}", fillcolor=1]
a2 [label="{a:number|456}", fillcolor=6]
}
a2 -> b2 [style=invis, dir=back]
}
b1 -> b2 [style=invis]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,62 @@
digraph {
compound=true
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
ranksep=0.2
penwidth=0.5
node [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
edge [fontname="Inconsolata, Consolas", fontsize=10, arrowhead=normal]
subgraph cluster1 {
margin="10,10"
labeljust="left"
label = "Before Copying (Hypothetic)"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
c1 [label="{contact:array|}", fillcolor=4]
c11 [label="{|}"]
c12 [label="{|}"]
c13 [shape="none", label="...", style="solid"]
o1 [label="{o:object|}", fillcolor=3]
ghost [label="{o:object|}", style=invis]
c1 -> o1 [style="dashed", label="AddMember", constraint=false]
edge [arrowhead=vee]
c1 -> { c11; c12; c13 }
o1 -> ghost [style=invis]
}
subgraph cluster2 {
margin="10,10"
labeljust="left"
label = "After Copying (Hypothetic)"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
c2 [label="{contact:array|}", fillcolor=4]
c3 [label="{array|}", fillcolor=4]
c21 [label="{|}"]
c22 [label="{|}"]
c23 [shape=none, label="...", style="solid"]
o2 [label="{o:object|}", fillcolor=3]
cs [label="{string|\"contact\"}", fillcolor=5]
c31 [label="{|}"]
c32 [label="{|}"]
c33 [shape="none", label="...", style="solid"]
edge [arrowhead=vee]
c2 -> { c21; c22; c23 }
o2 -> cs
cs -> c3 [arrowhead=none]
c3 -> { c31; c32; c33 }
}
ghost -> o2 [style=invis]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,60 @@
digraph {
compound=true
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
ranksep=0.2
penwidth=0.5
forcelabels=true
node [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
edge [fontname="Inconsolata, Consolas", fontsize=10, arrowhead=normal]
subgraph cluster1 {
margin="10,10"
labeljust="left"
label = "Before Moving"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
c1 [label="{contact:array|}", fillcolor=4]
c11 [label="{|}"]
c12 [label="{|}"]
c13 [shape=none, label="...", style="solid"]
o1 [label="{o:object|}", fillcolor=3]
ghost [label="{o:object|}", style=invis]
c1 -> o1 [style="dashed", constraint=false, label="AddMember"]
edge [arrowhead=vee]
c1 -> { c11; c12; c13 }
o1 -> ghost [style=invis]
}
subgraph cluster2 {
margin="10,10"
labeljust="left"
label = "After Moving"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
c2 [label="{contact:null|}", fillcolor=1]
c3 [label="{array|}", fillcolor=4]
c21 [label="{|}"]
c22 [label="{|}"]
c23 [shape="none", label="...", style="solid"]
o2 [label="{o:object|}", fillcolor=3]
cs [label="{string|\"contact\"}", fillcolor=5]
c2 -> o2 [style="dashed", constraint=false, label="AddMember", style=invis]
edge [arrowhead=vee]
c3 -> { c21; c22; c23 }
o2 -> cs
cs -> c3 [arrowhead=none]
}
ghost -> o2 [style=invis]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,56 @@
digraph {
compound=true
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
ranksep=0.2
penwidth=0.5
node [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
edge [fontname="Inconsolata, Consolas", fontsize=10, arrowhead=normal]
{
node [shape=record, fontsize="8", margin="0.04", height=0.2, color=gray]
normaljson [label="\{|\"|m|s|g|\"|:|\"|H|e|l|l|o|\\|n|W|o|r|l|d|!|\"|,|\"|\\|u|0|0|7|3|t|a|r|s\"|:|1|0|\}"]
{
rank = same
msgstring [label="m|s|g|\\0"]
helloworldstring [label="H|e|l|l|o|\\n|W|o|r|l|d|!|\\0"]
starsstring [label="s|t|a|r|s\\0"]
}
}
subgraph cluster1 {
margin="10,10"
labeljust="left"
label = "Document by Normal Parsing"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
root [label="{object|}", fillcolor=3]
{
msg [label="{string|<a>}", fillcolor=5]
helloworld [label="{string|<a>}", fillcolor=5]
stars [label="{string|<a>}", fillcolor=5]
ten [label="{number|10}", fillcolor=6]
}
}
normaljson -> root [label=" Parse()" lhead="cluster1"]
edge [arrowhead=vee]
root -> { msg; stars }
edge [arrowhead="none"]
msg -> helloworld
stars -> ten
edge [arrowhead=vee, arrowtail=dot, arrowsize=0.5, dir=both, tailclip=false]
msg:a:c -> msgstring:w
helloworld:a:c -> helloworldstring:w
stars:a:c -> starsstring:w
msgstring -> helloworldstring -> starsstring [style=invis]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,54 @@
digraph {
compound=true
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
ranksep=0.2
penwidth=0.5
node [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
edge [fontname="Inconsolata, Consolas", fontsize=10, arrowhead=normal]
{
node [shape=record, fontsize="8", margin="0.04", height=0.2, color=gray]
srcjson [label="\{|\"|p|r|o|j|e|c|t|\"|:|\"|r|a|p|i|d|j|s|o|n|\"|,|\"|s|t|a|r|s|\"|:|1|0|\}"]
dstjson [label="\{|\"|p|r|o|j|e|c|t|\"|:|\"|r|a|p|i|d|j|s|o|n|\"|,|\"|s|t|a|r|s|\"|:|1|1|\}"]
}
{
node [shape="box", style="filled", fillcolor="gray95"]
Document2 [label="(Modified) Document"]
Writer
}
subgraph cluster1 {
margin="10,10"
labeljust="left"
label = "Document"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
root [label="{object|}", fillcolor=3]
{
project [label="{string|\"project\"}", fillcolor=5]
rapidjson [label="{string|\"rapidjson\"}", fillcolor=5]
stars [label="{string|\"stars\"}", fillcolor=5]
ten [label="{number|10}", fillcolor=6]
}
edge [arrowhead=vee]
root -> { project; stars }
edge [arrowhead="none"]
project -> rapidjson
stars -> ten
}
srcjson -> root [label=" Parse()", lhead="cluster1"]
ten -> Document2 [label=" Increase \"stars\"", ltail="cluster1" ]
Document2 -> Writer [label=" Traverse DOM by Accept()"]
Writer -> dstjson [label=" Output to StringBuffer"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -0,0 +1,58 @@
digraph {
compound=true
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
ranksep=0.2
penwidth=0.5
node [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
edge [fontname="Inconsolata, Consolas", fontsize=10]
subgraph cluster1 {
margin="10,10"
labeljust="left"
label = "Document"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
root [label="{object|}", fillcolor=3]
{
hello [label="{string|\"hello\"}", fillcolor=5]
t [label="{string|\"t\"}", fillcolor=5]
f [label="{string|\"f\"}", fillcolor=5]
n [label="{string|\"n\"}", fillcolor=5]
i [label="{string|\"i\"}", fillcolor=5]
pi [label="{string|\"pi\"}", fillcolor=5]
a [label="{string|\"a\"}", fillcolor=5]
world [label="{string|\"world\"}", fillcolor=5]
true [label="{true|}", fillcolor=7]
false [label="{false|}", fillcolor=2]
null [label="{null|}", fillcolor=1]
i1 [label="{number|123}", fillcolor=6]
pi1 [label="{number|3.1416}", fillcolor=6]
array [label="{array|size=4}", fillcolor=4]
a1 [label="{number|1}", fillcolor=6]
a2 [label="{number|2}", fillcolor=6]
a3 [label="{number|3}", fillcolor=6]
a4 [label="{number|4}", fillcolor=6]
}
edge [arrowhead=vee]
root -> { hello; t; f; n; i; pi; a }
array -> { a1; a2; a3; a4 }
edge [arrowhead=none]
hello -> world
t -> true
f -> false
n -> null
i -> i1
pi -> pi1
a -> array
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,73 @@
digraph {
rankdir=LR
compound=true
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
ranksep=0.3
nodesep=0.15
penwidth=0.5
colorscheme=spectral7
node [shape=box, fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5, style=filled, fillcolor=white]
edge [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
subgraph cluster0 {
style=filled
fillcolor=4
Encoding [label="<<concept>>\nEncoding"]
edge [arrowtail=onormal, dir=back]
Encoding -> { UTF8; UTF16; UTF32; ASCII; AutoUTF }
UTF16 -> { UTF16LE; UTF16BE }
UTF32 -> { UTF32LE; UTF32BE }
}
subgraph cluster1 {
style=filled
fillcolor=5
Stream [label="<<concept>>\nStream"]
InputByteStream [label="<<concept>>\nInputByteStream"]
OutputByteStream [label="<<concept>>\nOutputByteStream"]
edge [arrowtail=onormal, dir=back]
Stream -> {
StringStream; InsituStringStream; StringBuffer;
EncodedInputStream; EncodedOutputStream;
AutoUTFInputStream; AutoUTFOutputStream
InputByteStream; OutputByteStream
}
InputByteStream -> { MemoryStream; FlieReadStream }
OutputByteStream -> { MemoryBuffer; FileWriteStream }
}
subgraph cluster2 {
style=filled
fillcolor=3
Allocator [label="<<concept>>\nAllocator"]
edge [arrowtail=onormal, dir=back]
Allocator -> { CrtAllocator; MemoryPoolAllocator }
}
{
edge [arrowtail=odiamond, arrowhead=vee, dir=both]
EncodedInputStream -> InputByteStream
EncodedOutputStream -> OutputByteStream
AutoUTFInputStream -> InputByteStream
AutoUTFOutputStream -> OutputByteStream
MemoryPoolAllocator -> Allocator [label="base", tailport=s]
}
{
edge [arrowhead=vee, style=dashed]
AutoUTFInputStream -> AutoUTF
AutoUTFOutputStream -> AutoUTF
}
//UTF32LE -> Stream [style=invis]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

280
thirdparty/rapidjson-1.1.0/doc/dom.md vendored Normal file
View File

@ -0,0 +1,280 @@
# DOM
Document Object Model(DOM) is an in-memory representation of JSON for query and manipulation. The basic usage of DOM is described in [Tutorial](doc/tutorial.md). This section will describe some details and more advanced usages.
[TOC]
# Template {#Template}
In the tutorial, `Value` and `Document` was used. Similarly to `std::string`, these are actually `typedef` of template classes:
~~~~~~~~~~cpp
namespace rapidjson {
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
class GenericValue {
// ...
};
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
class GenericDocument : public GenericValue<Encoding, Allocator> {
// ...
};
typedef GenericValue<UTF8<> > Value;
typedef GenericDocument<UTF8<> > Document;
} // namespace rapidjson
~~~~~~~~~~
User can customize these template parameters.
## Encoding {#Encoding}
The `Encoding` parameter specifies the encoding of JSON String value in memory. Possible options are `UTF8`, `UTF16`, `UTF32`. Note that, these 3 types are also template class. `UTF8<>` is `UTF8<char>`, which means using char to store the characters. You may refer to [Encoding](doc/encoding.md) for details.
Suppose a Windows application would query localization strings stored in JSON files. Unicode-enabled functions in Windows use UTF-16 (wide character) encoding. No matter what encoding was used in JSON files, we can store the strings in UTF-16 in memory.
~~~~~~~~~~cpp
using namespace rapidjson;
typedef GenericDocument<UTF16<> > WDocument;
typedef GenericValue<UTF16<> > WValue;
FILE* fp = fopen("localization.json", "rb"); // non-Windows use "r"
char readBuffer[256];
FileReadStream bis(fp, readBuffer, sizeof(readBuffer));
AutoUTFInputStream<unsigned, FileReadStream> eis(bis); // wraps bis into eis
WDocument d;
d.ParseStream<0, AutoUTF<unsigned> >(eis);
const WValue locale(L"ja"); // Japanese
MessageBoxW(hWnd, d[locale].GetString(), L"Test", MB_OK);
~~~~~~~~~~
## Allocator {#Allocator}
The `Allocator` defines which allocator class is used when allocating/deallocating memory for `Document`/`Value`. `Document` owns, or references to an `Allocator` instance. On the other hand, `Value` does not do so, in order to reduce memory consumption.
The default allocator used in `GenericDocument` is `MemoryPoolAllocator`. This allocator actually allocate memory sequentially, and cannot deallocate one by one. This is very suitable when parsing a JSON into a DOM tree.
Another allocator is `CrtAllocator`, of which CRT is short for C RunTime library. This allocator simply calls the standard `malloc()`/`realloc()`/`free()`. When there is a lot of add and remove operations, this allocator may be preferred. But this allocator is far less efficient than `MemoryPoolAllocator`.
# Parsing {#Parsing}
`Document` provides several functions for parsing. In below, (1) is the fundamental function, while the others are helpers which call (1).
~~~~~~~~~~cpp
using namespace rapidjson;
// (1) Fundamental
template <unsigned parseFlags, typename SourceEncoding, typename InputStream>
GenericDocument& GenericDocument::ParseStream(InputStream& is);
// (2) Using the same Encoding for stream
template <unsigned parseFlags, typename InputStream>
GenericDocument& GenericDocument::ParseStream(InputStream& is);
// (3) Using default parse flags
template <typename InputStream>
GenericDocument& GenericDocument::ParseStream(InputStream& is);
// (4) In situ parsing
template <unsigned parseFlags>
GenericDocument& GenericDocument::ParseInsitu(Ch* str);
// (5) In situ parsing, using default parse flags
GenericDocument& GenericDocument::ParseInsitu(Ch* str);
// (6) Normal parsing of a string
template <unsigned parseFlags, typename SourceEncoding>
GenericDocument& GenericDocument::Parse(const Ch* str);
// (7) Normal parsing of a string, using same Encoding of Document
template <unsigned parseFlags>
GenericDocument& GenericDocument::Parse(const Ch* str);
// (8) Normal parsing of a string, using default parse flags
GenericDocument& GenericDocument::Parse(const Ch* str);
~~~~~~~~~~
The examples of [tutorial](doc/tutorial.md) uses (8) for normal parsing of string. The examples of [stream](doc/stream.md) uses the first three. *In situ* parsing will be described soon.
The `parseFlags` are combination of the following bit-flags:
Parse flags | Meaning
------------------------------|-----------------------------------
`kParseNoFlags` | No flag is set.
`kParseDefaultFlags` | Default parse flags. It is equal to macro `RAPIDJSON_PARSE_DEFAULT_FLAGS`, which is defined as `kParseNoFlags`.
`kParseInsituFlag` | In-situ(destructive) parsing.
`kParseValidateEncodingFlag` | Validate encoding of JSON strings.
`kParseIterativeFlag` | Iterative(constant complexity in terms of function call stack size) parsing.
`kParseStopWhenDoneFlag` | After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate `kParseErrorDocumentRootNotSingular` error. Using this flag for parsing multiple JSONs in the same stream.
`kParseFullPrecisionFlag` | Parse number in full precision (slower). If this flag is not set, the normal precision (faster) is used. Normal precision has maximum 3 [ULP](http://en.wikipedia.org/wiki/Unit_in_the_last_place) error.
`kParseCommentsFlag` | Allow one-line `// ...` and multi-line `/* ... */` comments (relaxed JSON syntax).
`kParseNumbersAsStringsFlag` | Parse numerical type values as strings.
`kParseTrailingCommasFlag` | Allow trailing commas at the end of objects and arrays (relaxed JSON syntax).
`kParseNanAndInfFlag` | Allow parsing `NaN`, `Inf`, `Infinity`, `-Inf` and `-Infinity` as `double` values (relaxed JSON syntax).
By using a non-type template parameter, instead of a function parameter, C++ compiler can generate code which is optimized for specified combinations, improving speed, and reducing code size (if only using a single specialization). The downside is the flags needed to be determined in compile-time.
The `SourceEncoding` parameter defines what encoding is in the stream. This can be differed to the `Encoding` of the `Document`. See [Transcoding and Validation](#TranscodingAndValidation) section for details.
And the `InputStream` is type of input stream.
## Parse Error {#ParseError}
When the parse processing succeeded, the `Document` contains the parse results. When there is an error, the original DOM is *unchanged*. And the error state of parsing can be obtained by `bool HasParseError()`, `ParseErrorCode GetParseError()` and `size_t GetParseOffset()`.
Parse Error Code | Description
--------------------------------------------|---------------------------------------------------
`kParseErrorNone` | No error.
`kParseErrorDocumentEmpty` | The document is empty.
`kParseErrorDocumentRootNotSingular` | The document root must not follow by other values.
`kParseErrorValueInvalid` | Invalid value.
`kParseErrorObjectMissName` | Missing a name for object member.
`kParseErrorObjectMissColon` | Missing a colon after a name of object member.
`kParseErrorObjectMissCommaOrCurlyBracket` | Missing a comma or `}` after an object member.
`kParseErrorArrayMissCommaOrSquareBracket` | Missing a comma or `]` after an array element.
`kParseErrorStringUnicodeEscapeInvalidHex` | Incorrect hex digit after `\\u` escape in string.
`kParseErrorStringUnicodeSurrogateInvalid` | The surrogate pair in string is invalid.
`kParseErrorStringEscapeInvalid` | Invalid escape character in string.
`kParseErrorStringMissQuotationMark` | Missing a closing quotation mark in string.
`kParseErrorStringInvalidEncoding` | Invalid encoding in string.
`kParseErrorNumberTooBig` | Number too big to be stored in `double`.
`kParseErrorNumberMissFraction` | Miss fraction part in number.
`kParseErrorNumberMissExponent` | Miss exponent in number.
The offset of error is defined as the character number from beginning of stream. Currently RapidJSON does not keep track of line number.
To get an error message, RapidJSON provided a English messages in `rapidjson/error/en.h`. User can customize it for other locales, or use a custom localization system.
Here shows an example of parse error handling.
~~~~~~~~~~cpp
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
// ...
Document d;
if (d.Parse(json).HasParseError()) {
fprintf(stderr, "\nError(offset %u): %s\n",
(unsigned)d.GetErrorOffset(),
GetParseError_En(d.GetParseError()));
// ...
}
~~~~~~~~~~
## In Situ Parsing {#InSituParsing}
From [Wikipedia](http://en.wikipedia.org/wiki/In_situ):
> *In situ* ... is a Latin phrase that translates literally to "on site" or "in position". It means "locally", "on site", "on the premises" or "in place" to describe an event where it takes place, and is used in many different contexts.
> ...
> (In computer science) An algorithm is said to be an in situ algorithm, or in-place algorithm, if the extra amount of memory required to execute the algorithm is O(1), that is, does not exceed a constant no matter how large the input. For example, heapsort is an in situ sorting algorithm.
In normal parsing process, a large overhead is to decode JSON strings and copy them to other buffers. *In situ* parsing decodes those JSON string at the place where it is stored. It is possible in JSON because the length of decoded string is always shorter than or equal to the one in JSON. In this context, decoding a JSON string means to process the escapes, such as `"\n"`, `"\u1234"`, etc., and add a null terminator (`'\0'`)at the end of string.
The following diagrams compare normal and *in situ* parsing. The JSON string values contain pointers to the decoded string.
![normal parsing](diagram/normalparsing.png)
In normal parsing, the decoded string are copied to freshly allocated buffers. `"\\n"` (2 characters) is decoded as `"\n"` (1 character). `"\\u0073"` (6 characters) is decoded as `"s"` (1 character).
![instiu parsing](diagram/insituparsing.png)
*In situ* parsing just modified the original JSON. Updated characters are highlighted in the diagram. If the JSON string does not contain escape character, such as `"msg"`, the parsing process merely replace the closing double quotation mark with a null character.
Since *in situ* parsing modify the input, the parsing API needs `char*` instead of `const char*`.
~~~~~~~~~~cpp
// Read whole file into a buffer
FILE* fp = fopen("test.json", "r");
fseek(fp, 0, SEEK_END);
size_t filesize = (size_t)ftell(fp);
fseek(fp, 0, SEEK_SET);
char* buffer = (char*)malloc(filesize + 1);
size_t readLength = fread(buffer, 1, filesize, fp);
buffer[readLength] = '\0';
fclose(fp);
// In situ parsing the buffer into d, buffer will also be modified
Document d;
d.ParseInsitu(buffer);
// Query/manipulate the DOM here...
free(buffer);
// Note: At this point, d may have dangling pointers pointed to the deallocated buffer.
~~~~~~~~~~
The JSON strings are marked as const-string. But they may not be really "constant". The life cycle of it depends on the JSON buffer.
In situ parsing minimizes allocation overheads and memory copying. Generally this improves cache coherence, which is an important factor of performance in modern computer.
There are some limitations of *in situ* parsing:
1. The whole JSON is in memory.
2. The source encoding in stream and target encoding in document must be the same.
3. The buffer need to be retained until the document is no longer used.
4. If the DOM need to be used for long period after parsing, and there are few JSON strings in the DOM, retaining the buffer may be a memory waste.
*In situ* parsing is mostly suitable for short-term JSON that only need to be processed once, and then be released from memory. In practice, these situation is very common, for example, deserializing JSON to C++ objects, processing web requests represented in JSON, etc.
## Transcoding and Validation {#TranscodingAndValidation}
RapidJSON supports conversion between Unicode formats (officially termed UCS Transformation Format) internally. During DOM parsing, the source encoding of the stream can be different from the encoding of the DOM. For example, the source stream contains a UTF-8 JSON, while the DOM is using UTF-16 encoding. There is an example code in [EncodedInputStream](doc/stream.md).
When writing a JSON from DOM to output stream, transcoding can also be used. An example is in [EncodedOutputStream](doc/stream.md).
During transcoding, the source string is decoded to into Unicode code points, and then the code points are encoded in the target format. During decoding, it will validate the byte sequence in the source string. If it is not a valid sequence, the parser will be stopped with `kParseErrorStringInvalidEncoding` error.
When the source encoding of stream is the same as encoding of DOM, by default, the parser will *not* validate the sequence. User may use `kParseValidateEncodingFlag` to force validation.
# Techniques {#Techniques}
Some techniques about using DOM API is discussed here.
## DOM as SAX Event Publisher
In RapidJSON, stringifying a DOM with `Writer` may be look a little bit weired.
~~~~~~~~~~cpp
// ...
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
~~~~~~~~~~
Actually, `Value::Accept()` is responsible for publishing SAX events about the value to the handler. With this design, `Value` and `Writer` are decoupled. `Value` can generate SAX events, and `Writer` can handle those events.
User may create custom handlers for transforming the DOM into other formats. For example, a handler which converts the DOM into XML.
For more about SAX events and handler, please refer to [SAX](doc/sax.md).
## User Buffer {#UserBuffer}
Some applications may try to avoid memory allocations whenever possible.
`MemoryPoolAllocator` can support this by letting user to provide a buffer. The buffer can be on the program stack, or a "scratch buffer" which is statically allocated (a static/global array) for storing temporary data.
`MemoryPoolAllocator` will use the user buffer to satisfy allocations. When the user buffer is used up, it will allocate a chunk of memory from the base allocator (by default the `CrtAllocator`).
Here is an example of using stack memory. The first allocator is for storing values, while the second allocator is for storing temporary data during parsing.
~~~~~~~~~~cpp
typedef GenericDocument<UTF8<>, MemoryPoolAllocator<>, MemoryPoolAllocator<>> DocumentType;
char valueBuffer[4096];
char parseBuffer[1024];
MemoryPoolAllocator<> valueAllocator(valueBuffer, sizeof(valueBuffer));
MemoryPoolAllocator<> parseAllocator(parseBuffer, sizeof(parseBuffer));
DocumentType d(&valueAllocator, sizeof(parseBuffer), &parseAllocator);
d.Parse(json);
~~~~~~~~~~
If the total size of allocation is less than 4096+1024 bytes during parsing, this code does not invoke any heap allocation (via `new` or `malloc()`) at all.
User can query the current memory consumption in bytes via `MemoryPoolAllocator::Size()`. And then user can determine a suitable size of user buffer.

View File

@ -0,0 +1,284 @@
# DOM
文档对象模型Document Object Model, DOM是一种罝于内存中的 JSON 表示方式,以供查询及操作。我们己于 [教程](doc/tutorial.zh-cn.md) 中介绍了 DOM 的基本用法,本节将讲述一些细节及高级用法。
[TOC]
# 模板 {#Template}
教程中使用了 `Value``Document` 类型。与 `std::string` 相似,这些类型其实是两个模板类的 `typedef`
~~~~~~~~~~cpp
namespace rapidjson {
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
class GenericValue {
// ...
};
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
class GenericDocument : public GenericValue<Encoding, Allocator> {
// ...
};
typedef GenericValue<UTF8<> > Value;
typedef GenericDocument<UTF8<> > Document;
} // namespace rapidjson
~~~~~~~~~~
使用者可以自定义这些模板参数。
## 编码 {#Encoding}
`Encoding` 参数指明在内存中的 JSON String 使用哪种编码。可行的选项有 `UTF8``UTF16``UTF32`。要注意这 3 个类型其实也是模板类。`UTF8<>` 等同 `UTF8<char>`,这代表它使用 `char` 来存储字符串。更多细节可以参考 [编码](doc/encoding.zh-cn.md)。
这里是一个例子。假设一个 Windows 应用软件希望查询存储于 JSON 中的本地化字符串。Windows 中含 Unicode 的函数使用 UTF-16宽字符编码。无论 JSON 文件使用哪种编码,我们都可以把字符串以 UTF-16 形式存储在内存。
~~~~~~~~~~cpp
using namespace rapidjson;
typedef GenericDocument<UTF16<> > WDocument;
typedef GenericValue<UTF16<> > WValue;
FILE* fp = fopen("localization.json", "rb"); // 非 Windows 平台使用 "r"
char readBuffer[256];
FileReadStream bis(fp, readBuffer, sizeof(readBuffer));
AutoUTFInputStream<unsigned, FileReadStream> eis(bis); // 包装 bis 成 eis
WDocument d;
d.ParseStream<0, AutoUTF<unsigned> >(eis);
const WValue locale(L"ja"); // Japanese
MessageBoxW(hWnd, d[locale].GetString(), L"Test", MB_OK);
~~~~~~~~~~
## 分配器 {#Allocator}
`Allocator` 定义当 `Document`/`Value` 分配或释放内存时使用那个分配类。`Document` 拥有或引用到一个 `Allocator` 实例。而为了节省内存,`Value` 没有这么做。
`GenericDocument` 的缺省分配器是 `MemoryPoolAllocator`。此分配器实际上会顺序地分配内存,并且不能逐一释放。当要解析一个 JSON 并生成 DOM这种分配器是非常合适的。
RapidJSON 还提供另一个分配器 `CrtAllocator`,当中 CRT 是 C 运行库C RunTime library的缩写。此分配器简单地读用标准的 `malloc()`/`realloc()`/`free()`。当我们需要许多增减操作,这种分配器会更为适合。然而这种分配器远远比 `MemoryPoolAllocator` 低效。
# 解析 {#Parsing}
`Document` 提供几个解析函数。以下的 (1) 是根本的函数,其他都是调用 (1) 的协助函数。
~~~~~~~~~~cpp
using namespace rapidjson;
// (1) 根本
template <unsigned parseFlags, typename SourceEncoding, typename InputStream>
GenericDocument& GenericDocument::ParseStream(InputStream& is);
// (2) 使用流的编码
template <unsigned parseFlags, typename InputStream>
GenericDocument& GenericDocument::ParseStream(InputStream& is);
// (3) 使用缺省标志
template <typename InputStream>
GenericDocument& GenericDocument::ParseStream(InputStream& is);
// (4) 原位解析
template <unsigned parseFlags>
GenericDocument& GenericDocument::ParseInsitu(Ch* str);
// (5) 原位解析,使用缺省标志
GenericDocument& GenericDocument::ParseInsitu(Ch* str);
// (6) 正常解析一个字符串
template <unsigned parseFlags, typename SourceEncoding>
GenericDocument& GenericDocument::Parse(const Ch* str);
// (7) 正常解析一个字符串,使用 Document 的编码
template <unsigned parseFlags>
GenericDocument& GenericDocument::Parse(const Ch* str);
// (8) 正常解析一个字符串,使用缺省标志
GenericDocument& GenericDocument::Parse(const Ch* str);
~~~~~~~~~~
[教程](doc/tutorial.zh-cn.md) 中的例使用 (8) 去正常解析字符串。而 [](doc/stream.zh-cn.md) 的例子使用前 3 个函数。我们将稍后介绍原位(*In situ* 解析。
`parseFlags` 是以下位标置的组合:
解析位标志 | 意义
------------------------------|-----------------------------------
`kParseNoFlags` | 没有任何标志。
`kParseDefaultFlags` | 缺省的解析选项。它等于 `RAPIDJSON_PARSE_DEFAULT_FLAGS` 宏,此宏定义为 `kParseNoFlags`
`kParseInsituFlag` | 原位(破坏性)解析。
`kParseValidateEncodingFlag` | 校验 JSON 字符串的编码。
`kParseIterativeFlag` | 迭代式(调用堆栈大小为常数复杂度)解析。
`kParseStopWhenDoneFlag` | 当从流解析了一个完整的 JSON 根节点之后,停止继续处理余下的流。当使用了此标志,解析器便不会产生 `kParseErrorDocumentRootNotSingular` 错误。可使用本标志去解析同一个流里的多个 JSON。
`kParseFullPrecisionFlag` | 使用完整的精确度去解析数字(较慢)。如不设置此标节,则会使用正常的精确度(较快)。正常精确度会有最多 3 个 [ULP](http://en.wikipedia.org/wiki/Unit_in_the_last_place) 的误差。
`kParseCommentsFlag` | 容许单行 `// ...` 及多行 `/* ... */` 注释(放宽的 JSON 语法)。
`kParseNumbersAsStringsFlag` | 把数字类型解析成字符串。
`kParseTrailingCommasFlag` | 容许在对象和数组结束前含有逗号(放宽的 JSON 语法)。
`kParseNanAndInfFlag` | 容许 `NaN``Inf``Infinity``-Inf``-Infinity` 作为 `double` 值(放宽的 JSON 语法)。
由于使用了非类型模板参数而不是函数参数C++ 编译器能为个别组合生成代码,以改善性能及减少代码尺寸(当只用单种特化)。缺点是需要在编译期决定标志。
`SourceEncoding` 参数定义流使用了什么编码。这与 `Document``Encoding` 不相同。细节可参考 [转码和校验](#TranscodingAndValidation) 一节。
此外 `InputStream` 是输入流的类型。
## 解析错误 {#ParseError}
当解析过程顺利完成,`Document` 便会含有解析结果。当过程出现错误,原来的 DOM 会 * 维持不便 *。可使用 `bool HasParseError()``ParseErrorCode GetParseError()``size_t GetParseOffset()` 获取解析的错误状态。
解析错误代号 | 描述
--------------------------------------------|---------------------------------------------------
`kParseErrorNone` | 无错误。
`kParseErrorDocumentEmpty` | 文档是空的。
`kParseErrorDocumentRootNotSingular` | 文档的根后面不能有其它值。
`kParseErrorValueInvalid` | 不合法的值。
`kParseErrorObjectMissName` | Object 成员缺少名字。
`kParseErrorObjectMissColon` | Object 成员名字后缺少冒号。
`kParseErrorObjectMissCommaOrCurlyBracket` | Object 成员后缺少逗号或 `}`
`kParseErrorArrayMissCommaOrSquareBracket` | Array 元素后缺少逗号或 `]`
`kParseErrorStringUnicodeEscapeInvalidHex` | String 中的 `\\u` 转义符后含非十六进位数字。
`kParseErrorStringUnicodeSurrogateInvalid` | String 中的代理对surrogate pair不合法。
`kParseErrorStringEscapeInvalid` | String 含非法转义字符。
`kParseErrorStringMissQuotationMark` | String 缺少关闭引号。
`kParseErrorStringInvalidEncoding` | String 含非法编码。
`kParseErrorNumberTooBig` | Number 的值太大,不能存储于 `double`
`kParseErrorNumberMissFraction` | Number 缺少了小数部分。
`kParseErrorNumberMissExponent` | Number 缺少了指数。
错误的偏移量定义为从流开始至错误处的字符数量。目前 RapidJSON 不记录错误行号。
要取得错误讯息RapidJSON 在 `rapidjson/error/en.h` 中提供了英文错误讯息。使用者可以修改它用于其他语言环境,或使用一个自定义的本地化系统。
以下是一个处理错误的例子。
~~~~~~~~~~cpp
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
// ...
Document d;
if (d.Parse(json).HasParseError()) {
fprintf(stderr, "\nError(offset %u): %s\n",
(unsigned)d.GetErrorOffset(),
GetParseError_En(d.GetParseErrorCode()));
// ...
}
~~~~~~~~~~
## 原位解析 {#InSituParsing}
根据 [维基百科](http://en.wikipedia.org/wiki/In_situ):
> *In situ* ... is a Latin phrase that translates literally to "on site" or "in position". It means "locally", "on site", "on the premises" or "in place" to describe an event where it takes place, and is used in many different contexts.
> ...
> (In computer science) An algorithm is said to be an in situ algorithm, or in-place algorithm, if the extra amount of memory required to execute the algorithm is O(1), that is, does not exceed a constant no matter how large the input. For example, heapsort is an in situ sorting algorithm.
> 翻译:*In situ*……是一个拉丁文片语,字面上的意思是指「现场」、「在位置」。在许多不同语境中,它描述一个事件发生的位置,意指「本地」、「现场」、「在处所」、「就位」。
> ……
> (在计算机科学中)一个算法若称为原位算法,或在位算法,是指执行该算法所需的额外内存空间是 O(1) 的,换句话说,无论输入大小都只需要常数空间。例如,堆排序是一个原位排序算法。
在正常的解析过程中,对 JSON string 解码并复制至其他缓冲区是一个很大的开销。原位解析(*in situ* parsing把这些 JSON string 直接解码于它原来存储的地方。由于解码后的 string 长度总是短于或等于原来储存于 JSON 的 string所以这是可行的。在这个语境下对 JSON string 进行解码是指处理转义符,如 `"\n"``"\u1234"` 等,以及在 string 末端加入空终止符号 (`'\0'`)。
以下的图比较正常及原位解析。JSON string 值包含指向解码后的字符串。
![正常解析](diagram/normalparsing.png)
在正常解析中,解码后的字符串被复制至全新分配的缓冲区中。`"\\n"`2 个字符)被解码成 `"\n"`1 个字符)。`"\\u0073"`6 个字符)被解码成 `"s"`1 个字符)。
![原位解析](diagram/insituparsing.png)
原位解析直接修改了原来的 JSON。图中高亮了被更新的字符。若 JSON string 不含转义符,例如 `"msg"`,那么解析过程仅仅是以空字符代替结束双引号。
由于原位解析修改了输入,其解析 API 需要 `char*` 而非 `const char*`
~~~~~~~~~~cpp
// 把整个文件读入 buffer
FILE* fp = fopen("test.json", "r");
fseek(fp, 0, SEEK_END);
size_t filesize = (size_t)ftell(fp);
fseek(fp, 0, SEEK_SET);
char* buffer = (char*)malloc(filesize + 1);
size_t readLength = fread(buffer, 1, filesize, fp);
buffer[readLength] = '\0';
fclose(fp);
// 原位解析 buffer 至 dbuffer 内容会被修改。
Document d;
d.ParseInsitu(buffer);
// 在此查询、修改 DOM……
free(buffer);
// 注意在这个位置d 可能含有指向已被释放的 buffer 的悬空指针
~~~~~~~~~~
JSON string 会被打上 const-string 的标志。但它们可能并非真正的「常数」。它的生命周期取决于存储 JSON 的缓冲区。
原位解析把分配开销及内存复制减至最小。通常这样做能改善缓存一致性,而这对现代计算机来说是一个重要的性能因素。
原位解析有以下限制:
1. 整个 JSON 须存储在内存之中。
2. 流的来源缓码与文档的目标编码必须相同。
3. 需要保留缓冲区,直至文档不再被使用。
4. 若 DOM 需要在解析后被长期使用,而 DOM 内只有很少 JSON string保留缓冲区可能造成内存浪费。
原位解析最适合用于短期的、用完即弃的 JSON。实际应用中这些场合是非常普遍的例如反序列化 JSON 至 C++ 对象、处理以 JSON 表示的 web 请求等。
## 转码与校验 {#TranscodingAndValidation}
RapidJSON 内部支持不同 Unicode 格式(正式的术语是 UCS 变换格式)间的转换。在 DOM 解析时,流的来源编码与 DOM 的编码可以不同。例如,来源流可能含有 UTF-8 的 JSON而 DOM 则使用 UTF-16 编码。在 [EncodedInputStream](doc/stream.zh-cn.md) 一节里有一个例子。
当从 DOM 输出一个 JSON 至输出流之时,也可以使用转码功能。在 [EncodedOutputStream](doc/stream.zh-cn.md) 一节里有一个例子。
在转码过程中,会把来源 string 解码成 Unicode 码点,然后把码点编码成目标格式。在解码时,它会校验来源 string 的字节序列是否合法。若遇上非合法序列,解析器会停止并返回 `kParseErrorStringInvalidEncoding` 错误。
当来源编码与 DOM 的编码相同,解析器缺省地 * 不会 * 校验序列。使用者可开启 `kParseValidateEncodingFlag` 去强制校验。
# 技巧 {#Techniques}
这里讨论一些 DOM API 的使用技巧。
## 把 DOM 作为 SAX 事件发表者
在 RapidJSON 中,利用 `Writer` 把 DOM 生成 JSON 的做法,看来有点奇怪。
~~~~~~~~~~cpp
// ...
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
~~~~~~~~~~
实际上,`Value::Accept()` 是负责发布该值相关的 SAX 事件至处理器的。通过这个设计,`Value``Writer` 解除了偶合。`Value` 可生成 SAX 事件,而 `Writer` 则可以处理这些事件。
使用者可以创建自定义的处理器,去把 DOM 转换成其它格式。例如,一个把 DOM 转换成 XML 的处理器。
要知道更多关于 SAX 事件与处理器,可参阅 [SAX](doc/sax.zh-cn.md)。
## 使用者缓冲区 {#UserBuffer}
许多应用软件可能需要尽量减少内存分配。
`MemoryPoolAllocator` 可以帮助这方面它容许使用者提供一个缓冲区。该缓冲区可能置于程序堆栈或是一个静态分配的「草稿缓冲区scratch buffer一个静态全局的数组用于储存临时数据。
`MemoryPoolAllocator` 会先用使用者缓冲区去解决分配请求。当使用者缓冲区用完,就会从基础分配器(缺省为 `CrtAllocator`)分配一块内存。
以下是使用堆栈内存的例子,第一个分配器用于存储值,第二个用于解析时的临时缓冲。
~~~~~~~~~~cpp
typedef GenericDocument<UTF8<>, MemoryPoolAllocator<>, MemoryPoolAllocator<>> DocumentType;
char valueBuffer[4096];
char parseBuffer[1024];
MemoryPoolAllocator<> valueAllocator(valueBuffer, sizeof(valueBuffer));
MemoryPoolAllocator<> parseAllocator(parseBuffer, sizeof(parseBuffer));
DocumentType d(&valueAllocator, sizeof(parseBuffer), &parseAllocator);
d.Parse(json);
~~~~~~~~~~
若解析时分配总量少于 4096+1024 字节时,这段代码不会造成任何堆内存分配(经 `new``malloc()`)。
使用者可以通过 `MemoryPoolAllocator::Size()` 查询当前已分的内存大小。那么使用者可以拟定使用者缓冲区的合适大小。

View File

@ -0,0 +1,146 @@
# Encoding
According to [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf),
> (in Introduction) JSON text is a sequence of Unicode code points.
The earlier [RFC4627](http://www.ietf.org/rfc/rfc4627.txt) stated that,
> (in §3) JSON text SHALL be encoded in Unicode. The default encoding is 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.
RapidJSON supports various encodings. It can also validate the encodings of JSON, and transconding JSON among encodings. All these features are implemented internally, without the need for external libraries (e.g. [ICU](http://site.icu-project.org/)).
[TOC]
# Unicode {#Unicode}
From [Unicode's official website](http://www.unicode.org/standard/WhatIsUnicode.html):
> Unicode provides a unique number for every character,
> no matter what the platform,
> no matter what the program,
> no matter what the language.
Those unique numbers are called code points, which is in the range `0x0` to `0x10FFFF`.
## Unicode Transformation Format {#UTF}
There are various encodings for storing Unicode code points. These are called Unicode Transformation Format (UTF). RapidJSON supports the most commonly used UTFs, including
* UTF-8: 8-bit variable-width encoding. It maps a code point to 14 bytes.
* UTF-16: 16-bit variable-width encoding. It maps a code point to 12 16-bit code units (i.e., 24 bytes).
* UTF-32: 32-bit fixed-width encoding. It directly maps a code point to a single 32-bit code unit (i.e. 4 bytes).
For UTF-16 and UTF-32, the byte order (endianness) does matter. Within computer memory, they are often stored in the computer's endianness. However, when it is stored in file or transferred over network, we need to state the byte order of the byte sequence, either little-endian (LE) or big-endian (BE).
RapidJSON provide these encodings via the structs in `rapidjson/encodings.h`:
~~~~~~~~~~cpp
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
~~~~~~~~~~
For processing text in memory, we normally use `UTF8`, `UTF16` or `UTF32`. For processing text via I/O, we may use `UTF8`, `UTF16LE`, `UTF16BE`, `UTF32LE` or `UTF32BE`.
When using the DOM-style API, the `Encoding` template parameter in `GenericValue<Encoding>` and `GenericDocument<Encoding>` indicates the encoding to be used to represent JSON string in memory. So normally we will use `UTF8`, `UTF16` or `UTF32` for this template parameter. The choice depends on operating systems and other libraries that the application is using. For example, Windows API represents Unicode characters in UTF-16, while most Linux distributions and applications prefer UTF-8.
Example of UTF-16 DOM declaration:
~~~~~~~~~~cpp
typedef GenericDocument<UTF16<> > WDocument;
typedef GenericValue<UTF16<> > WValue;
~~~~~~~~~~
For a detail example, please check the example in [DOM's Encoding](doc/stream.md) section.
## Character Type {#CharacterType}
As shown in the declaration, each encoding has a `CharType` template parameter. Actually, it may be a little bit confusing, but each `CharType` stores a code unit, not a character (code point). As mentioned in previous section, a code point may be encoded to 14 code units for UTF-8.
For `UTF16(LE|BE)`, `UTF32(LE|BE)`, the `CharType` must be integer type of at least 2 and 4 bytes respectively.
Note that C++11 introduces `char16_t` and `char32_t`, which can be used for `UTF16` and `UTF32` respectively.
## AutoUTF {#AutoUTF}
Previous encodings are statically bound in compile-time. In other words, user must know exactly which encodings will be used in the memory or streams. However, sometimes we may need to read/write files of different encodings. The encoding needed to be decided in runtime.
`AutoUTF` is an encoding designed for this purpose. It chooses which encoding to be used according to the input or output stream. Currently, it should be used with `EncodedInputStream` and `EncodedOutputStream`.
## ASCII {#ASCII}
Although the JSON standards did not mention about [ASCII](http://en.wikipedia.org/wiki/ASCII), sometimes we would like to write 7-bit ASCII JSON for applications that cannot handle UTF-8. Since any JSON can represent unicode characters in escaped sequence `\uXXXX`, JSON can always be encoded in ASCII.
Here is an example for writing a UTF-8 DOM into ASCII:
~~~~~~~~~~cpp
using namespace rapidjson;
Document d; // UTF8<>
// ...
StringBuffer buffer;
Writer<StringBuffer, Document::EncodingType, ASCII<> > writer(buffer);
d.Accept(writer);
std::cout << buffer.GetString();
~~~~~~~~~~
ASCII can be used in input stream. If the input stream contains bytes with values above 127, it will cause `kParseErrorStringInvalidEncoding` error.
ASCII *cannot* be used in memory (encoding of `Document` or target encoding of `Reader`), as it cannot represent Unicode code points.
# Validation & Transcoding {#ValidationTranscoding}
When RapidJSON parses a JSON, it can validate the input JSON, whether it is a valid sequence of a specified encoding. This option can be turned on by adding `kParseValidateEncodingFlag` in `parseFlags` template parameter.
If the input encoding and output encoding is different, `Reader` and `Writer` will automatically transcode (convert) the text. In this case, `kParseValidateEncodingFlag` is not necessary, as it must decode the input sequence. And if the sequence was unable to be decoded, it must be invalid.
## Transcoder {#Transcoder}
Although the encoding functions in RapidJSON are designed for JSON parsing/generation, user may abuse them for transcoding of non-JSON strings.
Here is an example for transcoding a string from UTF-8 to UTF-16:
~~~~~~~~~~cpp
#include "rapidjson/encodings.h"
using namespace rapidjson;
const char* s = "..."; // UTF-8 string
StringStream source(s);
GenericStringBuffer<UTF16<> > target;
bool hasError = false;
while (source.Peek() != '\0')
if (!Transcoder<UTF8<>, UTF16<> >::Transcode(source, target)) {
hasError = true;
break;
}
if (!hasError) {
const wchar_t* t = target.GetString();
// ...
}
~~~~~~~~~~
You may also use `AutoUTF` and the associated streams for setting source/target encoding in runtime.

View File

@ -0,0 +1,152 @@
# 编码
根据 [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)
> (in Introduction) JSON text is a sequence of Unicode code points.
>
> 翻译JSON 文本是 Unicode 码点的序列。
较早的 [RFC4627](http://www.ietf.org/rfc/rfc4627.txt) 申明:
> (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](http://site.icu-project.org/))。
[TOC]
# Unicode {#Unicode}
根据 [Unicode 的官方网站](http://www.unicode.org/standard/translations/t-chinese.html)
>Unicode 给每个字符提供了一个唯一的数字,
不论是什么平台、
不论是什么程序、
不论是什么语言。
这些唯一数字称为码点code point其范围介乎 `0x0``0x10FFFF` 之间。
## Unicode 转换格式 {#UTF}
存储 Unicode 码点有多种编码方式。这些称为 Unicode 转换格式Unicode Transformation Format, UTF。RapidJSON 支持最常用的 UTF包括
* UTF-88 位可变长度编码。它把一个码点映射至 1 至 4 个字节。
* UTF-1616 位可变长度编码。它把一个码点映射至 1 至 2 个 16 位编码单元(即 2 至 4 个字节)。
* UTF-3232 位固定长度编码。它直接把码点映射至单个 32 位编码单元(即 4 字节)。
对于 UTF-16 及 UTF-32 来说字节序endianness是有影响的。在内存中它们通常都是以该计算机的字节序来存储。然而当要储存在文件中或在网上传输我们需要指明字节序列的字节序是小端little endian, LE还是大端big-endian, BE
RapidJSON 通过 `rapidjson/encodings.h` 中的 struct 去提供各种编码:
~~~~~~~~~~cpp
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
~~~~~~~~~~
对于在内存中的文本,我们正常会使用 `UTF8``UTF16``UTF32`。对于处理经过 I/O 的文本,我们可使用 `UTF8``UTF16LE``UTF16BE``UTF32LE``UTF32BE`
当使用 DOM 风格的 API`GenericValue<Encoding>``GenericDocument<Encoding>` 里的 `Encoding` 模板参数是用于指明内存中存储的 JSON 字符串使用哪种编码。因此通常我们会在此参数中使用 `UTF8``UTF16``UTF32`。如何选择视乎应用软件所使用的操作系统及其他程序库。例如Windows API 使用 UTF-16 表示 Unicode 字符,而多数的 Linux 发行版本及应用软件则更喜欢 UTF-8。
使用 UTF-16 的 DOM 声明例子:
~~~~~~~~~~cpp
typedef GenericDocument<UTF16<> > WDocument;
typedef GenericValue<UTF16<> > WValue;
~~~~~~~~~~
可以在 [DOM's Encoding](doc/stream.zh-cn.md) 一节看到更详细的使用例子。
## 字符类型 {#CharacterType}
从之前的声明中可以看到,每个编码都有一个 `CharType` 模板参数。这可能比较容易混淆,实际上,每个 `CharType` 存储一个编码单元,而不是一个字符(码点)。如之前所谈及,在 UTF-8 中一个码点可能会编码成 1 至 4 个编码单元。
对于 `UTF16(LE|BE)``UTF32(LE|BE)` 来说,`CharType` 必须分别是一个至少 2 及 4 字节的整数类型。
注意 C++11 新添了 `char16_t``char32_t` 类型,也可分别用于 `UTF16``UTF32`
## AutoUTF {#AutoUTF}
上述所介绍的编码都是在编译期静态挷定的。换句话说,使用者必须知道内存或流之中使用了哪种编码。然而,有时候我们可能需要读写不同编码的文件,而且这些编码需要在运行时才能决定。
`AutoUTF` 是为此而设计的编码。它根据输入或输出流来选择使用哪种编码。目前它应该与 `EncodedInputStream``EncodedOutputStream` 结合使用。
## ASCII {#ASCII}
虽然 JSON 标准并未提及 [ASCII](http://en.wikipedia.org/wiki/ASCII),有时候我们希望写入 7 位的 ASCII JSON以供未能处理 UTF-8 的应用程序使用。由于任 JSON 都可以把 Unicode 字符表示为 `\uXXXX` 转义序列JSON 总是可用 ASCII 来编码。
以下的例子把 UTF-8 的 DOM 写成 ASCII 的 JSON
~~~~~~~~~~cpp
using namespace rapidjson;
Document d; // UTF8<>
// ...
StringBuffer buffer;
Writer<StringBuffer, Document::EncodingType, ASCII<> > writer(buffer);
d.Accept(writer);
std::cout << buffer.GetString();
~~~~~~~~~~
ASCII 可用于输入流。当输入流包含大于 127 的字节,就会导致 `kParseErrorStringInvalidEncoding` 错误。
ASCII * 不能 * 用于内存(`Document` 的编码,或 `Reader` 的目标编码),因为它不能表示 Unicode 码点。
# 校验及转码 {#ValidationTranscoding}
当 RapidJSON 解析一个 JSON 时,它能校验输入 JSON判断它是否所标明编码的合法序列。要开启此选项请把 `kParseValidateEncodingFlag` 加入 `parseFlags` 模板参数。
若输入编码和输出编码并不相同,`Reader``Writer` 会算把文本转码。在这种情况下,并不需要 `kParseValidateEncodingFlag`,因为它必须解码输入序列。若序列不能被解码,它必然是不合法的。
## 转码器 {#Transcoder}
虽然 RapidJSON 的编码功能是为 JSON 解析/生成而设计,使用者也可以“滥用”它们来为非 JSON 字符串转码。
以下的例子把 UTF-8 字符串转码成 UTF-16
~~~~~~~~~~cpp
#include "rapidjson/encodings.h"
using namespace rapidjson;
const char* s = "..."; // UTF-8 string
StringStream source(s);
GenericStringBuffer<UTF16<> > target;
bool hasError = false;
while (source.Peek() != '\0')
if (!Transcoder<UTF8<>, UTF16<> >::Transcode(source, target)) {
hasError = true;
break;
}
if (!hasError) {
const wchar_t* t = target.GetString();
// ...
}
~~~~~~~~~~
你也可以用 `AutoUTF` 及对应的流来在运行时设置内源/目的之编码。

289
thirdparty/rapidjson-1.1.0/doc/faq.md vendored Normal file
View File

@ -0,0 +1,289 @@
# FAQ
[TOC]
## General
1. What is RapidJSON?
RapidJSON is a C++ library for parsing and generating JSON. You may check all [features](doc/features.md) of it.
2. Why is RapidJSON named so?
It is inspired by [RapidXML](http://rapidxml.sourceforge.net/), which is a fast XML DOM parser.
3. Is RapidJSON similar to RapidXML?
RapidJSON borrowed some designs of RapidXML, including *in situ* parsing, header-only library. But the two APIs are completely different. Also RapidJSON provide many features that are not in RapidXML.
4. Is RapidJSON free?
Yes, it is free under MIT license. It can be used in commercial applications. Please check the details in [license.txt](https://github.com/miloyip/rapidjson/blob/master/license.txt).
5. Is RapidJSON small? What are its dependencies?
Yes. A simple executable which parses a JSON and prints its statistics is less than 30KB on Windows.
RapidJSON depends on C++ standard library only.
6. How to install RapidJSON?
Check [Installation section](https://miloyip.github.io/rapidjson/).
7. Can RapidJSON run on my platform?
RapidJSON has been tested in many combinations of operating systems, compilers and CPU architecture by the community. But we cannot ensure that it can be run on your particular platform. Building and running the unit test suite will give you the answer.
8. Does RapidJSON support C++03? C++11?
RapidJSON was firstly implemented for C++03. Later it added optional support of some C++11 features (e.g., move constructor, `noexcept`). RapidJSON shall be compatible with C++03 or C++11 compliant compilers.
9. Does RapidJSON really work in real applications?
Yes. It is deployed in both client and server real applications. A community member reported that RapidJSON in their system parses 50 million JSONs daily.
10. How RapidJSON is tested?
RapidJSON contains a unit test suite for automatic testing. [Travis](https://travis-ci.org/miloyip/rapidjson/)(for Linux) and [AppVeyor](https://ci.appveyor.com/project/miloyip/rapidjson/)(for Windows) will compile and run the unit test suite for all modifications. The test process also uses Valgrind (in Linux) to detect memory leaks.
11. Is RapidJSON well documented?
RapidJSON provides user guide and API documentationn.
12. Are there alternatives?
Yes, there are a lot alternatives. For example, [nativejson-benchmark](https://github.com/miloyip/nativejson-benchmark) has a listing of open-source C/C++ JSON libraries. [json.org](http://www.json.org/) also has a list.
## JSON
1. What is JSON?
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It uses human readable text format. More details of JSON can be referred to [RFC7159](http://www.ietf.org/rfc/rfc7159.txt) and [ECMA-404](http://www.ecma-international.org/publications/standards/Ecma-404.htm).
2. What are applications of JSON?
JSON are commonly used in web applications for transferring structured data. It is also used as a file format for data persistence.
2. Does RapidJSON conform to the JSON standard?
Yes. RapidJSON is fully compliance with [RFC7159](http://www.ietf.org/rfc/rfc7159.txt) and [ECMA-404](http://www.ecma-international.org/publications/standards/Ecma-404.htm). It can handle corner cases, such as supporting null character and surrogate pairs in JSON strings.
3. Does RapidJSON support relaxed syntax?
Currently no. RapidJSON only support the strict standardized format. Support on related syntax is under discussion in this [issue](https://github.com/miloyip/rapidjson/issues/36).
## DOM and SAX
1. What is DOM style API?
Document Object Model (DOM) is an in-memory representation of JSON for query and manipulation.
2. What is SAX style API?
SAX is an event-driven API for parsing and generation.
3. Should I choose DOM or SAX?
DOM is easy for query and manipulation. SAX is very fast and memory-saving but often more difficult to be applied.
4. What is *in situ* parsing?
*in situ* parsing decodes the JSON strings directly into the input JSON. This is an optimization which can reduce memory consumption and improve performance, but the input JSON will be modified. Check [in-situ parsing](doc/dom.md) for details.
5. When does parsing generate an error?
The parser generates an error when the input JSON contains invalid syntax, or a value can not be represented (a number is too big), or the handler of parsers terminate the parsing. Check [parse error](doc/dom.md) for details.
6. What error information is provided?
The error is stored in `ParseResult`, which includes the error code and offset (number of characters from the beginning of JSON). The error code can be translated into human-readable error message.
7. Why not just using `double` to represent JSON number?
Some applications use 64-bit unsigned/signed integers. And these integers cannot be converted into `double` without loss of precision. So the parsers detects whether a JSON number is convertible to different types of integers and/or `double`.
8. How to clear-and-minimize a document or value?
Call one of the `SetXXX()` methods - they call destructor which deallocates DOM data:
~~~~~~~~~~cpp
Document d;
...
d.SetObject(); // clear and minimize
~~~~~~~~~~
Alternatively, use equivalent of the [C++ swap with temporary idiom](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Clear-and-minimize):
~~~~~~~~~~cpp
Value(kObjectType).Swap(d);
~~~~~~~~~~
or equivalent, but sightly longer to type:
~~~~~~~~~~cpp
d.Swap(Value(kObjectType).Move());
~~~~~~~~~~
9. How to insert a document node into another document?
Let's take the following two DOM trees represented as JSON documents:
~~~~~~~~~~cpp
Document person;
person.Parse("{\"person\":{\"name\":{\"first\":\"Adam\",\"last\":\"Thomas\"}}}");
Document address;
address.Parse("{\"address\":{\"city\":\"Moscow\",\"street\":\"Quiet\"}}");
~~~~~~~~~~
Let's assume we want to merge them in such way that the whole `address` document becomes a node of the `person`:
~~~~~~~~~~js
{ "person": {
"name": { "first": "Adam", "last": "Thomas" },
"address": { "city": "Moscow", "street": "Quiet" }
}
}
~~~~~~~~~~
The most important requirement to take care of document and value life-cycle as well as consistent memory managent using the right allocator during the value transfer.
Simple yet most efficient way to achieve that is to modify the `address` definition above to initialize it with allocator of the `person` document, then we just add the root member of the value:
~~~~~~~~~~cpp
Documnet address(person.GetAllocator());
...
person["person"].AddMember("address", address["address"], person.GetAllocator());
~~~~~~~~~~
Alternatively, if we don't want to explicitly refer to the root value of `address` by name, we can refer to it via iterator:
~~~~~~~~~~cpp
auto addressRoot = address.MemberBegin();
person["person"].AddMember(addressRoot->name, addressRoot->value, person.GetAllocator());
~~~~~~~~~~
Second way is to deep-clone the value from the address document:
~~~~~~~~~~cpp
Value addressValue = Value(address["address"], person.GetAllocator());
person["person"].AddMember("address", addressValue, person.GetAllocator());
~~~~~~~~~~
## Document/Value (DOM)
1. What is move semantics? Why?
Instead of copy semantics, move semantics is used in `Value`. That means, when assigning a source value to a target value, the ownership of source value is moved to the target value.
Since moving is faster than copying, this design decision forces user to aware of the copying overhead.
2. How to copy a value?
There are two APIs: constructor with allocator, and `CopyFrom()`. See [Deep Copy Value](doc/tutorial.md) for an example.
3. Why do I need to provide the length of string?
Since C string is null-terminated, the length of string needs to be computed via `strlen()`, with linear runtime complexity. This incurs an unncessary overhead of many operations, if the user already knows the length of string.
Also, RapidJSON can handle `\u0000` (null character) within a string. If a string contains null characters, `strlen()` cannot return the true length of it. In such case user must provide the length of string explicitly.
4. Why do I need to provide allocator parameter in many DOM manipulation API?
Since the APIs are member functions of `Value`, we do not want to save an allocator pointer in every `Value`.
5. Does it convert between numerical types?
When using `GetInt()`, `GetUint()`, ... conversion may occur. For integer-to-integer conversion, it only convert when it is safe (otherwise it will assert). However, when converting a 64-bit signed/unsigned integer to double, it will convert but be aware that it may lose precision. A number with fraction, or an integer larger than 64-bit, can only be obtained by `GetDouble()`.
## Reader/Writer (SAX)
1. Why don't we just `printf` a JSON? Why do we need a `Writer`?
Most importantly, `Writer` will ensure the output JSON is well-formed. Calling SAX events incorrectly (e.g. `StartObject()` pairing with `EndArray()`) will assert. Besides, `Writer` will escapes strings (e.g., `\n`). Finally, the numeric output of `printf()` may not be a valid JSON number, especially in some locale with digit delimiters. And the number-to-string conversion in `Writer` is implemented with very fast algorithms, which outperforms than `printf()` or `iostream`.
2. Can I pause the parsing process and resume it later?
This is not directly supported in the current version due to performance consideration. However, if the execution environment supports multi-threading, user can parse a JSON in a separate thread, and pause it by blocking in the input stream.
## Unicode
1. Does it support UTF-8, UTF-16 and other format?
Yes. It fully support UTF-8, UTF-16 (LE/BE), UTF-32 (LE/BE) and ASCII.
2. Can it validate the encoding?
Yes, just pass `kParseValidateEncodingFlag` to `Parse()`. If there is invalid encoding in the stream, it wil generate `kParseErrorStringInvalidEncoding` error.
3. What is surrogate pair? Does RapidJSON support it?
JSON uses UTF-16 encoding when escaping unicode character, e.g. `\u5927` representing Chinese character "big". To handle characters other than those in basic multilingual plane (BMP), UTF-16 encodes those characters with two 16-bit values, which is called UTF-16 surrogate pair. For example, the Emoji character U+1F602 can be encoded as `\uD83D\uDE02` in JSON.
RapidJSON fully support parsing/generating UTF-16 surrogates.
4. Can it handle `\u0000` (null character) in JSON string?
Yes. RapidJSON fully support null character in JSON string. However, user need to be aware of it and using `GetStringLength()` and related APIs to obtain the true length of string.
5. Can I output `\uxxxx` for all non-ASCII character?
Yes, use `ASCII<>` as output encoding template parameter in `Writer` can enforce escaping those characters.
## Stream
1. I have a big JSON file. Should I load the whole file to memory?
User can use `FileReadStream` to read the file chunk-by-chunk. But for *in situ* parsing, the whole file must be loaded.
2. Can I parse JSON while it is streamed from network?
Yes. User can implement a custom stream for this. Please refer to the implementation of `FileReadStream`.
3. I don't know what encoding will the JSON be. How to handle them?
You may use `AutoUTFInputStream` which detects the encoding of input stream automatically. However, it will incur some performance overhead.
4. What is BOM? How RapidJSON handle it?
[Byte order mark (BOM)](http://en.wikipedia.org/wiki/Byte_order_mark) sometimes reside at the beginning of file/stream to indiciate the UTF encoding type of it.
RapidJSON's `EncodedInputStream` can detect/consume BOM. `EncodedOutputStream` can optionally write a BOM. See [Encoded Streams](doc/stream.md) for example.
5. Why little/big endian is related?
little/big endian of stream is an issue for UTF-16 and UTF-32 streams, but not UTF-8 stream.
## Performance
1. Is RapidJSON really fast?
Yes. It may be the fastest open source JSON library. There is a [benchmark](https://github.com/miloyip/nativejson-benchmark) for evaluating performance of C/C++ JSON libaries.
2. Why is it fast?
Many design decisions of RapidJSON is aimed at time/space performance. These may reduce user-friendliness of APIs. Besides, it also employs low-level optimizations (intrinsics, SIMD) and special algorithms (custom double-to-string, string-to-double conversions).
3. What is SIMD? How it is applied in RapidJSON?
[SIMD](http://en.wikipedia.org/wiki/SIMD) instructions can perform parallel computation in modern CPUs. RapidJSON support Intel's SSE2/SSE4.2 to accelerate whitespace skipping. This improves performance of parsing indent formatted JSON. Define `RAPIDJSON_SSE2` or `RAPIDJSON_SSE42` macro to enable this feature. However, running the executable on a machine without such instruction set support will make it crash.
4. Does it consume a lot of memory?
The design of RapidJSON aims at reducing memory footprint.
In the SAX API, `Reader` consumes memory portional to maximum depth of JSON tree, plus maximum length of JSON string.
In the DOM API, each `Value` consumes exactly 16/24 bytes for 32/64-bit architecture respectively. RapidJSON also uses a special memory allocator to minimize overhead of allocations.
5. What is the purpose of being high performance?
Some applications need to process very large JSON files. Some server-side applications need to process huge amount of JSONs. Being high performance can improve both latency and throuput. In a broad sense, it will also save energy.
## Gossip
1. Who are the developers of RapidJSON?
Milo Yip ([miloyip](https://github.com/miloyip)) is the original author of RapidJSON. Many contributors from the world have improved RapidJSON. Philipp A. Hartmann ([pah](https://github.com/pah)) has implemented a lot of improvements, setting up automatic testing and also involves in a lot of discussions for the community. Don Ding ([thebusytypist](https://github.com/thebusytypist)) implemented the iterative parser. Andrii Senkovych ([jollyroger](https://github.com/jollyroger)) completed the CMake migration. Kosta ([Kosta-Github](https://github.com/Kosta-Github)) provided a very neat short-string optimization. Thank you for all other contributors and community members as well.
2. Why do you develop RapidJSON?
It was just a hobby project initially in 2011. Milo Yip is a game programmer and he just knew about JSON at that time and would like to apply JSON in future projects. As JSON seems very simple he would like to write a header-only and fast library.
3. Why there is a long empty period of development?
It is basically due to personal issues, such as getting new family members. Also, Milo Yip has spent a lot of spare time on translating "Game Engine Architecture" by Jason Gregory into Chinese.
4. Why did the repository move from Google Code to GitHub?
This is the trend. And GitHub is much more powerful and convenient.

View File

@ -0,0 +1,290 @@
# 常见问题
[TOC]
## 一般问题
1. RapidJSON 是什么?
RapidJSON 是一个 C++ 库,用于解析及生成 JSON。读者可参考它的所有 [特点](doc/features.zh-cn.md)。
2. 为什么称作 RapidJSON
它的灵感来自于 [RapidXML](http://rapidxml.sourceforge.net/)RapidXML 是一个高速的 XML DOM 解析器。
3. RapidJSON 与 RapidXML 相似么?
RapidJSON 借镜了 RapidXML 的一些设计, 包括原位(*in situ*)解析、只有头文件的库。但两者的 API 是完全不同的。此外 RapidJSON 也提供许多 RapidXML 没有的特点。
4. RapidJSON 是免费的么?
是的,它在 MIT 特許條款下免费。它可用于商业软件。详情请参看 [license.txt](https://github.com/miloyip/rapidjson/blob/master/license.txt)。
5. RapidJSON 很小么?它有何依赖?
是的。在 Windows 上,一个解析 JSON 并打印出统计的可执行文件少于 30KB。
RapidJSON 仅依赖于 C++ 标准库。
6. 怎样安装 RapidJSON
见 [安装一节](../readme.zh-cn.md#安装)。
7. RapidJSON 能否运行于我的平台?
社区已在多个操作系统编译器CPU 架构的组合上测试 RapidJSON。但我们无法确保它能运行于你特定的平台上。只需要生成及执行单元测试便能获取答案。
8. RapidJSON 支持 C++03 么C++11 呢?
RapidJSON 开始时在 C++03 上实现。后来加入了可选的 C++11 特性支持(如转移构造函数、`noexcept`。RapidJSON 应该兼容所有遵从 C++03 或 C++11 的编译器。
9. RapidJSON 是否真的用于实际应用?
是的。它被配置于前台及后台的真实应用中。一个社区成员说 RapidJSON 在他们的系统中每日解析 5 千万个 JSON。
10. RapidJSON 是如何被测试的?
RapidJSON 包含一组单元测试去执行自动测试。[Travis](https://travis-ci.org/miloyip/rapidjson/)(供 Linux 平台)及 [AppVeyor](https://ci.appveyor.com/project/miloyip/rapidjson/)(供 Windows 平台)会对所有修改进行编译及执行单元测试。在 Linux 下还会使用 Valgrind 去检测内存泄漏。
11. RapidJSON 是否有完整的文档?
RapidJSON 提供了使用手册及 API 说明文档。
12. 有没有其他替代品?
有许多替代品。例如 [nativejson-benchmark](https://github.com/miloyip/nativejson-benchmark) 列出了一些开源的 C/C++ JSON 库。[json.org](http://www.json.org/) 也有一个列表。
## JSON
1. 什么是 JSON
JSON (JavaScript Object Notation) 是一个轻量的数据交换格式。它使用人类可读的文本格式。更多关于 JSON 的细节可考 [RFC7159](http://www.ietf.org/rfc/rfc7159.txt) 及 [ECMA-404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)。
2. JSON 有什么应用场合?
JSON 常用于网页应用程序,以传送结构化数据。它也可作为文件格式用于数据持久化。
2. RapidJSON 是否符合 JSON 标准?
是。RapidJSON 完全符合 [RFC7159](http://www.ietf.org/rfc/rfc7159.txt) 及 [ECMA-404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)。它能处理一些特殊情况,例如支持 JSON 字符串中含有空字符及代理对surrogate pair
3. RapidJSON 是否支持宽松的语法?
现时不支持。RapidJSON 只支持严格的标准格式。宽松语法现时在这 [issue](https://github.com/miloyip/rapidjson/issues/36) 中进行讨论。
## DOM 与 SAX
1. 什么是 DOM 风格 API
Document Object ModelDOM是一个储存于内存的 JSON 表示方式,用于查询及修改 JSON。
2. 什么是 SAX 风格 API?
SAX 是一个事件驱动的 API用于解析及生成 JSON。
3. 我应用 DOM 还是 SAX
DOM 易于查询及修改。SAX 则是非常快及省内存的,但通常较难使用。
4. 什么是原位(*in situ*)解析?
原位解析会把 JSON 字符串直接解码至输入的 JSON 中。这是一个优化,可减少内存消耗及提升性能,但输入的 JSON 会被更改。进一步细节请参考 [原位解析](doc/dom.zh-cn.md) 。
5. 什么时候会产生解析错误?
当输入的 JSON 包含非法语法,或不能表示一个值(如 Number 太大),或解析器的处理器中断解析过程,解析器都会产生一个错误。详情请参考 [解析错误](doc/dom.zh-cn.md)。
6. 有什么错误信息?
错误信息存储在 `ParseResult`,它包含错误代号及偏移值(从 JSON 开始至错误处的字符数目)。可以把错误代号翻译为人类可读的错误讯息。
7. 为何不只使用 `double` 去表示 JSON number
一些应用需要使用 64 位无号/有号整数。这些整数不能无损地转换成 `double`。因此解析器会检测一个 JSON number 是否能转换至各种整数类型及 `double`
8. 如何清空并最小化 `document``value` 的容量?
调用 `SetXXX()` 方法 - 这些方法会调用析构函数,并重建空的 Object 或 Array:
~~~~~~~~~~cpp
Document d;
...
d.SetObject(); // clear and minimize
~~~~~~~~~~
另外,也可以参考在 [C++ swap with temporary idiom](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Clear-and-minimize) 中的一种等价的方法:
~~~~~~~~~~cpp
Value(kObjectType).Swap(d);
~~~~~~~~~~
或者,使用这个稍微长一点的代码也能完成同样的事情:
~~~~~~~~~~cpp
d.Swap(Value(kObjectType).Move());
~~~~~~~~~~
9. 如何将一个 `document` 节点插入到另一个 `document` 中?
比如有以下两个 document(DOM):
~~~~~~~~~~cpp
Document person;
person.Parse("{\"person\":{\"name\":{\"first\":\"Adam\",\"last\":\"Thomas\"}}}");
Document address;
address.Parse("{\"address\":{\"city\":\"Moscow\",\"street\":\"Quiet\"}}");
~~~~~~~~~~
假设我们希望将整个 `address` 插入到 `person` 中,作为其的一个子节点:
~~~~~~~~~~js
{ "person": {
"name": { "first": "Adam", "last": "Thomas" },
"address": { "city": "Moscow", "street": "Quiet" }
}
}
~~~~~~~~~~
在插入节点的过程中需要注意 `document``value` 的生命周期并且正确地使用 allocator 进行内存分配和管理。
一个简单有效的方法就是修改上述 `address` 变量的定义,让其使用 `person` 的 allocator 初始化,然后将其添加到根节点。
~~~~~~~~~~cpp
Documnet address(person.GetAllocator());
...
person["person"].AddMember("address", address["address"], person.GetAllocator());
~~~~~~~~~~
当然,如果你不想通过显式地写出 `address` 的 key 来得到其值,可以使用迭代器来实现:
~~~~~~~~~~cpp
auto addressRoot = address.MemberBegin();
person["person"].AddMember(addressRoot->name, addressRoot->value, person.GetAllocator());
~~~~~~~~~~
此外,还可以通过深拷贝 address document 来实现:
~~~~~~~~~~cpp
Value addressValue = Value(address["address"], person.GetAllocator());
person["person"].AddMember("address", addressValue, person.GetAllocator());
~~~~~~~~~~
## Document/Value (DOM)
1. 什么是转移语意?为什么?
`Value` 不用复制语意,而使用了转移语意。这是指,当把来源值赋值于目标值时,来源值的所有权会转移至目标值。
由于转移快于复制,此设计决定强迫使用者注意到复制的消耗。
2. 怎样去复制一个值?
有两个 API 可用:含 allocator 的构造函数,以及 `CopyFrom()`。可参考 [深复制 Value](doc/tutorial.zh-cn.md) 里的用例。
3. 为什么我需要提供字符串的长度?
由于 C 字符串是空字符结尾的,需要使用 `strlen()` 去计算其长度,这是线性复杂度的操作。若使用者已知字符串的长度,对很多操作来说会造成不必要的消耗。
此外RapidJSON 可处理含有 `\u0000`(空字符)的字符串。若一个字符串含有空字符,`strlen()` 便不能返回真正的字符串长度。在这种情况下使用者必须明确地提供字符串长度。
4. 为什么在许多 DOM 操作 API 中要提供分配器作为参数?
由于这些 API 是 `Value` 的成员函数,我们不希望为每个 `Value` 储存一个分配器指针。
5. 它会转换各种数值类型么?
当使用 `GetInt()``GetUint()` 等 API 时,可能会发生转换。对于整数至整数转换,仅当保证转换安全才会转换(否则会断言失败)。然而,当把一个 64 位有号/无号整数转换至 double 时,它会转换,但有可能会损失精度。含有小数的数字、或大于 64 位的整数,都只能使用 `GetDouble()` 获取其值。
## Reader/Writer (SAX)
1. 为什么不仅仅用 `printf` 输出一个 JSON为什么需要 `Writer`
最重要的是,`Writer` 能确保输出的 JSON 是格式正确的。错误地调用 SAX 事件(如 `StartObject()` 错配 `EndArray()`)会造成断言失败。此外,`Writer` 会把字符串进行转义(如 `\n`)。最后,`printf()` 的数值输出可能并不是一个合法的 JSON number特别是某些 locale 会有数字分隔符。而且 `Writer` 的数值字符串转换是使用非常快的算法来实现的,胜过 `printf()``iostream`
2. 我能否暂停解析过程,并在稍后继续?
基于性能考虑,目前版本并不直接支持此功能。然而,若执行环境支持多线程,使用者可以在另一线程解析 JSON并通过阻塞输入流去暂停。
## Unicode
1. 它是否支持 UTF-8、UTF-16 及其他格式?
是。它完全支持 UTF-8、UTF-16大端小端、UTF-32大端小端及 ASCII。
2. 它能否检测编码的合法性?
能。只需把 `kParseValidateEncodingFlag` 参考传给 `Parse()`。若发现在输入流中有非法的编码,它就会产生 `kParseErrorStringInvalidEncoding` 错误。
3. 什么是代理对surrogate pair)RapidJSON 是否支持?
JSON 使用 UTF-16 编码去转义 Unicode 字符,例如 `\u5927` 表示中文字“大”。要处理基本多文种平面basic multilingual planeBMP以外的字符时UTF-16 会把那些字符编码成两个 16 位值,这称为 UTF-16 代理对。例如,绘文字字符 U+1F602 在 JSON 中可被编码成 `\uD83D\uDE02`
RapidJSON 完全支持解析及生成 UTF-16 代理对。
4. 它能否处理 JSON 字符串中的 `\u0000`(空字符)?
能。RapidJSON 完全支持 JSON 字符串中的空字符。然而,使用者需要注意到这件事,并使用 `GetStringLength()` 及相关 API 去取得字符串真正长度。
5. 能否对所有非 ASCII 字符输出成 `\uxxxx` 形式?
可以。只要在 `Writer` 中使用 `ASCII<>` 作为输出编码参数,就可以强逼转义那些字符。
## 流
1. 我有一个很大的 JSON 文件。我应否把它整个载入内存中?
使用者可使用 `FileReadStream` 去逐块读入文件。但若使用于原位解析,必须载入整个文件。
2. 我能否解析一个从网络上串流进来的 JSON
可以。使用者可根据 `FileReadStream` 的实现,去实现一个自定义的流。
3. 我不知道一些 JSON 将会使用哪种编码。怎样处理它们?
你可以使用 `AutoUTFInputStream`,它能自动检测输入流的编码。然而,它会带来一些性能开销。
4. 什么是 BOMRapidJSON 怎样处理它?
[字节顺序标记byte order mark, BOM](http://en.wikipedia.org/wiki/Byte_order_mark) 有时会出现于文件/流的开始,以表示其 UTF 编码类型。
RapidJSON 的 `EncodedInputStream` 可检测/跳过 BOM。`EncodedOutputStream` 可选择是否写入 BOM。可参考 [编码流](doc/stream.zh-cn.md) 中的例子。
5. 为什么会涉及大端/小端?
流的大端/小端是 UTF-16 及 UTF-32 流要处理的问题,而 UTF-8 不需要处理。
## 性能
1. RapidJSON 是否真的快?
是。它可能是最快的开源 JSON 库。有一个 [评测](https://github.com/miloyip/nativejson-benchmark) 评估 C/C++ JSON 库的性能。
2. 为什么它会快?
RapidJSON 的许多设计是针对时间/空间性能来设计的,这些决定可能会影响 API 的易用性。此外它也使用了许多底层优化内部函数intrinsic、SIMD及特别的算法自定义的 double 至字符串转换、字符串至 double 的转换)。
3. 什是是 SIMD它如何用于 RapidJSON
[SIMD](http://en.wikipedia.org/wiki/SIMD) 指令可以在现代 CPU 中执行并行运算。RapidJSON 支持了 Intel 的 SSE2/SSE4.2 去加速跳过空白字符。在解析含缩进的 JSON 时,这能提升性能。只要定义名为 `RAPIDJSON_SSE2``RAPIDJSON_SSE42` 的宏,就能启动这个功能。然而,若在不支持这些指令集的机器上执行这些可执行文件,会导致崩溃。
4. 它会消耗许多内存么?
RapidJSON 的设计目标是减低内存占用。
在 SAX API 中,`Reader` 消耗的内存与 JSON 树深度加上最长 JSON 字符成正比。
在 DOM API 中,每个 `Value` 在 32/64 位架构下分别消耗 16/24 字节。RapidJSON 也使用一个特殊的内存分配器去减少分配的额外开销。
5. 高性能的意义何在?
有些应用程序需要处理非常大的 JSON 文件。而有些后台应用程序需要处理大量的 JSON。达到高性能同时改善延时及吞吐量。更广义来说这也可以节省能源。
## 八挂
1. 谁是 RapidJSON 的开发者?
叶劲峰Milo Yip[miloyip](https://github.com/miloyip))是 RapidJSON 的原作者。全世界许多贡献者一直在改善 RapidJSON。Philipp A. Hartmann[pah](https://github.com/pah)实现了许多改进也设置了自动化测试而且还参与许多社区讨论。丁欧南Don Ding[thebusytypist](https://github.com/thebusytypist)实现了迭代式解析器。Andrii Senkovych[jollyroger](https://github.com/jollyroger))完成了向 CMake 的迁移。Kosta[Kosta-Github](https://github.com/Kosta-Github))提供了一个非常灵巧的短字符串优化。也需要感谢其他献者及社区成员。
2. 为何你要开发 RapidJSON
在 2011 年开始这项目是它仅一个兴趣项目。Milo Yip 是一个游戏程序员,他在那时候认识到 JSON 并希望在未来的项目中使用。由于 JSON 好像很简单,他希望写一个仅有头文件并且快速的程序库。
3. 为什么开发中段有一段长期空档?
主要是个人因素例如加入新家庭成员。另外Milo Yip 也花了许多业馀时间去翻译 Jason Gregory 的《Game Engine Architecture》至中文版《游戏引擎架构》。
4. 为什么这个项目从 Google Code 搬到 GitHub
这是大势所趋,而且 GitHub 更为强大及方便。

View File

@ -0,0 +1,104 @@
# Features
## General
* Cross-platform
* Compilers: Visual Studio, gcc, clang, etc.
* Architectures: x86, x64, ARM, etc.
* Operating systems: Windows, Mac OS X, Linux, iOS, Android, etc.
* Easy installation
* Header files only library. Just copy the headers to your project.
* Self-contained, minimal dependences
* No STL, BOOST, etc.
* Only included `<cstdio>`, `<cstdlib>`, `<cstring>`, `<inttypes.h>`, `<new>`, `<stdint.h>`.
* Without C++ exception, RTTI
* High performance
* Use template and inline functions to reduce function call overheads.
* Internal optimized Grisu2 and floating point parsing implementations.
* Optional SSE2/SSE4.2 support.
## Standard compliance
* RapidJSON should be fully RFC4627/ECMA-404 compliance.
* Support JSON Pointer (RFC6901).
* Support JSON Schema Draft v4.
* Support Unicode surrogate.
* Support null character (`"\u0000"`)
* For example, `["Hello\u0000World"]` can be parsed and handled gracefully. There is API for getting/setting lengths of string.
* Support optional relaxed syntax.
* Single line (`// ...`) and multiple line (`/* ... */`) comments (`kParseCommentsFlag`).
* Trailing commas at the end of objects and arrays (`kParseTrailingCommasFlag`).
* `NaN`, `Inf`, `Infinity`, `-Inf` and `-Infinity` as `double` values (`kParseNanAndInfFlag`)
* [NPM compliant](http://github.com/miloyip/rapidjson/blob/master/doc/npm.md).
## Unicode
* Support UTF-8, UTF-16, UTF-32 encodings, including little endian and big endian.
* These encodings are used in input/output streams and in-memory representation.
* Support automatic detection of encodings in input stream.
* Support transcoding between encodings internally.
* For example, you can read a UTF-8 file and let RapidJSON transcode the JSON strings into UTF-16 in the DOM.
* Support encoding validation internally.
* For example, you can read a UTF-8 file, and let RapidJSON check whether all JSON strings are valid UTF-8 byte sequence.
* Support custom character types.
* By default the character types are `char` for UTF8, `wchar_t` for UTF16, `uint32_t` for UTF32.
* Support custom encodings.
## API styles
* SAX (Simple API for XML) style API
* Similar to [SAX](http://en.wikipedia.org/wiki/Simple_API_for_XML), RapidJSON provides a event sequential access parser API (`rapidjson::GenericReader`). It also provides a generator API (`rapidjson::Writer`) which consumes the same set of events.
* DOM (Document Object Model) style API
* Similar to [DOM](http://en.wikipedia.org/wiki/Document_Object_Model) for HTML/XML, RapidJSON can parse JSON into a DOM representation (`rapidjson::GenericDocument`), for easy manipulation, and finally stringify back to JSON if needed.
* The DOM style API (`rapidjson::GenericDocument`) is actually implemented with SAX style API (`rapidjson::GenericReader`). SAX is faster but sometimes DOM is easier. Users can pick their choices according to scenarios.
## Parsing
* Recursive (default) and iterative parser
* Recursive parser is faster but prone to stack overflow in extreme cases.
* Iterative parser use custom stack to keep parsing state.
* Support *in situ* parsing.
* Parse JSON string values in-place at the source JSON, and then the DOM points to addresses of those strings.
* Faster than convention parsing: no allocation for strings, no copy (if string does not contain escapes), cache-friendly.
* Support 32-bit/64-bit signed/unsigned integer and `double` for JSON number type.
* Support parsing multiple JSONs in input stream (`kParseStopWhenDoneFlag`).
* Error Handling
* Support comprehensive error code if parsing failed.
* Support error message localization.
## DOM (Document)
* RapidJSON checks range of numerical values for conversions.
* Optimization for string literal
* Only store pointer instead of copying
* Optimization for "short" strings
* Store short string in `Value` internally without additional allocation.
* For UTF-8 string: maximum 11 characters in 32-bit, 21 characters in 64-bit (13 characters in x86-64).
* Optionally support `std::string` (define `RAPIDJSON_HAS_STDSTRING=1`)
## Generation
* Support `rapidjson::PrettyWriter` for adding newlines and indentations.
## Stream
* Support `rapidjson::GenericStringBuffer` for storing the output JSON as string.
* Support `rapidjson::FileReadStream` and `rapidjson::FileWriteStream` for input/output `FILE` object.
* Support custom streams.
## Memory
* Minimize memory overheads for DOM.
* Each JSON value occupies exactly 16/20 bytes for most 32/64-bit machines (excluding text string).
* Support fast default allocator.
* A stack-based allocator (allocate sequentially, prohibit to free individual allocations, suitable for parsing).
* User can provide a pre-allocated buffer. (Possible to parse a number of JSONs without any CRT allocation)
* Support standard CRT(C-runtime) allocator.
* Support custom allocators.
## Miscellaneous
* Some C++11 support (optional)
* Rvalue reference
* `noexcept` specifier
* Range-based for loop

View File

@ -0,0 +1,103 @@
# 特点
## 总体
* 跨平台
* 编译器Visual Studio、gcc、clang 等
* 架构x86、x64、ARM 等
* 操作系统Windows、Mac OS X、Linux、iOS、Android 等
* 容易安装
* 只有头文件的库。只需把头文件复制至你的项目中。
* 独立、最小依赖
* 不需依赖 STL、BOOST 等。
* 只包含 `<cstdio>`, `<cstdlib>`, `<cstring>`, `<inttypes.h>`, `<new>`, `<stdint.h>`
* 没使用 C++ 异常、RTTI
* 高性能
* 使用模版及内联函数去降低函数调用开销。
* 内部经优化的 Grisu2 及浮点数解析实现。
* 可选的 SSE2/SSE4.2 支持。
## 符合标准
* RapidJSON 应完全符合 RFC4627/ECMA-404 标准。
* 支持 JSON Pointer (RFC6901).
* 支持 JSON Schema Draft v4.
* 支持 Unicod 代理对surrogate pair
* 支持空字符(`"\u0000"`)。
* 例如,可以优雅地解析及处理 `["Hello\u0000World"]`。含读写字符串长度的 API。
* 支持可选的放宽语法
* 单行(`// ...`)及多行(`/* ... */` 注释 (`kParseCommentsFlag`)。
* 在对象和数组结束前含逗号 (`kParseTrailingCommasFlag`)。
* `NaN``Inf``Infinity``-Inf``-Infinity` 作为 `double` 值 (`kParseNanAndInfFlag`)
* [NPM 兼容](https://github.com/miloyip/rapidjson/blob/master/doc/npm.md).
## Unicode
* 支持 UTF-8、UTF-16、UTF-32 编码,包括小端序和大端序。
* 这些编码用于输入输出流,以及内存中的表示。
* 支持从输入流自动检测编码。
* 内部支持编码的转换。
* 例如,你可以读取一个 UTF-8 文件,让 RapidJSON 把 JSON 字符串转换至 UTF-16 的 DOM。
* 内部支持编码校验。
* 例如,你可以读取一个 UTF-8 文件,让 RapidJSON 检查是否所有 JSON 字符串是合法的 UTF-8 字节序列。
* 支持自定义的字符类型。
* 预设的字符类型是UTF-8 为 `char`UTF-16 为 `wchar_t`UTF32 为 `uint32_t`
* 支持自定义的编码。
## API 风格
* SAXSimple API for XML风格 API
* 类似于 [SAX](http://en.wikipedia.org/wiki/Simple_API_for_XML), RapidJSON 提供一个事件循序访问的解析器 API`rapidjson::GenericReader`。RapidJSON 也提供一个生成器 API`rapidjson::Writer`),可以处理相同的事件集合。
* DOMDocument Object Model风格 API
* 类似于 HTMLXML 的 [DOM](http://en.wikipedia.org/wiki/Document_Object_Model)RapidJSON 可把 JSON 解析至一个 DOM 表示方式(`rapidjson::GenericDocument`),以方便操作。如有需要,可把 DOM 转换stringify回 JSON。
* DOM 风格 API`rapidjson::GenericDocument`)实际上是由 SAX 风格 API`rapidjson::GenericReader`实现的。SAX 更快,但有时 DOM 更易用。用户可根据情况作出选择。
## 解析
* 递归式(预设)及迭代式解析器
* 递归式解析器较快,但在极端情况下可出现堆栈溢出。
* 迭代式解析器使用自定义的堆栈去维持解析状态。
* 支持原位(*in situ*)解析。
* 把 JSON 字符串的值解析至原 JSON 之中,然后让 DOM 指向那些字符串。
* 比常规分析更快:不需字符串的内存分配、不需复制(如字符串不含转义符)、缓存友好。
* 对于 JSON 数字类型,支持 32-bit/64-bit 的有号/无号整数,以及 `double`
* 错误处理
* 支持详尽的解析错误代号。
* 支持本地化错误信息。
## DOM (Document)
* RapidJSON 在类型转换时会检查数值的范围。
* 字符串字面量的优化
* 只储存指针,不作复制
* 优化“短”字符串
* 在 `Value` 内储存短字符串,无需额外分配。
* 对 UTF-8 字符串来说32 位架构下可存储最多 11 字符64 位下 21 字符x86-64 下 13 字符)。
* 可选地支持 `std::string`(定义 `RAPIDJSON_HAS_STDSTRING=1`
## 生成
* 支持 `rapidjson::PrettyWriter` 去加入换行及缩进。
## 输入输出流
* 支持 `rapidjson::GenericStringBuffer`,把输出的 JSON 储存于字符串内。
* 支持 `rapidjson::FileReadStream``rapidjson::FileWriteStream`,使用 `FILE` 对象作输入输出。
* 支持自定义输入输出流。
## 内存
* 最小化 DOM 的内存开销。
* 对大部分 3264 位机器而言,每个 JSON 值只占 16 或 20 字节(不包含字符串)。
* 支持快速的预设分配器。
* 它是一个堆栈形式的分配器(顺序分配,不容许单独释放,适合解析过程之用)。
* 使用者也可提供一个预分配的缓冲区。(有可能达至无需 CRT 分配就能解析多个 JSON
* 支持标准 CRTC-runtime分配器。
* 支持自定义分配器。
## 其他
* 一些 C++11 的支持(可选)
* 右值引用rvalue reference
* `noexcept` 修饰符
* 范围 for 循环

View File

@ -0,0 +1,365 @@
# Internals
This section records some design and implementation details.
[TOC]
# Architecture {#Architecture}
## SAX and DOM
The basic relationships of SAX and DOM is shown in the following UML diagram.
![Architecture UML class diagram](diagram/architecture.png)
The core of the relationship is the `Handler` concept. From the SAX side, `Reader` parses a JSON from a stream and publish events to a `Handler`. `Writer` implements the `Handler` concept to handle the same set of events. From the DOM side, `Document` implements the `Handler` concept to build a DOM according to the events. `Value` supports a `Value::Accept(Handler&)` function, which traverses the DOM to publish events.
With this design, SAX is not dependent on DOM. Even `Reader` and `Writer` have no dependencies between them. This provides flexibility to chain event publisher and handlers. Besides, `Value` does not depends on SAX as well. So, in addition to stringify a DOM to JSON, user may also stringify it to a XML writer, or do anything else.
## Utility Classes
Both SAX and DOM APIs depends on 3 additional concepts: `Allocator`, `Encoding` and `Stream`. Their inheritance hierarchy is shown as below.
![Utility classes UML class diagram](diagram/utilityclass.png)
# Value {#Value}
`Value` (actually a typedef of `GenericValue<UTF8<>>`) is the core of DOM API. This section describes the design of it.
## Data Layout {#DataLayout}
`Value` is a [variant type](http://en.wikipedia.org/wiki/Variant_type). In RapidJSON's context, an instance of `Value` can contain 1 of 6 JSON value types. This is possible by using `union`. Each `Value` contains two members: `union Data data_` and a`unsigned flags_`. The `flags_` indiciates the JSON type, and also additional information.
The following tables show the data layout of each type. The 32-bit/64-bit columns indicates the size of the field in bytes.
| Null | |32-bit|64-bit|
|-------------------|----------------------------------|:----:|:----:|
| (unused) | |4 |8 |
| (unused) | |4 |4 |
| (unused) | |4 |4 |
| `unsigned flags_` | `kNullType kNullFlag` |4 |4 |
| Bool | |32-bit|64-bit|
|-------------------|----------------------------------------------------|:----:|:----:|
| (unused) | |4 |8 |
| (unused) | |4 |4 |
| (unused) | |4 |4 |
| `unsigned flags_` | `kBoolType` (either `kTrueFlag` or `kFalseFlag`) |4 |4 |
| String | |32-bit|64-bit|
|---------------------|-------------------------------------|:----:|:----:|
| `Ch* str` | Pointer to the string (may own) |4 |8 |
| `SizeType length` | Length of string |4 |4 |
| (unused) | |4 |4 |
| `unsigned flags_` | `kStringType kStringFlag ...` |4 |4 |
| Object | |32-bit|64-bit|
|---------------------|-------------------------------------|:----:|:----:|
| `Member* members` | Pointer to array of members (owned) |4 |8 |
| `SizeType size` | Number of members |4 |4 |
| `SizeType capacity` | Capacity of members |4 |4 |
| `unsigned flags_` | `kObjectType kObjectFlag` |4 |4 |
| Array | |32-bit|64-bit|
|---------------------|-------------------------------------|:----:|:----:|
| `Value* values` | Pointer to array of values (owned) |4 |8 |
| `SizeType size` | Number of values |4 |4 |
| `SizeType capacity` | Capacity of values |4 |4 |
| `unsigned flags_` | `kArrayType kArrayFlag` |4 |4 |
| Number (Int) | |32-bit|64-bit|
|---------------------|-------------------------------------|:----:|:----:|
| `int i` | 32-bit signed integer |4 |4 |
| (zero padding) | 0 |4 |4 |
| (unused) | |4 |8 |
| `unsigned flags_` | `kNumberType kNumberFlag kIntFlag kInt64Flag ...` |4 |4 |
| Number (UInt) | |32-bit|64-bit|
|---------------------|-------------------------------------|:----:|:----:|
| `unsigned u` | 32-bit unsigned integer |4 |4 |
| (zero padding) | 0 |4 |4 |
| (unused) | |4 |8 |
| `unsigned flags_` | `kNumberType kNumberFlag kUIntFlag kUInt64Flag ...` |4 |4 |
| Number (Int64) | |32-bit|64-bit|
|---------------------|-------------------------------------|:----:|:----:|
| `int64_t i64` | 64-bit signed integer |8 |8 |
| (unused) | |4 |8 |
| `unsigned flags_` | `kNumberType kNumberFlag kInt64Flag ...` |4 |4 |
| Number (Uint64) | |32-bit|64-bit|
|---------------------|-------------------------------------|:----:|:----:|
| `uint64_t i64` | 64-bit unsigned integer |8 |8 |
| (unused) | |4 |8 |
| `unsigned flags_` | `kNumberType kNumberFlag kInt64Flag ...` |4 |4 |
| Number (Double) | |32-bit|64-bit|
|---------------------|-------------------------------------|:----:|:----:|
| `uint64_t i64` | Double precision floating-point |8 |8 |
| (unused) | |4 |8 |
| `unsigned flags_` | `kNumberType kNumberFlag kDoubleFlag` |4 |4 |
Here are some notes:
* To reduce memory consumption for 64-bit architecture, `SizeType` is typedef as `unsigned` instead of `size_t`.
* Zero padding for 32-bit number may be placed after or before the actual type, according to the endianess. This makes possible for interpreting a 32-bit integer as a 64-bit integer, without any conversion.
* An `Int` is always an `Int64`, but the converse is not always true.
## Flags {#Flags}
The 32-bit `flags_` contains both JSON type and other additional information. As shown in the above tables, each JSON type contains redundant `kXXXType` and `kXXXFlag`. This design is for optimizing the operation of testing bit-flags (`IsNumber()`) and obtaining a sequential number for each type (`GetType()`).
String has two optional flags. `kCopyFlag` means that the string owns a copy of the string. `kInlineStrFlag` means using [Short-String Optimization](#ShortString).
Number is a bit more complicated. For normal integer values, it can contains `kIntFlag`, `kUintFlag`, `kInt64Flag` and/or `kUint64Flag`, according to the range of the integer. For numbers with fraction, and integers larger than 64-bit range, they will be stored as `double` with `kDoubleFlag`.
## Short-String Optimization {#ShortString}
[Kosta](https://github.com/Kosta-Github) provided a very neat short-string optimization. The optimization idea is given as follow. Excluding the `flags_`, a `Value` has 12 or 16 bytes (32-bit or 64-bit) for storing actual data. Instead of storing a pointer to a string, it is possible to store short strings in these space internally. For encoding with 1-byte character type (e.g. `char`), it can store maximum 11 or 15 characters string inside the `Value` type.
| ShortString (Ch=char) | |32-bit|64-bit|
|---------------------|-------------------------------------|:----:|:----:|
| `Ch str[MaxChars]` | String buffer |11 |15 |
| `Ch invLength` | MaxChars - Length |1 |1 |
| `unsigned flags_` | `kStringType kStringFlag ...` |4 |4 |
A special technique is applied. Instead of storing the length of string directly, it stores (MaxChars - length). This make it possible to store 11 characters with trailing `\0`.
This optimization can reduce memory usage for copy-string. It can also improve cache-coherence thus improve runtime performance.
# Allocator {#InternalAllocator}
`Allocator` is a concept in RapidJSON:
~~~cpp
concept Allocator {
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
// Allocate a memory block.
// \param size of the memory block in bytes.
// \returns pointer to the memory block.
void* Malloc(size_t size);
// Resize a memory block.
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
// \param newSize the new size in bytes.
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
// Free a memory block.
// \param pointer to the memory block. Null pointer is permitted.
static void Free(void *ptr);
};
~~~
Note that `Malloc()` and `Realloc()` are member functions but `Free()` is static member function.
## MemoryPoolAllocator {#MemoryPoolAllocator}
`MemoryPoolAllocator` is the default allocator for DOM. It allocate but do not free memory. This is suitable for building a DOM tree.
Internally, it allocates chunks of memory from the base allocator (by default `CrtAllocator`) and stores the chunks as a singly linked list. When user requests an allocation, it allocates memory from the following order:
1. User supplied buffer if it is available. (See [User Buffer section in DOM](doc/dom.md))
2. If user supplied buffer is full, use the current memory chunk.
3. If the current block is full, allocate a new block of memory.
# Parsing Optimization {#ParsingOptimization}
## Skip Whitespaces with SIMD {#SkipwhitespaceWithSIMD}
When parsing JSON from a stream, the parser need to skip 4 whitespace characters:
1. Space (`U+0020`)
2. Character Tabulation (`U+000B`)
3. Line Feed (`U+000A`)
4. Carriage Return (`U+000D`)
A simple implementation will be simply:
~~~cpp
void SkipWhitespace(InputStream& s) {
while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t')
s.Take();
}
~~~
However, this requires 4 comparisons and a few branching for each character. This was found to be a hot spot.
To accelerate this process, SIMD was applied to compare 16 characters with 4 white spaces for each iteration. Currently RapidJSON only supports SSE2 and SSE4.2 instructions for this. And it is only activated for UTF-8 memory streams, including string stream or *in situ* parsing.
To enable this optimization, need to define `RAPIDJSON_SSE2` or `RAPIDJSON_SSE42` before including `rapidjson.h`. Some compilers can detect the setting, as in `perftest.h`:
~~~cpp
// __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler.
// We use -march=native with gmake to enable -msse2 and -msse4.2, if supported.
#if defined(__SSE4_2__)
# define RAPIDJSON_SSE42
#elif defined(__SSE2__)
# define RAPIDJSON_SSE2
#endif
~~~
Note that, these are compile-time settings. Running the executable on a machine without such instruction set support will make it crash.
### Page boundary issue
In an early version of RapidJSON, [an issue](https://code.google.com/archive/p/rapidjson/issues/104) reported that the `SkipWhitespace_SIMD()` causes crash very rarely (around 1 in 500,000). After investigation, it is suspected that `_mm_loadu_si128()` accessed bytes after `'\0'`, and across a protected page boundary.
In [Intel® 64 and IA-32 Architectures Optimization Reference Manual
](http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html), section 10.2.1:
> To support algorithms requiring unaligned 128-bit SIMD memory accesses, memory buffer allocation by a caller function should consider adding some pad space so that a callee function can safely use the address pointer safely with unaligned 128-bit SIMD memory operations.
> The minimal padding size should be the width of the SIMD register that might be used in conjunction with unaligned SIMD memory access.
This is not feasible as RapidJSON should not enforce such requirement.
To fix this issue, currently the routine process bytes up to the next aligned address. After tha, use aligned read to perform SIMD processing. Also see [#85](https://github.com/miloyip/rapidjson/issues/85).
## Local Stream Copy {#LocalStreamCopy}
During optimization, it is found that some compilers cannot localize some member data access of streams into local variables or registers. Experimental results show that for some stream types, making a copy of the stream and used it in inner-loop can improve performance. For example, the actual (non-SIMD) implementation of `SkipWhitespace()` is implemented as:
~~~cpp
template<typename InputStream>
void SkipWhitespace(InputStream& is) {
internal::StreamLocalCopy<InputStream> copy(is);
InputStream& s(copy.s);
while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t')
s.Take();
}
~~~
Depending on the traits of stream, `StreamLocalCopy` will make (or not make) a copy of the stream object, use it locally and copy the states of stream back to the original stream.
## Parsing to Double {#ParsingDouble}
Parsing string into `double` is difficult. The standard library function `strtod()` can do the job but it is slow. By default, the parsers use normal precision setting. This has has maximum 3 [ULP](http://en.wikipedia.org/wiki/Unit_in_the_last_place) error and implemented in `internal::StrtodNormalPrecision()`.
When using `kParseFullPrecisionFlag`, the parsers calls `internal::StrtodFullPrecision()` instead, and this function actually implemented 3 versions of conversion methods.
1. [Fast-Path](http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/).
2. Custom DIY-FP implementation as in [double-conversion](https://github.com/floitsch/double-conversion).
3. Big Integer Method as in (Clinger, William D. How to read floating point numbers accurately. Vol. 25. No. 6. ACM, 1990).
If the first conversion methods fail, it will try the second, and so on.
# Generation Optimization {#GenerationOptimization}
## Integer-to-String conversion {#itoa}
The naive algorithm for integer-to-string conversion involves division per each decimal digit. We have implemented various implementations and evaluated them in [itoa-benchmark](https://github.com/miloyip/itoa-benchmark).
Although SSE2 version is the fastest but the difference is minor by comparing to the first running-up `branchlut`. And `branchlut` is pure C++ implementation so we adopt `branchlut` in RapidJSON.
## Double-to-String conversion {#dtoa}
Originally RapidJSON uses `snprintf(..., ..., "%g")` to achieve double-to-string conversion. This is not accurate as the default precision is 6. Later we also find that this is slow and there is an alternative.
Google's V8 [double-conversion](https://github.com/floitsch/double-conversion
) implemented a newer, fast algorithm called Grisu3 (Loitsch, Florian. "Printing floating-point numbers quickly and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243.).
However, since it is not header-only so that we implemented a header-only version of Grisu2. This algorithm guarantees that the result is always accurate. And in most of cases it produces the shortest (optimal) string representation.
The header-only conversion function has been evaluated in [dtoa-benchmark](https://github.com/miloyip/dtoa-benchmark).
# Parser {#Parser}
## Iterative Parser {#IterativeParser}
The iterative parser is a recursive descent LL(1) parser
implemented in a non-recursive manner.
### Grammar {#IterativeParserGrammar}
The grammar used for this parser is based on strict JSON syntax:
~~~~~~~~~~
S -> array | object
array -> [ values ]
object -> { members }
values -> non-empty-values | ε
non-empty-values -> value addition-values
addition-values -> ε | , non-empty-values
members -> non-empty-members | ε
non-empty-members -> member addition-members
addition-members -> ε | , non-empty-members
member -> STRING : value
value -> STRING | NUMBER | NULL | BOOLEAN | object | array
~~~~~~~~~~
Note that left factoring is applied to non-terminals `values` and `members`
to make the grammar be LL(1).
### Parsing Table {#IterativeParserParsingTable}
Based on the grammar, we can construct the FIRST and FOLLOW set.
The FIRST set of non-terminals is listed below:
| NON-TERMINAL | FIRST |
|:-----------------:|:--------------------------------:|
| array | [ |
| object | { |
| values | ε STRING NUMBER NULL BOOLEAN { [ |
| addition-values | ε COMMA |
| members | ε STRING |
| addition-members | ε COMMA |
| member | STRING |
| value | STRING NUMBER NULL BOOLEAN { [ |
| S | [ { |
| non-empty-members | STRING |
| non-empty-values | STRING NUMBER NULL BOOLEAN { [ |
The FOLLOW set is listed below:
| NON-TERMINAL | FOLLOW |
|:-----------------:|:-------:|
| S | $ |
| array | , $ } ] |
| object | , $ } ] |
| values | ] |
| non-empty-values | ] |
| addition-values | ] |
| members | } |
| non-empty-members | } |
| addition-members | } |
| member | , } |
| value | , } ] |
Finally the parsing table can be constructed from FIRST and FOLLOW set:
| NON-TERMINAL | [ | { | , | : | ] | } | STRING | NUMBER | NULL | BOOLEAN |
|:-----------------:|:---------------------:|:---------------------:|:-------------------:|:-:|:-:|:-:|:-----------------------:|:---------------------:|:---------------------:|:---------------------:|
| S | array | object | | | | | | | | |
| array | [ values ] | | | | | | | | | |
| object | | { members } | | | | | | | | |
| values | non-empty-values | non-empty-values | | | ε | | non-empty-values | non-empty-values | non-empty-values | non-empty-values |
| non-empty-values | value addition-values | value addition-values | | | | | value addition-values | value addition-values | value addition-values | value addition-values |
| addition-values | | | , non-empty-values | | ε | | | | | |
| members | | | | | | ε | non-empty-members | | | |
| non-empty-members | | | | | | | member addition-members | | | |
| addition-members | | | , non-empty-members | | | ε | | | | |
| member | | | | | | | STRING : value | | | |
| value | array | object | | | | | STRING | NUMBER | NULL | BOOLEAN |
There is a great [tool](http://hackingoff.com/compilers/predict-first-follow-set) for above grammar analysis.
### Implementation {#IterativeParserImplementation}
Based on the parsing table, a direct(or conventional) implementation
that pushes the production body in reverse order
while generating a production could work.
In RapidJSON, several modifications(or adaptations to current design) are made to a direct implementation.
First, the parsing table is encoded in a state machine in RapidJSON.
States are constructed by the head and body of production.
State transitions are constructed by production rules.
Besides, extra states are added for productions involved with `array` and `object`.
In this way the generation of array values or object members would be a single state transition,
rather than several pop/push operations in the direct implementation.
This also makes the estimation of stack size more easier.
The state diagram is shown as follows:
![State Diagram](diagram/iterative-parser-states-diagram.png)
Second, the iterative parser also keeps track of array's value count and object's member count
in its internal stack, which may be different from a conventional implementation.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="217.15039"
height="60.831055"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="rapidjson.svg">
<defs
id="defs4">
<linearGradient
id="linearGradient3801">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3803" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3805" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3801"
id="linearGradient3807"
x1="81.25"
y1="52.737183"
x2="122.25"
y2="52.737183"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2378503,0,0,1.1662045,-226.99279,64.427324)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3801"
id="linearGradient3935"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.4768835,0,0,2.2904698,246.48785,81.630301)"
x1="81.25"
y1="52.737183"
x2="115.96579"
y2="48.439766" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3801"
id="linearGradient3947"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2378503,0,0,1.1662045,-226.99279,-10.072676)"
x1="81.25"
y1="52.737183"
x2="122.25"
y2="52.737183" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2"
inkscape:cx="207.8959"
inkscape:cy="-3.2283687"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1137"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-39.132812,-38.772339)">
<text
sodipodi:linespacing="125%"
id="text3939"
y="79.862183"
x="147.5"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Microsoft JhengHei;-inkscape-font-specification:Microsoft JhengHei"
xml:space="preserve"><tspan
style="font-size:48px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Inconsolata;-inkscape-font-specification:Inconsolata"
y="79.862183"
x="147.5"
id="tspan3941"
sodipodi:role="line"><tspan
id="tspan3943"
style="font-size:42px;font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Segoe UI;-inkscape-font-specification:Segoe UI Oblique">Rapid</tspan><tspan
id="tspan3945"
style="font-weight:bold;-inkscape-font-specification:Inconsolata Bold">JSON</tspan></tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,194 @@
<doxygenlayout version="1.0">
<!-- Generated by doxygen 1.8.7 -->
<!-- Navigation index tabs for HTML output -->
<navindex>
<tab type="mainpage" visible="yes" title=""/>
<tab type="pages" visible="yes" title="" intro=""/>
<tab type="modules" visible="yes" title="" intro=""/>
<tab type="namespaces" visible="yes" title="">
<tab type="namespacelist" visible="yes" title="" intro=""/>
<tab type="namespacemembers" visible="yes" title="" intro=""/>
</tab>
<tab type="classes" visible="yes" title="">
<tab type="classlist" visible="yes" title="" intro=""/>
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="hierarchy" visible="yes" title="" intro=""/>
<tab type="classmembers" visible="yes" title="" intro=""/>
</tab>
<tab type="files" visible="yes" title="">
<tab type="filelist" visible="yes" title="" intro=""/>
<tab type="globals" visible="yes" title="" intro=""/>
</tab>
<tab type="examples" visible="yes" title="" intro=""/>
</navindex>
<!-- Layout definition for a class page -->
<class>
<briefdescription visible="yes"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<inheritancegraph visible="$CLASS_GRAPH"/>
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
<memberdecl>
<nestedclasses visible="yes" title=""/>
<publictypes title=""/>
<services title=""/>
<interfaces title=""/>
<publicslots title=""/>
<signals title=""/>
<publicmethods title=""/>
<publicstaticmethods title=""/>
<publicattributes title=""/>
<publicstaticattributes title=""/>
<protectedtypes title=""/>
<protectedslots title=""/>
<protectedmethods title=""/>
<protectedstaticmethods title=""/>
<protectedattributes title=""/>
<protectedstaticattributes title=""/>
<packagetypes title=""/>
<packagemethods title=""/>
<packagestaticmethods title=""/>
<packageattributes title=""/>
<packagestaticattributes title=""/>
<properties title=""/>
<events title=""/>
<privatetypes title=""/>
<privateslots title=""/>
<privatemethods title=""/>
<privatestaticmethods title=""/>
<privateattributes title=""/>
<privatestaticattributes title=""/>
<friends title=""/>
<related title="" subtitle=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<services title=""/>
<interfaces title=""/>
<constructors title=""/>
<functions title=""/>
<related title=""/>
<variables title=""/>
<properties title=""/>
<events title=""/>
</memberdef>
<allmemberslink visible="yes"/>
<usedfiles visible="$SHOW_USED_FILES"/>
<authorsection visible="yes"/>
</class>
<!-- Layout definition for a namespace page -->
<namespace>
<briefdescription visible="yes"/>
<memberdecl>
<nestednamespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<classes visible="yes" title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection visible="yes"/>
</namespace>
<!-- Layout definition for a file page -->
<file>
<briefdescription visible="yes"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<includegraph visible="$INCLUDE_GRAPH"/>
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
<sourcelink visible="yes"/>
<memberdecl>
<classes visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection/>
</file>
<!-- Layout definition for a group page -->
<group>
<briefdescription visible="yes"/>
<groupgraph visible="$GROUP_GRAPHS"/>
<memberdecl>
<nestedgroups visible="yes" title=""/>
<dirs visible="yes" title=""/>
<files visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<pagedocs/>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
</memberdef>
<authorsection visible="yes"/>
</group>
<!-- Layout definition for a directory page -->
<directory>
<briefdescription visible="yes"/>
<directorygraph visible="yes"/>
<memberdecl>
<dirs visible="yes"/>
<files visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
</directory>
</doxygenlayout>

View File

@ -0,0 +1,274 @@
body code {
margin: 0;
border: 1px solid #ddd;
background-color: #f8f8f8;
border-radius: 3px;
padding: 0;
}
a {
color: #4183c4;
}
a.el {
font-weight: normal;
}
body, table, div, p, dl {
color: #333333;
font-family: Helvetica, arial, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol';
font-size: 15px;
font-style: normal;
font-variant: normal;
font-weight: normal;
line-height: 25.5px;
}
body {
background-color: #eee;
}
div.header {
background-image: none;
background-color: white;
margin: 0px;
border: 0px;
}
div.headertitle {
width: 858px;
margin: 30px;
padding: 0px;
}
div.toc {
background-color: #f8f8f8;
border-color: #ddd;
margin-right: 10px;
margin-left: 20px;
}
div.toc h3 {
color: #333333;
font-family: Helvetica, arial, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol';
font-size: 18px;
font-style: normal;
font-variant: normal;
font-weight: normal;
}
div.toc li {
color: #333333;
font-family: Helvetica, arial, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol';
font-size: 12px;
font-style: normal;
font-variant: normal;
font-weight: normal;
}
.title {
font-size: 2.5em;
line-height: 63.75px;
border-bottom: 1px solid #ddd;
margin-bottom: 15px;
margin-left: 0px;
margin-right: 0px;
margin-top: 0px;
}
.summary {
float: none !important;
width: auto !important;
padding-top: 10px;
padding-right: 10px !important;
}
.summary + .headertitle .title {
font-size: 1.5em;
line-height: 2.0em;
}
body h1 {
font-size: 2em;
line-height: 1.7;
border-bottom: 1px solid #eee;
margin: 1em 0 15px;
padding: 0;
overflow: hidden;
}
body h2 {
font-size: 1.5em;
line-height: 1.7;
margin: 1em 0 15px;
padding: 0;
}
pre.fragment {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 13px;
font-style: normal;
font-variant: normal;
font-weight: normal;
line-height: 19px;
}
table.doxtable th {
background-color: #f8f8f8;
color: #333333;
font-size: 15px;
}
table.doxtable td, table.doxtable th {
border: 1px solid #ddd;
}
#doc-content {
background-color: #fff;
width: 918px;
height: auto !important;
margin-left: 270px !important;
}
div.contents {
width: 858px;
margin: 30px;
}
div.line {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 13px;
font-style: normal;
font-variant: normal;
font-weight: normal;
line-height: 19px;
}
tt, code, pre {
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
}
div.fragment {
background-color: #f8f8f8;
border: 1px solid #ddd;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
#topbanner {
position: fixed;
margin: 15px;
z-index: 101;
}
#projectname
{
font-family: Helvetica, arial, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol';
font-size: 38px;
font-weight: bold;
line-height: 63.75px;
margin: 0px;
padding: 2px 0px;
}
#projectbrief
{
font-family: Helvetica, arial, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol';
font-size: 16px;
line-height: 22.4px;
margin: 0px 0px 13px 0px;
padding: 2px;
}
/* side bar and search */
#side-nav
{
padding: 10px 0px 20px 20px;
border-top: 60px solid #2980b9;
background-color: #343131;
width: 250px !important;
height: 100% !important;
position: fixed;
}
#nav-tree
{
background-color: transparent;
background-image: none;
height: 100% !important;
}
#nav-tree .label
{
font-family: Helvetica, arial, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol';
line-height: 25.5px;
font-size: 15px;
}
#nav-tree
{
color: #b3b3b3;
}
#nav-tree .selected {
background-image: none;
}
#nav-tree a
{
color: #b3b3b3;
}
#github
{
position: fixed;
left: auto;
right: auto;
width: 250px;
}
#MSearchBox
{
margin: 20px;
left: 40px;
right: auto;
position: fixed;
width: 180px;
}
#MSearchField
{
width: 121px;
}
#MSearchResultsWindow
{
left: 45px !important;
}
#nav-sync
{
display: none;
}
.ui-resizable .ui-resizable-handle
{
width: 0px;
}
#nav-path
{
display: none;
}
/* external link icon */
div.contents a[href ^= "http"]:after {
content: " " url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAVklEQVR4Xn3PgQkAMQhDUXfqTu7kTtkpd5RA8AInfArtQ2iRXFWT2QedAfttj2FsPIOE1eCOlEuoWWjgzYaB/IkeGOrxXhqB+uA9Bfcm0lAZuh+YIeAD+cAqSz4kCMUAAAAASUVORK5CYII=);
}
.githublogo {
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RERCMUIwOUY4NkNFMTFFM0FBNTJFRTMzNTJEMUJDNDYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RERCMUIwOUU4NkNFMTFFM0FBNTJFRTMzNTJEMUJDNDYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkU1MTc4QTJBOTlBMDExRTI5QTE1QkMxMDQ2QTg5MDREIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkU1MTc4QTJCOTlBMDExRTI5QTE1QkMxMDQ2QTg5MDREIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+jUqS1wAAApVJREFUeNq0l89rE1EQx3e3gVJoSPzZeNEWPKgHoa0HBak0iHiy/4C3WvDmoZ56qJ7txVsPQu8qlqqHIhRKJZceesmhioQEfxTEtsoSpdJg1u/ABJ7Pmc1m8zLwgWTmzcw3L+/te+tHUeQltONgCkyCi2AEDHLsJ6iBMlgHL8FeoqokoA2j4CloRMmtwTmj7erHBXPgCWhG6a3JNXKdCiDl1cidVbXZkJoXQRi5t5BrxwoY71FzU8S4JuAIqFkJ2+BFSlEh525b/hr3+k/AklDkNsf6wTT4yv46KIMNpsy+iMdMc47HNWxbsgVcUn7FmLAzzoFAWDsBx+wVP6bUpp5ewI+DOeUx0Wd9D8F70BTGNjkWtqnhmT1JQAHcUgZd8Lo3rQb1LAT8eJVUfgGvHQigGp+V2Z0iAUUl8QH47kAA1XioxIo+bRN8OG8F/oBjwv+Z1nJgX5jpdzQDw0LCjsPmrcW7I/iHScCAEDj03FtD8A0EyuChHgg4KTlJQF3wZ7WELppnBX+dBFSVpJsOBWi1qiRgSwnOgoyD5hmuJdkWCVhTgnTvW3AgYIFrSbZGh0UW/Io5Vp+DQoK7o80pztWMemZbgxeNwCNwDbw1fIfgGZjhU6xPaJgBV8BdsMw5cbZoHsenwYFxkZzl83xTSKTiviCAfCsJLysH3POfC8m8NegyGAGfLP/VmGmfSChgXroR0RSWjEFv2J/nG84cuKFMf4sTCZqXuJd4KaXFVjEG3+tw4eXbNK/YC9oXXs3O8NY8y99L4BXY5cvLY/Bb2VZ58EOJVcB18DHJq9lRsKr8inyKGVjlmh29mtHs3AHfuhCwy1vXT/Nu2GKQt+UHsGdctyX6eQyNvc+5sfX9Dl7Pe2J/BRgAl2CpwmrsHR0AAAAASUVORK5CYII=);
}

View File

@ -0,0 +1,11 @@
<!-- HTML footer for doxygen 1.8.7-->
<!-- start footer part -->
<!--BEGIN GENERATE_TREEVIEW-->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
$navpath
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
</body>
</html>

View File

@ -0,0 +1,24 @@
<!-- HTML header for doxygen 1.8.7-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="topbanner"><a href="https://github.com/miloyip/rapidjson" title="RapidJSON GitHub"><i class="githublogo"></i></a></div>
$searchbox
<!--END TITLEAREA-->
<!-- end header part -->

31
thirdparty/rapidjson-1.1.0/doc/npm.md vendored Normal file
View File

@ -0,0 +1,31 @@
## NPM
# package.json {#package}
~~~~~~~~~~js
{
...
"dependencies": {
...
"rapidjson": "git@github.com:miloyip/rapidjson.git"
},
...
"gypfile": true
}
~~~~~~~~~~
# binding.gyp {#binding}
~~~~~~~~~~js
{
...
'targets': [
{
...
'include_dirs': [
'<!(node -e \'require("rapidjson")\')'
]
}
]
}
~~~~~~~~~~

View File

@ -0,0 +1,26 @@
# Performance
There is a [native JSON benchmark collection] [1] which evaluates speed, memory usage and code size of various operations among 37 JSON libaries.
[1]: https://github.com/miloyip/nativejson-benchmark
The old performance article for RapidJSON 0.1 is provided [here](https://code.google.com/p/rapidjson/wiki/Performance).
Additionally, you may refer to the following third-party benchmarks.
## Third-party benchmarks
* [Basic benchmarks for miscellaneous C++ JSON parsers and generators](https://github.com/mloskot/json_benchmark) by Mateusz Loskot (Jun 2013)
* [casablanca](https://casablanca.codeplex.com/)
* [json_spirit](https://github.com/cierelabs/json_spirit)
* [jsoncpp](http://jsoncpp.sourceforge.net/)
* [libjson](http://sourceforge.net/projects/libjson/)
* [rapidjson](https://github.com/miloyip/rapidjson/)
* [QJsonDocument](http://qt-project.org/doc/qt-5.0/qtcore/qjsondocument.html)
* [JSON Parser Benchmarking](http://chadaustin.me/2013/01/json-parser-benchmarking/) by Chad Austin (Jan 2013)
* [sajson](https://github.com/chadaustin/sajson)
* [rapidjson](https://github.com/miloyip/rapidjson/)
* [vjson](https://code.google.com/p/vjson/)
* [YAJL](http://lloyd.github.com/yajl/)
* [Jansson](http://www.digip.org/jansson/)

View File

@ -0,0 +1,26 @@
# 性能
有一个 [native JSON benchmark collection][1] 项目,能评估 37 个 JSON 库在不同操作下的速度、內存用量及代码大小。
[1]: https://github.com/miloyip/nativejson-benchmark
RapidJSON 0.1 版本的性能测试文章位于 [这里](https://code.google.com/p/rapidjson/wiki/Performance).
此外,你也可以参考以下这些第三方的评测。
## 第三方评测
* [Basic benchmarks for miscellaneous C++ JSON parsers and generators](https://github.com/mloskot/json_benchmark) by Mateusz Loskot (Jun 2013)
* [casablanca](https://casablanca.codeplex.com/)
* [json_spirit](https://github.com/cierelabs/json_spirit)
* [jsoncpp](http://jsoncpp.sourceforge.net/)
* [libjson](http://sourceforge.net/projects/libjson/)
* [rapidjson](https://github.com/miloyip/rapidjson/)
* [QJsonDocument](http://qt-project.org/doc/qt-5.0/qtcore/qjsondocument.html)
* [JSON Parser Benchmarking](http://chadaustin.me/2013/01/json-parser-benchmarking/) by Chad Austin (Jan 2013)
* [sajson](https://github.com/chadaustin/sajson)
* [rapidjson](https://github.com/miloyip/rapidjson/)
* [vjson](https://code.google.com/p/vjson/)
* [YAJL](http://lloyd.github.com/yajl/)
* [Jansson](http://www.digip.org/jansson/)

View File

@ -0,0 +1,234 @@
# Pointer
(This feature was released in v1.1.0)
JSON Pointer is a standardized ([RFC6901]) way to select a value inside a JSON Document (DOM). This can be analogous to XPath for XML document. However, JSON Pointer is much simpler, and a single JSON Pointer only pointed to a single value.
Using RapidJSON's implementation of JSON Pointer can simplify some manipulations of the DOM.
[TOC]
# JSON Pointer {#JsonPointer}
A JSON Pointer is a list of zero-to-many tokens, each prefixed by `/`. Each token can be a string or a number. For example, given a JSON:
~~~javascript
{
"foo" : ["bar", "baz"],
"pi" : 3.1416
}
~~~
The following JSON Pointers resolve this JSON as:
1. `"/foo"``[ "bar", "baz" ]`
2. `"/foo/0"``"bar"`
3. `"/foo/1"``"baz"`
4. `"/pi"``3.1416`
Note that, an empty JSON Pointer `""` (zero token) resolves to the whole JSON.
# Basic Usage {#BasicUsage}
The following example code is self-explanatory.
~~~cpp
#include "rapidjson/pointer.h"
// ...
Document d;
// Create DOM by Set()
Pointer("/project").Set(d, "RapidJSON");
Pointer("/stars").Set(d, 10);
// { "project" : "RapidJSON", "stars" : 10 }
// Access DOM by Get(). It return nullptr if the value does not exist.
if (Value* stars = Pointer("/stars").Get(d))
stars->SetInt(stars->GetInt() + 1);
// { "project" : "RapidJSON", "stars" : 11 }
// Set() and Create() automatically generate parents if not exist.
Pointer("/a/b/0").Create(d);
// { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] } }
// GetWithDefault() returns reference. And it deep clones the default value.
Value& hello = Pointer("/hello").GetWithDefault(d, "world");
// { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] }, "hello" : "world" }
// Swap() is similar to Set()
Value x("C++");
Pointer("/hello").Swap(d, x);
// { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] }, "hello" : "C++" }
// x becomes "world"
// Erase a member or element, return true if the value exists
bool success = Pointer("/a").Erase(d);
assert(success);
// { "project" : "RapidJSON", "stars" : 10 }
~~~
# Helper Functions {#HelperFunctions}
Since object-oriented calling convention may be non-intuitive, RapidJSON also provides helper functions, which just wrap the member functions with free-functions.
The following example does exactly the same as the above one.
~~~cpp
Document d;
SetValueByPointer(d, "/project", "RapidJSON");
SetValueByPointer(d, "/stars", 10);
if (Value* stars = GetValueByPointer(d, "/stars"))
stars->SetInt(stars->GetInt() + 1);
CreateValueByPointer(d, "/a/b/0");
Value& hello = GetValueByPointerWithDefault(d, "/hello", "world");
Value x("C++");
SwapValueByPointer(d, "/hello", x);
bool success = EraseValueByPointer(d, "/a");
assert(success);
~~~
The conventions are shown here for comparison:
1. `Pointer(source).<Method>(root, ...)`
2. `<Method>ValueByPointer(root, Pointer(source), ...)`
3. `<Method>ValueByPointer(root, source, ...)`
# Resolving Pointer {#ResolvingPointer}
`Pointer::Get()` or `GetValueByPointer()` function does not modify the DOM. If the tokens cannot match a value in the DOM, it returns `nullptr`. User can use this to check whether a value exists.
Note that, numerical tokens can represent an array index or member name. The resolving process will match the values according to the types of value.
~~~javascript
{
"0" : 123,
"1" : [456]
}
~~~
1. `"/0"``123`
2. `"/1/0"``456`
The token `"0"` is treated as member name in the first pointer. It is treated as an array index in the second pointer.
The other functions, including `Create()`, `GetWithDefault()`, `Set()` and `Swap()`, will change the DOM. These functions will always succeed. They will create the parent values if they do not exist. If the parent values do not match the tokens, they will also be forced to change their type. Changing the type also mean fully removal of that DOM subtree.
Parsing the above JSON into `d`,
~~~cpp
SetValueByPointer(d, "1/a", 789); // { "0" : 123, "1" : { "a" : 789 } }
~~~
## Resolving Minus Sign Token
Besides, [RFC6901] defines a special token `-` (single minus sign), which represents the pass-the-end element of an array. `Get()` only treats this token as a member name '"-"'. Yet the other functions can resolve this for array, equivalent to calling `Value::PushBack()` to the array.
~~~cpp
Document d;
d.Parse("{\"foo\":[123]}");
SetValueByPointer(d, "/foo/-", 456); // { "foo" : [123, 456] }
SetValueByPointer(d, "/-", 789); // { "foo" : [123, 456], "-" : 789 }
~~~
## Resolving Document and Value
When using `p.Get(root)` or `GetValueByPointer(root, p)`, `root` is a (const) `Value&`. That means, it can be a subtree of the DOM.
The other functions have two groups of signature. One group uses `Document& document` as parameter, another one uses `Value& root`. The first group uses `document.GetAllocator()` for creating values. And the second group needs user to supply an allocator, like the functions in DOM.
All examples above do not require an allocator parameter, because the first parameter is a `Document&`. But if you want to resolve a pointer to a subtree, you need to supply the allocator as in the following example:
~~~cpp
class Person {
public:
Person() {
document_ = new Document();
// CreateValueByPointer() here no need allocator
SetLocation(CreateValueByPointer(*document_, "/residence"), ...);
SetLocation(CreateValueByPointer(*document_, "/office"), ...);
};
private:
void SetLocation(Value& location, const char* country, const char* addresses[2]) {
Value::Allocator& a = document_->GetAllocator();
// SetValueByPointer() here need allocator
SetValueByPointer(location, "/country", country, a);
SetValueByPointer(location, "/address/0", address[0], a);
SetValueByPointer(location, "/address/1", address[1], a);
}
// ...
Document* document_;
};
~~~
`Erase()` or `EraseValueByPointer()` does not need allocator. And they return `true` if the value is erased successfully.
# Error Handling {#ErrorHandling}
A `Pointer` parses a source string in its constructor. If there is parsing error, `Pointer::IsValid()` returns `false`. And you can use `Pointer::GetParseErrorCode()` and `GetParseErrorOffset()` to retrieve the error information.
Note that, all resolving functions assumes valid pointer. Resolving with an invalid pointer causes assertion failure.
# URI Fragment Representation {#URIFragment}
In addition to the string representation of JSON pointer that we are using till now, [RFC6901] also defines the URI fragment representation of JSON pointer. URI fragment is specified in [RFC3986] "Uniform Resource Identifier (URI): Generic Syntax".
The main differences are that a the URI fragment always has a `#` (pound sign) in the beginning, and some characters are encoded by percent-encoding in UTF-8 sequence. For example, the following table shows different C/C++ string literals of different representations.
String Representation | URI Fragment Representation | Pointer Tokens (UTF-8)
----------------------|-----------------------------|------------------------
`"/foo/0"` | `"#/foo/0"` | `{"foo", 0}`
`"/a~1b"` | `"#/a~1b"` | `{"a/b"}`
`"/m~0n"` | `"#/m~0n"` | `{"m~n"}`
`"/ "` | `"#/%20"` | `{" "}`
`"/\0"` | `"#/%00"` | `{"\0"}`
`"/€"` | `"#/%E2%82%AC"` | `{"€"}`
RapidJSON fully support URI fragment representation. It automatically detects the pound sign during parsing.
# Stringify
You may also stringify a `Pointer` to a string or other output streams. This can be done by:
~~~
Pointer p(...);
StringBuffer sb;
p.Stringify(sb);
std::cout << sb.GetString() << std::endl;
~~~
It can also stringify to URI fragment reprsentation by `StringifyUriFragment()`.
# User-Supplied Tokens {#UserSuppliedTokens}
If a pointer will be resolved multiple times, it should be constructed once, and then apply it to different DOMs or in different times. This reduce time and memory allocation for constructing `Pointer` multiple times.
We can go one step further, to completely eliminate the parsing process and dynamic memory allocation, we can establish the token array directly:
~~~cpp
#define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex }
#define INDEX(i) { #i, sizeof(#i) - 1, i }
static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) };
static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
// Equivalent to static const Pointer p("/foo/123");
~~~
This may be useful for memory constrained systems.
[RFC3986]: https://tools.ietf.org/html/rfc3986
[RFC6901]: https://tools.ietf.org/html/rfc6901

View File

@ -0,0 +1,234 @@
# Pointer
(本功能于 v1.1.0 发布)
JSON Pointer 是一个标准化([RFC6901])的方式去选取一个 JSON DocumentDOM中的值。这类似于 XML 的 XPath。然而JSON Pointer 简单得多,而且每个 JSON Pointer 仅指向单个值。
使用 RapidJSON 的 JSON Pointer 实现能简化一些 DOM 的操作。
[TOC]
# JSON Pointer {#JsonPointer}
一个 JSON Pointer 由一串零至多个token 所组成,每个 token 都有 `/` 前缀。每个 token 可以是一个字符串或数字。例如,给定一个 JSON
~~~javascript
{
"foo" : ["bar", "baz"],
"pi" : 3.1416
}
~~~
以下的 JSON Pointer 解析为:
1. `"/foo"``[ "bar", "baz" ]`
2. `"/foo/0"``"bar"`
3. `"/foo/1"``"baz"`
4. `"/pi"``3.1416`
要注意,一个空 JSON Pointer `""` (零个 token解析为整个 JSON。
# 基本使用方法 {#BasicUsage}
以下的代码范例不解自明。
~~~cpp
#include "rapidjson/pointer.h"
// ...
Document d;
// 使用 Set() 创建 DOM
Pointer("/project").Set(d, "RapidJSON");
Pointer("/stars").Set(d, 10);
// { "project" : "RapidJSON", "stars" : 10 }
// 使用 Get() 访问 DOM。若该值不存在则返回 nullptr。
if (Value* stars = Pointer("/stars").Get(d))
stars->SetInt(stars->GetInt() + 1);
// { "project" : "RapidJSON", "stars" : 11 }
// Set() 和 Create() 自动生成父值(如果它们不存在)。
Pointer("/a/b/0").Create(d);
// { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] } }
// GetWithDefault() 返回引用。若该值不存在则会深拷贝缺省值。
Value& hello = Pointer("/hello").GetWithDefault(d, "world");
// { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] }, "hello" : "world" }
// Swap() 和 Set() 相似
Value x("C++");
Pointer("/hello").Swap(d, x);
// { "project" : "RapidJSON", "stars" : 11, "a" : { "b" : [ null ] }, "hello" : "C++" }
// x 变成 "world"
// 删去一个成员或元素,若值存在返回 true
bool success = Pointer("/a").Erase(d);
assert(success);
// { "project" : "RapidJSON", "stars" : 10 }
~~~
# 辅助函数 {#HelperFunctions}
由于面向对象的调用习惯可能不符直觉RapidJSON 也提供了一些辅助函数,它们把成员函数包装成自由函数。
以下的例子与上面例子所做的事情完全相同。
~~~cpp
Document d;
SetValueByPointer(d, "/project", "RapidJSON");
SetValueByPointer(d, "/stars", 10);
if (Value* stars = GetValueByPointer(d, "/stars"))
stars->SetInt(stars->GetInt() + 1);
CreateValueByPointer(d, "/a/b/0");
Value& hello = GetValueByPointerWithDefault(d, "/hello", "world");
Value x("C++");
SwapValueByPointer(d, "/hello", x);
bool success = EraseValueByPointer(d, "/a");
assert(success);
~~~
以下对比 3 种调用方式:
1. `Pointer(source).<Method>(root, ...)`
2. `<Method>ValueByPointer(root, Pointer(source), ...)`
3. `<Method>ValueByPointer(root, source, ...)`
# 解析 Pointer {#ResolvingPointer}
`Pointer::Get()``GetValueByPointer()` 函数并不修改 DOM。若那些 token 不能匹配 DOM 里的值,这些函数便返回 `nullptr`。使用者可利用这个方法来检查一个值是否存在。
注意,数值 token 可表示数组索引或成员名字。解析过程中会按值的类型来匹配。
~~~javascript
{
"0" : 123,
"1" : [456]
}
~~~
1. `"/0"``123`
2. `"/1/0"``456`
Token `"0"` 在第一个 pointer 中被当作成员名字。它在第二个 pointer 中被当作成数组索引。
其他函数会改变 DOM包括 `Create()``GetWithDefault()``Set()``Swap()`。这些函数总是成功的。若一些父值不存在,就会创建它们。若父值类型不匹配 token也会强行改变其类型。改变类型也意味着完全移除其 DOM 子树的内容。
例如,把上面的 JSON 解译至 `d` 之后,
~~~cpp
SetValueByPointer(d, "1/a", 789); // { "0" : 123, "1" : { "a" : 789 } }
~~~
## 解析负号 token
另外,[RFC6901] 定义了一个特殊 token `-` (单个负号),用于表示数组最后元素的下一个元素。 `Get()` 只会把此 token 当作成员名字 '"-"'。而其他函数则会以此解析数组,等同于对数组调用 `Value::PushBack()`
~~~cpp
Document d;
d.Parse("{\"foo\":[123]}");
SetValueByPointer(d, "/foo/-", 456); // { "foo" : [123, 456] }
SetValueByPointer(d, "/-", 789); // { "foo" : [123, 456], "-" : 789 }
~~~
## 解析 Document 及 Value
当使用 `p.Get(root)``GetValueByPointer(root, p)``root` 是一个(常数) `Value&`。这意味着,它也可以是 DOM 里的一个子树。
其他函数有两组签名。一组使用 `Document& document` 作为参数,另一组使用 `Value& root`。第一组使用 `document.GetAllocator()` 去创建值,而第二组则需要使用者提供一个 allocator如同 DOM 里的函数。
以上例子都不需要 allocator 参数,因为它的第一个参数是 `Document&`。但如果你需要对一个子树进行解析,就需要如下面的例子般提供 allocator
~~~cpp
class Person {
public:
Person() {
document_ = new Document();
// CreateValueByPointer() here no need allocator
SetLocation(CreateValueByPointer(*document_, "/residence"), ...);
SetLocation(CreateValueByPointer(*document_, "/office"), ...);
};
private:
void SetLocation(Value& location, const char* country, const char* addresses[2]) {
Value::Allocator& a = document_->GetAllocator();
// SetValueByPointer() here need allocator
SetValueByPointer(location, "/country", country, a);
SetValueByPointer(location, "/address/0", address[0], a);
SetValueByPointer(location, "/address/1", address[1], a);
}
// ...
Document* document_;
};
~~~
`Erase()``EraseValueByPointer()` 不需要 allocator。而且它们成功删除值之后会返回 `true`
# 错误处理 {#ErrorHandling}
`Pointer` 在其建构函数里会解译源字符串。若有解析错误,`Pointer::IsValid()` 返回 `false`。你可使用 `Pointer::GetParseErrorCode()``GetParseErrorOffset()` 去获取错信息。
要注意的是,所有解析函数都假设 pointer 是合法的。对一个非法 pointer 解析会做成断言失败。
# URI 片段表示方式 {#URIFragment}
除了我们一直在使用的字符串方式表示 JSON pointer[RFC6901] 也定义了一个 JSON Pointer 的 URI 片段fragment表示方式。URI 片段是定义于 [RFC3986] "Uniform Resource Identifier (URI): Generic Syntax"。
URI 片段的主要分别是必然以 `#` pound sign开头而一些字符也会以百分比编码成 UTF-8 序列。例如,以下的表展示了不同表示法下的 C/C++ 字符串常数。
字符串表示方式 | URI 片段表示方式 | Pointer Tokens UTF-8
----------------------|-----------------------------|------------------------
`"/foo/0"` | `"#/foo/0"` | `{"foo", 0}`
`"/a~1b"` | `"#/a~1b"` | `{"a/b"}`
`"/m~0n"` | `"#/m~0n"` | `{"m~n"}`
`"/ "` | `"#/%20"` | `{" "}`
`"/\0"` | `"#/%00"` | `{"\0"}`
`"/€"` | `"#/%E2%82%AC"` | `{"€"}`
RapidJSON 完全支持 URI 片段表示方式。它在解译时会自动检测 `#` 号。
# 字符串化
你也可以把一个 `Pointer` 字符串化,储存于字符串或其他输出流。例如:
~~~
Pointer p(...);
StringBuffer sb;
p.Stringify(sb);
std::cout << sb.GetString() << std::endl;
~~~
使用 `StringifyUriFragment()` 可以把 pointer 字符串化为 URI 片段表示法。
# 使用者提供的 tokens {#UserSuppliedTokens}
若一个 pointer 会用于多次解析,它应该只被创建一次,然后再施于不同的 DOM ,或在不同时间做解析。这样可以避免多次创键 `Pointer`,节省时间和内存分配。
我们甚至可以再更进一步,完全消去解析过程及动态内存分配。我们可以直接生成 token 数组:
~~~cpp
#define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex }
#define INDEX(i) { #i, sizeof(#i) - 1, i }
static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) };
static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
// Equivalent to static const Pointer p("/foo/123");
~~~
这种做法可能适合内存受限的系统。
[RFC3986]: https://tools.ietf.org/html/rfc3986
[RFC6901]: https://tools.ietf.org/html/rfc6901

486
thirdparty/rapidjson-1.1.0/doc/sax.md vendored Normal file
View File

@ -0,0 +1,486 @@
# SAX
The term "SAX" originated from [Simple API for XML](http://en.wikipedia.org/wiki/Simple_API_for_XML). We borrowed this term for JSON parsing and generation.
In RapidJSON, `Reader` (typedef of `GenericReader<...>`) is the SAX-style parser for JSON, and `Writer` (typedef of `GenericWriter<...>`) is the SAX-style generator for JSON.
[TOC]
# Reader {#Reader}
`Reader` parses a JSON from a stream. While it reads characters from the stream, it analyze the characters according to the syntax of JSON, and publish events to a handler.
For example, here is a JSON.
~~~~~~~~~~js
{
"hello": "world",
"t": true ,
"f": false,
"n": null,
"i": 123,
"pi": 3.1416,
"a": [1, 2, 3, 4]
}
~~~~~~~~~~
While a `Reader` parses this JSON, it publishes the following events to the handler sequentially:
~~~~~~~~~~
StartObject()
Key("hello", 5, true)
String("world", 5, true)
Key("t", 1, true)
Bool(true)
Key("f", 1, true)
Bool(false)
Key("n", 1, true)
Null()
Key("i")
UInt(123)
Key("pi")
Double(3.1416)
Key("a")
StartArray()
Uint(1)
Uint(2)
Uint(3)
Uint(4)
EndArray(4)
EndObject(7)
~~~~~~~~~~
These events can be easily matched with the JSON, except some event parameters need further explanation. Let's see the `simplereader` example which produces exactly the same output as above:
~~~~~~~~~~cpp
#include "rapidjson/reader.h"
#include <iostream>
using namespace rapidjson;
using namespace std;
struct MyHandler : public BaseReaderHandler<UTF8<>, MyHandler> {
bool Null() { cout << "Null()" << endl; return true; }
bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; }
bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; }
bool Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; return true; }
bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; }
bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; }
bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; }
bool String(const char* str, SizeType length, bool copy) {
cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool StartObject() { cout << "StartObject()" << endl; return true; }
bool Key(const char* str, SizeType length, bool copy) {
cout << "Key(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
bool StartArray() { cout << "StartArray()" << endl; return true; }
bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
};
void main() {
const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
MyHandler handler;
Reader reader;
StringStream ss(json);
reader.Parse(ss, handler);
}
~~~~~~~~~~
Note that, RapidJSON uses template to statically bind the `Reader` type and the handler type, instead of using class with virtual functions. This paradigm can improve the performance by inlining functions.
## Handler {#Handler}
As the previous example showed, user needs to implement a handler, which consumes the events (function calls) from `Reader`. The handler must contain the following member functions.
~~~~~~~~~~cpp
class Handler {
bool Null();
bool Bool(bool b);
bool Int(int i);
bool Uint(unsigned i);
bool Int64(int64_t i);
bool Uint64(uint64_t i);
bool Double(double d);
bool RawNumber(const Ch* str, SizeType length, bool copy);
bool String(const Ch* str, SizeType length, bool copy);
bool StartObject();
bool Key(const Ch* str, SizeType length, bool copy);
bool EndObject(SizeType memberCount);
bool StartArray();
bool EndArray(SizeType elementCount);
};
~~~~~~~~~~
`Null()` is called when the `Reader` encounters a JSON null value.
`Bool(bool)` is called when the `Reader` encounters a JSON true or false value.
When the `Reader` encounters a JSON number, it chooses a suitable C++ type mapping. And then it calls *one* function out of `Int(int)`, `Uint(unsigned)`, `Int64(int64_t)`, `Uint64(uint64_t)` and `Double(double)`. If `kParseNumbersAsStrings` is enabled, `Reader` will always calls `RawNumber()` instead.
`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `'\0'` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And beware that, the character type depends on the target encoding, which will be explained later.
When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `Key()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeats until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler, user may not need this parameter.
Array is similar to object but simpler. At the beginning of an array, the `Reader` calls `BeginArary()`. If there is elements, it calls functions according to the types of element. Similarly, in the last call `EndArray(SizeType elementCount)`, the parameter `elementCount` is just an aid for the handler.
Every handler functions returns a `bool`. Normally it should returns `true`. If the handler encounters an error, it can return `false` to notify event publisher to stop further processing.
For example, when we parse a JSON with `Reader` and the handler detected that the JSON does not conform to the required schema, then the handler can return `false` and let the `Reader` stop further parsing. And the `Reader` will be in error state with error code `kParseErrorTermination`.
## GenericReader {#GenericReader}
As mentioned before, `Reader` is a typedef of a template class `GenericReader`:
~~~~~~~~~~cpp
namespace rapidjson {
template <typename SourceEncoding, typename TargetEncoding, typename Allocator = MemoryPoolAllocator<> >
class GenericReader {
// ...
};
typedef GenericReader<UTF8<>, UTF8<> > Reader;
} // namespace rapidjson
~~~~~~~~~~
The `Reader` uses UTF-8 as both source and target encoding. The source encoding means the encoding in the JSON stream. The target encoding means the encoding of the `str` parameter in `String()` calls. For example, to parse a UTF-8 stream and outputs UTF-16 string events, you can define a reader by:
~~~~~~~~~~cpp
GenericReader<UTF8<>, UTF16<> > reader;
~~~~~~~~~~
Note that, the default character type of `UTF16` is `wchar_t`. So this `reader`needs to call `String(const wchar_t*, SizeType, bool)` of the handler.
The third template parameter `Allocator` is the allocator type for internal data structure (actually a stack).
## Parsing {#SaxParsing}
The one and only one function of `Reader` is to parse JSON.
~~~~~~~~~~cpp
template <unsigned parseFlags, typename InputStream, typename Handler>
bool Parse(InputStream& is, Handler& handler);
// with parseFlags = kDefaultParseFlags
template <typename InputStream, typename Handler>
bool Parse(InputStream& is, Handler& handler);
~~~~~~~~~~
If an error occurs during parsing, it will return `false`. User can also calls `bool HasParseEror()`, `ParseErrorCode GetParseErrorCode()` and `size_t GetErrorOffset()` to obtain the error states. Actually `Document` uses these `Reader` functions to obtain parse errors. Please refer to [DOM](doc/dom.md) for details about parse error.
# Writer {#Writer}
`Reader` converts (parses) JSON into events. `Writer` does exactly the opposite. It converts events into JSON.
`Writer` is very easy to use. If your application only need to converts some data into JSON, it may be a good choice to use `Writer` directly, instead of building a `Document` and then stringifying it with a `Writer`.
In `simplewriter` example, we do exactly the reverse of `simplereader`.
~~~~~~~~~~cpp
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
using namespace std;
void main() {
StringBuffer s;
Writer<StringBuffer> writer(s);
writer.StartObject();
writer.Key("hello");
writer.String("world");
writer.Key("t");
writer.Bool(true);
writer.Key("f");
writer.Bool(false);
writer.Key("n");
writer.Null();
writer.Key("i");
writer.Uint(123);
writer.Key("pi");
writer.Double(3.1416);
writer.Key("a");
writer.StartArray();
for (unsigned i = 0; i < 4; i++)
writer.Uint(i);
writer.EndArray();
writer.EndObject();
cout << s.GetString() << endl;
}
~~~~~~~~~~
~~~~~~~~~~
{"hello":"world","t":true,"f":false,"n":null,"i":123,"pi":3.1416,"a":[0,1,2,3]}
~~~~~~~~~~
There are two `String()` and `Key()` overloads. One is the same as defined in handler concept with 3 parameters. It can handle string with null characters. Another one is the simpler version used in the above example.
Note that, the example code does not pass any parameters in `EndArray()` and `EndObject()`. An `SizeType` can be passed but it will be simply ignored by `Writer`.
You may doubt that, why not just using `sprintf()` or `std::stringstream` to build a JSON?
There are various reasons:
1. `Writer` must output a well-formed JSON. If there is incorrect event sequence (e.g. `Int()` just after `StartObject()`), it generates assertion fail in debug mode.
2. `Writer::String()` can handle string escaping (e.g. converting code point `U+000A` to `\n`) and Unicode transcoding.
3. `Writer` handles number output consistently.
4. `Writer` implements the event handler concept. It can be used to handle events from `Reader`, `Document` or other event publisher.
5. `Writer` can be optimized for different platforms.
Anyway, using `Writer` API is even simpler than generating a JSON by ad hoc methods.
## Template {#WriterTemplate}
`Writer` has a minor design difference to `Reader`. `Writer` is a template class, not a typedef. There is no `GenericWriter`. The following is the declaration.
~~~~~~~~~~cpp
namespace rapidjson {
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = CrtAllocator<>, unsigned writeFlags = kWriteDefaultFlags>
class Writer {
public:
Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth)
// ...
};
} // namespace rapidjson
~~~~~~~~~~
The `OutputStream` template parameter is the type of output stream. It cannot be deduced and must be specified by user.
The `SourceEncoding` template parameter specifies the encoding to be used in `String(const Ch*, ...)`.
The `TargetEncoding` template parameter specifies the encoding in the output stream.
The `Allocator` is the type of allocator, which is used for allocating internal data structure (a stack).
The `writeFlags` are combination of the following bit-flags:
Parse flags | Meaning
------------------------------|-----------------------------------
`kWriteNoFlags` | No flag is set.
`kWriteDefaultFlags` | Default write flags. It is equal to macro `RAPIDJSON_WRITE_DEFAULT_FLAGS`, which is defined as `kWriteNoFlags`.
`kWriteValidateEncodingFlag` | Validate encoding of JSON strings.
`kWriteNanAndInfFlag` | Allow writing of `Infinity`, `-Infinity` and `NaN`.
Besides, the constructor of `Writer` has a `levelDepth` parameter. This parameter affects the initial memory allocated for storing information per hierarchy level.
## PrettyWriter {#PrettyWriter}
While the output of `Writer` is the most condensed JSON without white-spaces, suitable for network transfer or storage, it is not easily readable by human.
Therefore, RapidJSON provides a `PrettyWriter`, which adds indentation and line feeds in the output.
The usage of `PrettyWriter` is exactly the same as `Writer`, expect that `PrettyWriter` provides a `SetIndent(Ch indentChar, unsigned indentCharCount)` function. The default is 4 spaces.
## Completeness and Reset {#CompletenessReset}
A `Writer` can only output a single JSON, which can be any JSON type at the root. Once the singular event for root (e.g. `String()`), or the last matching `EndObject()` or `EndArray()` event, is handled, the output JSON is well-formed and complete. User can detect this state by calling `Writer::IsComplete()`.
When a JSON is complete, the `Writer` cannot accept any new events. Otherwise the output will be invalid (i.e. having more than one root). To reuse the `Writer` object, user can call `Writer::Reset(OutputStream& os)` to reset all internal states of the `Writer` with a new output stream.
# Techniques {#SaxTechniques}
## Parsing JSON to Custom Data Structure {#CustomDataStructure}
`Document`'s parsing capability is completely based on `Reader`. Actually `Document` is a handler which receives events from a reader to build a DOM during parsing.
User may uses `Reader` to build other data structures directly. This eliminates building of DOM, thus reducing memory and improving performance.
In the following `messagereader` example, `ParseMessages()` parses a JSON which should be an object with key-string pairs.
~~~~~~~~~~cpp
#include "rapidjson/reader.h"
#include "rapidjson/error/en.h"
#include <iostream>
#include <string>
#include <map>
using namespace std;
using namespace rapidjson;
typedef map<string, string> MessageMap;
struct MessageHandler
: public BaseReaderHandler<UTF8<>, MessageHandler> {
MessageHandler() : state_(kExpectObjectStart) {
}
bool StartObject() {
switch (state_) {
case kExpectObjectStart:
state_ = kExpectNameOrObjectEnd;
return true;
default:
return false;
}
}
bool String(const char* str, SizeType length, bool) {
switch (state_) {
case kExpectNameOrObjectEnd:
name_ = string(str, length);
state_ = kExpectValue;
return true;
case kExpectValue:
messages_.insert(MessageMap::value_type(name_, string(str, length)));
state_ = kExpectNameOrObjectEnd;
return true;
default:
return false;
}
}
bool EndObject(SizeType) { return state_ == kExpectNameOrObjectEnd; }
bool Default() { return false; } // All other events are invalid.
MessageMap messages_;
enum State {
kExpectObjectStart,
kExpectNameOrObjectEnd,
kExpectValue,
}state_;
std::string name_;
};
void ParseMessages(const char* json, MessageMap& messages) {
Reader reader;
MessageHandler handler;
StringStream ss(json);
if (reader.Parse(ss, handler))
messages.swap(handler.messages_); // Only change it if success.
else {
ParseErrorCode e = reader.GetParseErrorCode();
size_t o = reader.GetErrorOffset();
cout << "Error: " << GetParseError_En(e) << endl;;
cout << " at offset " << o << " near '" << string(json).substr(o, 10) << "...'" << endl;
}
}
int main() {
MessageMap messages;
const char* json1 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }";
cout << json1 << endl;
ParseMessages(json1, messages);
for (MessageMap::const_iterator itr = messages.begin(); itr != messages.end(); ++itr)
cout << itr->first << ": " << itr->second << endl;
cout << endl << "Parse a JSON with invalid schema." << endl;
const char* json2 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\", \"foo\" : {} }";
cout << json2 << endl;
ParseMessages(json2, messages);
return 0;
}
~~~~~~~~~~
~~~~~~~~~~
{ "greeting" : "Hello!", "farewell" : "bye-bye!" }
farewell: bye-bye!
greeting: Hello!
Parse a JSON with invalid schema.
{ "greeting" : "Hello!", "farewell" : "bye-bye!", "foo" : {} }
Error: Terminate parsing due to Handler error.
at offset 59 near '} }...'
~~~~~~~~~~
The first JSON (`json1`) was successfully parsed into `MessageMap`. Since `MessageMap` is a `std::map`, the printing order are sorted by the key. This order is different from the JSON's order.
In the second JSON (`json2`), `foo`'s value is an empty object. As it is an object, `MessageHandler::StartObject()` will be called. However, at that moment `state_ = kExpectValue`, so that function returns `false` and cause the parsing process be terminated. The error code is `kParseErrorTermination`.
## Filtering of JSON {#Filtering}
As mentioned earlier, `Writer` can handle the events published by `Reader`. `condense` example simply set a `Writer` as handler of a `Reader`, so it can remove all white-spaces in JSON. `pretty` example uses the same relationship, but replacing `Writer` by `PrettyWriter`. So `pretty` can be used to reformat a JSON with indentation and line feed.
Actually, we can add intermediate layer(s) to filter the contents of JSON via these SAX-style API. For example, `capitalize` example capitalize all strings in a JSON.
~~~~~~~~~~cpp
#include "rapidjson/reader.h"
#include "rapidjson/writer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/error/en.h"
#include <vector>
#include <cctype>
using namespace rapidjson;
template<typename OutputHandler>
struct CapitalizeFilter {
CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() {
}
bool Null() { return out_.Null(); }
bool Bool(bool b) { return out_.Bool(b); }
bool Int(int i) { return out_.Int(i); }
bool Uint(unsigned u) { return out_.Uint(u); }
bool Int64(int64_t i) { return out_.Int64(i); }
bool Uint64(uint64_t u) { return out_.Uint64(u); }
bool Double(double d) { return out_.Double(d); }
bool RawNumber(const char* str, SizeType length, bool copy) { return out_.RawNumber(str, length, copy); }
bool String(const char* str, SizeType length, bool) {
buffer_.clear();
for (SizeType i = 0; i < length; i++)
buffer_.push_back(std::toupper(str[i]));
return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string
}
bool StartObject() { return out_.StartObject(); }
bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); }
bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); }
bool StartArray() { return out_.StartArray(); }
bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); }
OutputHandler& out_;
std::vector<char> buffer_;
};
int main(int, char*[]) {
// Prepare JSON reader and input stream.
Reader reader;
char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare JSON writer and output stream.
char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output.
CapitalizeFilter<Writer<FileWriteStream> > filter(writer);
if (!reader.Parse(is, filter)) {
fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
return 0;
}
~~~~~~~~~~
Note that, it is incorrect to simply capitalize the JSON as a string. For example:
~~~~~~~~~~
["Hello\nWorld"]
~~~~~~~~~~
Simply capitalizing the whole JSON would contain incorrect escape character:
~~~~~~~~~~
["HELLO\NWORLD"]
~~~~~~~~~~
The correct result by `capitalize`:
~~~~~~~~~~
["HELLO\nWORLD"]
~~~~~~~~~~
More complicated filters can be developed. However, since SAX-style API can only provide information about a single event at a time, user may need to book-keeping the contextual information (e.g. the path from root value, storage of other related values). Some processing may be easier to be implemented in DOM than SAX.

View File

@ -0,0 +1,487 @@
# SAX
"SAX" 此术语源于 [Simple API for XML](http://en.wikipedia.org/wiki/Simple_API_for_XML)。我们借了此术语去套用在 JSON 的解析及生成。
在 RapidJSON 中,`Reader``GenericReader<...>` 的 typedef是 JSON 的 SAX 风格解析器,而 `Writer``GenericWriter<...>` 的 typedef则是 JSON 的 SAX 风格生成器。
[TOC]
# Reader {#Reader}
`Reader` 从输入流解析一个 JSON。当它从流中读取字符时它会基于 JSON 的语法去分析字符,并向处理器发送事件。
例如,以下是一个 JSON。
~~~~~~~~~~js
{
"hello": "world",
"t": true ,
"f": false,
"n": null,
"i": 123,
"pi": 3.1416,
"a": [1, 2, 3, 4]
}
~~~~~~~~~~
当一个 `Reader` 解析此 JSON 时,它会顺序地向处理器发送以下的事件:
~~~~~~~~~~
StartObject()
Key("hello", 5, true)
String("world", 5, true)
Key("t", 1, true)
Bool(true)
Key("f", 1, true)
Bool(false)
Key("n", 1, true)
Null()
Key("i")
UInt(123)
Key("pi")
Double(3.1416)
Key("a")
StartArray()
Uint(1)
Uint(2)
Uint(3)
Uint(4)
EndArray(4)
EndObject(7)
~~~~~~~~~~
除了一些事件参数需要再作解释,这些事件可以轻松地与 JSON 对上。我们可以看看 `simplereader` 例子怎样产生和以上完全相同的结果:
~~~~~~~~~~cpp
#include "rapidjson/reader.h"
#include <iostream>
using namespace rapidjson;
using namespace std;
struct MyHandler : public BaseReaderHandler<UTF8<>, MyHandler> {
bool Null() { cout << "Null()" << endl; return true; }
bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; }
bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; }
bool Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; return true; }
bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; }
bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; }
bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; }
bool String(const char* str, SizeType length, bool copy) {
cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool StartObject() { cout << "StartObject()" << endl; return true; }
bool Key(const char* str, SizeType length, bool copy) {
cout << "Key(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
bool StartArray() { cout << "StartArray()" << endl; return true; }
bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
};
void main() {
const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
MyHandler handler;
Reader reader;
StringStream ss(json);
reader.Parse(ss, handler);
}
~~~~~~~~~~
注意 RapidJSON 使用模板去静态挷定 `Reader` 类型及处理器的类形,而不是使用含虚函数的类。这个范式可以通过把函数内联而改善性能。
## 处理器 {#Handler}
如前例所示使用者需要实现一个处理器handler用于处理来自 `Reader` 的事件(函数调用)。处理器必须包含以下的成员函数。
~~~~~~~~~~cpp
class Handler {
bool Null();
bool Bool(bool b);
bool Int(int i);
bool Uint(unsigned i);
bool Int64(int64_t i);
bool Uint64(uint64_t i);
bool Double(double d);
bool RawNumber(const Ch* str, SizeType length, bool copy);
bool String(const Ch* str, SizeType length, bool copy);
bool StartObject();
bool Key(const Ch* str, SizeType length, bool copy);
bool EndObject(SizeType memberCount);
bool StartArray();
bool EndArray(SizeType elementCount);
};
~~~~~~~~~~
`Reader` 遇到 JSON null 值时会调用 `Null()`
`Reader` 遇到 JSON true 或 false 值时会调用 `Bool(bool)`
`Reader` 遇到 JSON number它会选择一个合适的 C++ 类型映射,然后调用 `Int(int)``Uint(unsigned)``Int64(int64_t)``Uint64(uint64_t)``Double(double)`* 其中之一个 *。 若开启了 `kParseNumbersAsStrings` 选项,`Reader` 便会改为调用 `RawNumber()`
`Reader` 遇到 JSON string它会调用 `String(const char* str, SizeType length, bool copy)`。第一个参数是字符串的指针。第二个参数是字符串的长度(不包含空终止符号)。注意 RapidJSON 支持字串中含有空字符 `'\0'`。若出现这种情况,便会有 `strlen(str) < length`。最后的 `copy` 参数表示处理器是否需要复制该字符串。在正常解析时,`copy = true`。仅当使用原位解析时,`copy = false`。此外,还要注意字符的类型与目标编码相关,我们稍后会再谈这一点。
`Reader` 遇到 JSON object 的开始之时,它会调用 `StartObject()`。JSON 的 object 是一个键值对(成员)的集合。若 object 包含成员,它会先为成员的名字调用 `Key()`,然后再按值的类型调用函数。它不断调用这些键值对,直至最终调用 `EndObject(SizeType memberCount)`。注意 `memberCount` 参数对处理器来说只是协助性质,使用者可能不需要此参数。
JSON array 与 object 相似,但更简单。在 array 开始时,`Reader` 会调用 `BeginArary()`。若 array 含有元素,它会按元素的类型来读用函数。相似地,最后它会调用 `EndArray(SizeType elementCount)`,其中 `elementCount` 参数对处理器来说只是协助性质。
每个处理器函数都返回一个 `bool`。正常它们应返回 `true`。若处理器遇到错误,它可以返回 `false` 去通知事件发送方停止继续处理。
例如,当我们用 `Reader` 解析一个 JSON 时,处理器检测到该 JSON 并不符合所需的 schema那么处理器可以返回 `false`,令 `Reader` 停止之后的解析工作。而 `Reader` 会进入一个错误状态,并以 `kParseErrorTermination` 错误码标识。
## GenericReader {#GenericReader}
前面提及,`Reader``GenericReader` 模板类的 typedef
~~~~~~~~~~cpp
namespace rapidjson {
template <typename SourceEncoding, typename TargetEncoding, typename Allocator = MemoryPoolAllocator<> >
class GenericReader {
// ...
};
typedef GenericReader<UTF8<>, UTF8<> > Reader;
} // namespace rapidjson
~~~~~~~~~~
`Reader` 使用 UTF-8 作为来源及目标编码。来源编码是指 JSON 流的编码。目标编码是指 `String()``str` 参数所用的编码。例如,要解析一个 UTF-8 流并输出至 UTF-16 string 事件,你需要这么定义一个 reader
~~~~~~~~~~cpp
GenericReader<UTF8<>, UTF16<> > reader;
~~~~~~~~~~
注意到 `UTF16` 的缺省类型是 `wchar_t`。因此这个 `reader` 需要调用处理器的 `String(const wchar_t*, SizeType, bool)`
第三个模板参数 `Allocator` 是内部数据结构(实际上是一个堆栈)的分配器类型。
## 解析 {#SaxParsing}
`Reader` 的唯一功能就是解析 JSON。
~~~~~~~~~~cpp
template <unsigned parseFlags, typename InputStream, typename Handler>
bool Parse(InputStream& is, Handler& handler);
// 使用 parseFlags = kDefaultParseFlags
template <typename InputStream, typename Handler>
bool Parse(InputStream& is, Handler& handler);
~~~~~~~~~~
若在解析中出现错误,它会返回 `false`。使用者可调用 `bool HasParseEror()`, `ParseErrorCode GetParseErrorCode()``size_t GetErrorOffset()` 获取错误状态。实际上 `Document` 使用这些 `Reader` 函数去获取解析错误。请参考 [DOM](doc/dom.zh-cn.md) 去了解有关解析错误的细节。
# Writer {#Writer}
`Reader` 把 JSON 转换(解析)成为事件。`Writer` 做完全相反的事情。它把事件转换成 JSON。
`Writer` 是非常容易使用的。若你的应用程序只需把一些数据转换成 JSON可能直接使用 `Writer`,会比建立一个 `Document` 然后用 `Writer` 把它转换成 JSON 更加方便。
`simplewriter` 例子里,我们做 `simplereader` 完全相反的事情。
~~~~~~~~~~cpp
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
using namespace std;
void main() {
StringBuffer s;
Writer<StringBuffer> writer(s);
writer.StartObject();
writer.Key("hello");
writer.String("world");
writer.Key("t");
writer.Bool(true);
writer.Key("f");
writer.Bool(false);
writer.Key("n");
writer.Null();
writer.Key("i");
writer.Uint(123);
writer.Key("pi");
writer.Double(3.1416);
writer.Key("a");
writer.StartArray();
for (unsigned i = 0; i < 4; i++)
writer.Uint(i);
writer.EndArray();
writer.EndObject();
cout << s.GetString() << endl;
}
~~~~~~~~~~
~~~~~~~~~~
{"hello":"world","t":true,"f":false,"n":null,"i":123,"pi":3.1416,"a":[0,1,2,3]}
~~~~~~~~~~
`String()``Key()` 各有两个重载。一个是如处理器 concept 般,有 3 个参数。它能处理含空字符的字符串。另一个是如上中使用的较简单版本。
注意到,例子代码中的 `EndArray()``EndObject()` 并没有参数。可以传递一个 `SizeType` 的参数,但它会被 `Writer` 忽略。
你可能会怀疑,为什么不使用 `sprintf()``std::stringstream` 去建立一个 JSON
这有几个原因:
1. `Writer` 必然会输出一个结构良好well-formed的 JSON。若然有错误的事件次序`Int()` 紧随 `StartObject()` 出现),它会在调试模式中产生断言失败。
2. `Writer::String()` 可处理字符串转义(如把码点 `U+000A` 转换成 `\n`)及进行 Unicode 转码。
3. `Writer` 一致地处理 number 的输出。
4. `Writer` 实现了事件处理器 concept。可用于处理来自 `Reader``Document` 或其他事件发生器。
5. `Writer` 可对不同平台进行优化。
无论如何,使用 `Writer` API 去生成 JSON 甚至乎比这些临时方法更简单。
## 模板 {#WriterTemplate}
`Writer``Reader` 有少许设计区别。`Writer` 是一个模板类,而不是一个 typedef。 并没有 `GenericWriter`。以下是 `Writer` 的声明。
~~~~~~~~~~cpp
namespace rapidjson {
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = CrtAllocator<> >
class Writer {
public:
Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth)
// ...
};
} // namespace rapidjson
~~~~~~~~~~
`OutputStream` 模板参数是输出流的类型。它的类型不可以被自动推断,必须由使用者提供。
`SourceEncoding` 模板参数指定了 `String(const Ch*, ...)` 的编码。
`TargetEncoding` 模板参数指定输出流的编码。
`Allocator` 是分配器的类型,用于分配内部数据结构(一个堆栈)。
`writeFlags` 是以下位标志的组合:
写入位标志 | 意义
------------------------------|-----------------------------------
`kWriteNoFlags` | 没有任何标志。
`kWriteDefaultFlags` | 缺省的解析选项。它等于 `RAPIDJSON_WRITE_DEFAULT_FLAGS` 宏,此宏定义为 `kWriteNoFlags`
`kWriteValidateEncodingFlag` | 校验 JSON 字符串的编码。
`kWriteNanAndInfFlag` | 容许写入 `Infinity`, `-Infinity``NaN`
此外,`Writer` 的构造函数有一 `levelDepth` 参数。存储每层阶信息的初始内存分配量受此参数影响。
## PrettyWriter {#PrettyWriter}
`Writer` 所输出的是没有空格字符的最紧凑 JSON适合网络传输或储存但不适合人类阅读。
因此RapidJSON 提供了一个 `PrettyWriter`,它在输出中加入缩进及换行。
`PrettyWriter` 的用法与 `Writer` 几乎一样,不同之处是 `PrettyWriter` 提供了一个 `SetIndent(Ch indentChar, unsigned indentCharCount)` 函数。缺省的缩进是 4 个空格。
## 完整性及重置 {#CompletenessReset}
一个 `Writer` 只可输出单个 JSON其根节点可以是任何 JSON 类型。当处理完单个根节点事件(如 `String()`),或匹配的最后 `EndObject()``EndArray()` 事件,输出的 JSON 是结构完整well-formed及完整的。使用者可调用 `Writer::IsComplete()` 去检测完整性。
当 JSON 完整时,`Writer` 不能再接受新的事件。不然其输出便会是不合法的(例如有超过一个根节点)。为了重新利用 `Writer` 对象,使用者可调用 `Writer::Reset(OutputStream& os)` 去重置其所有内部状态及设置新的输出流。
# 技巧 {#SaxTechniques}
## 解析 JSON 至自定义结构 {#CustomDataStructure}
`Document` 的解析功能完全依靠 `Reader`。实际上 `Document` 是一个处理器,在解析 JSON 时接收事件去建立一个 DOM。
使用者可以直接使用 `Reader` 去建立其他数据结构。这消除了建立 DOM 的步骤,从而减少了内存开销并改善性能。
在以下的 `messagereader` 例子中,`ParseMessages()` 解析一个 JSON该 JSON 应该是一个含键值对的 object。
~~~~~~~~~~cpp
#include "rapidjson/reader.h"
#include "rapidjson/error/en.h"
#include <iostream>
#include <string>
#include <map>
using namespace std;
using namespace rapidjson;
typedef map<string, string> MessageMap;
struct MessageHandler
: public BaseReaderHandler<UTF8<>, MessageHandler> {
MessageHandler() : state_(kExpectObjectStart) {
}
bool StartObject() {
switch (state_) {
case kExpectObjectStart:
state_ = kExpectNameOrObjectEnd;
return true;
default:
return false;
}
}
bool String(const char* str, SizeType length, bool) {
switch (state_) {
case kExpectNameOrObjectEnd:
name_ = string(str, length);
state_ = kExpectValue;
return true;
case kExpectValue:
messages_.insert(MessageMap::value_type(name_, string(str, length)));
state_ = kExpectNameOrObjectEnd;
return true;
default:
return false;
}
}
bool EndObject(SizeType) { return state_ == kExpectNameOrObjectEnd; }
bool Default() { return false; } // All other events are invalid.
MessageMap messages_;
enum State {
kExpectObjectStart,
kExpectNameOrObjectEnd,
kExpectValue,
}state_;
std::string name_;
};
void ParseMessages(const char* json, MessageMap& messages) {
Reader reader;
MessageHandler handler;
StringStream ss(json);
if (reader.Parse(ss, handler))
messages.swap(handler.messages_); // Only change it if success.
else {
ParseErrorCode e = reader.GetParseErrorCode();
size_t o = reader.GetErrorOffset();
cout << "Error: " << GetParseError_En(e) << endl;;
cout << " at offset " << o << " near '" << string(json).substr(o, 10) << "...'" << endl;
}
}
int main() {
MessageMap messages;
const char* json1 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }";
cout << json1 << endl;
ParseMessages(json1, messages);
for (MessageMap::const_iterator itr = messages.begin(); itr != messages.end(); ++itr)
cout << itr->first << ": " << itr->second << endl;
cout << endl << "Parse a JSON with invalid schema." << endl;
const char* json2 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\", \"foo\" : {} }";
cout << json2 << endl;
ParseMessages(json2, messages);
return 0;
}
~~~~~~~~~~
~~~~~~~~~~
{ "greeting" : "Hello!", "farewell" : "bye-bye!" }
farewell: bye-bye!
greeting: Hello!
Parse a JSON with invalid schema.
{ "greeting" : "Hello!", "farewell" : "bye-bye!", "foo" : {} }
Error: Terminate parsing due to Handler error.
at offset 59 near '} }...'
~~~~~~~~~~
第一个 JSON`json1`)被成功地解析至 `MessageMap`。由于 `MessageMap` 是一个 `std::map`,打印次序按键值排序。此次序与 JSON 中的次序不同。
在第二个 JSON`json2`)中,`foo` 的值是一个空 object。由于它是一个 object`MessageHandler::StartObject()` 会被调用。然而,在 `state_ = kExpectValue` 的情况下,该函数会返回 `false`,并导致解析过程终止。错误代码是 `kParseErrorTermination`
## 过滤 JSON {#Filtering}
如前面提及过,`Writer` 可处理 `Reader` 发出的事件。`example/condense/condense.cpp` 例子简单地设置 `Writer` 作为一个 `Reader` 的处理器,因此它能移除 JSON 中的所有空白字符。`example/pretty/pretty.cpp` 例子使用同样的关系,只是以 `PrettyWriter` 取代 `Writer`。因此 `pretty` 能够重新格式化 JSON加入缩进及换行。
实际上,我们可以使用 SAX 风格 API 去加入(多个)中间层去过滤 JSON 的内容。例如 `capitalize` 例子可以把所有 JSON string 改为大写。
~~~~~~~~~~cpp
#include "rapidjson/reader.h"
#include "rapidjson/writer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/error/en.h"
#include <vector>
#include <cctype>
using namespace rapidjson;
template<typename OutputHandler>
struct CapitalizeFilter {
CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() {
}
bool Null() { return out_.Null(); }
bool Bool(bool b) { return out_.Bool(b); }
bool Int(int i) { return out_.Int(i); }
bool Uint(unsigned u) { return out_.Uint(u); }
bool Int64(int64_t i) { return out_.Int64(i); }
bool Uint64(uint64_t u) { return out_.Uint64(u); }
bool Double(double d) { return out_.Double(d); }
bool RawNumber(const char* str, SizeType length, bool copy) { return out_.RawNumber(str, length, copy); }
bool String(const char* str, SizeType length, bool) {
buffer_.clear();
for (SizeType i = 0; i < length; i++)
buffer_.push_back(std::toupper(str[i]));
return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string
}
bool StartObject() { return out_.StartObject(); }
bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); }
bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); }
bool StartArray() { return out_.StartArray(); }
bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); }
OutputHandler& out_;
std::vector<char> buffer_;
};
int main(int, char*[]) {
// Prepare JSON reader and input stream.
Reader reader;
char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare JSON writer and output stream.
char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output.
CapitalizeFilter<Writer<FileWriteStream> > filter(writer);
if (!reader.Parse(is, filter)) {
fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
return 0;
}
~~~~~~~~~~
注意到,不可简单地把 JSON 当作字符串去改为大写。例如:
~~~~~~~~~~
["Hello\nWorld"]
~~~~~~~~~~
简单地把整个 JSON 转为大写的话会产生错误的转义符:
~~~~~~~~~~
["HELLO\NWORLD"]
~~~~~~~~~~
`capitalize` 就会产生正确的结果:
~~~~~~~~~~
["HELLO\nWORLD"]
~~~~~~~~~~
我们还可以开发更复杂的过滤器。然而,由于 SAX 风格 API 在某一时间点只能提供单一事件的信息,使用者需要自行记录一些上下文信息(例如从根节点起的路径、储存其他相关值)。对于处理某些情况,用 DOM 会比 SAX 更容易实现。

237
thirdparty/rapidjson-1.1.0/doc/schema.md vendored Normal file
View File

@ -0,0 +1,237 @@
# Schema
(This feature was released in v1.1.0)
JSON Schema is a draft standard for describing the format of JSON data. The schema itself is also JSON data. By validating a JSON structure with JSON Schema, your code can safely access the DOM without manually checking types, or whether a key exists, etc. It can also ensure that the serialized JSON conform to a specified schema.
RapidJSON implemented a JSON Schema validator for [JSON Schema Draft v4](http://json-schema.org/documentation.html). If you are not familiar with JSON Schema, you may refer to [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/).
[TOC]
## Basic Usage
First of all, you need to parse a JSON Schema into `Document`, and then compile the `Document` into a `SchemaDocument`.
Secondly, construct a `SchemaValidator` with the `SchemaDocument`. It is similar to a `Writer` in the sense of handling SAX events. So, you can use `document.Accept(validator)` to validate a document, and then check the validity.
~~~cpp
#include "rapidjson/schema.h"
// ...
Document sd;
if (!sd.Parse(schemaJson).HasParseError()) {
// the schema is not a valid JSON.
// ...
}
SchemaDocument schema(sd); // Compile a Document to SchemaDocument
// sd is no longer needed here.
Document d;
if (!d.Parse(inputJson).HasParseError()) {
// the input is not a valid JSON.
// ...
}
SchemaValidator validator(schema);
if (!d.Accept(validator)) {
// Input JSON is invalid according to the schema
// Output diagnostic information
StringBuffer sb;
validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
printf("Invalid schema: %s\n", sb.GetString());
printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());
sb.Clear();
validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
printf("Invalid document: %s\n", sb.GetString());
}
~~~
Some notes:
* One `SchemaDocment` can be referenced by multiple `SchemaValidator`s. It will not be modified by `SchemaValidator`s.
* A `SchemaValidator` may be reused to validate multiple documents. To run it for other documents, call `validator.Reset()` first.
## Validation during parsing/serialization
Unlike most JSON Schema validator implementations, RapidJSON provides a SAX-based schema validator. Therefore, you can parse a JSON from a stream while validating it on the fly. If the validator encounters a JSON value that invalidates the supplied schema, the parsing will be terminated immediately. This design is especially useful for parsing large JSON files.
### DOM parsing
For using DOM in parsing, `Document` needs some preparation and finalizing tasks, in addition to receiving SAX events, thus it needs some work to route the reader, validator and the document. `SchemaValidatingReader` is a helper class that doing such work.
~~~cpp
#include "rapidjson/filereadstream.h"
// ...
SchemaDocument schema(sd); // Compile a Document to SchemaDocument
// Use reader to parse the JSON
FILE* fp = fopen("big.json", "r");
FileReadStream is(fp, buffer, sizeof(buffer));
// Parse JSON from reader, validate the SAX events, and store in d.
Document d;
SchemaValidatingReader<kParseDefaultFlags, FileReadStream, UTF8<> > reader(is, schema);
d.Populate(reader);
if (!reader.GetParseResult()) {
// Not a valid JSON
// When reader.GetParseResult().Code() == kParseErrorTermination,
// it may be terminated by:
// (1) the validator found that the JSON is invalid according to schema; or
// (2) the input stream has I/O error.
// Check the validation result
if (!reader.IsValid()) {
// Input JSON is invalid according to the schema
// Output diagnostic information
StringBuffer sb;
reader.GetInvalidSchemaPointer().StringifyUriFragment(sb);
printf("Invalid schema: %s\n", sb.GetString());
printf("Invalid keyword: %s\n", reader.GetInvalidSchemaKeyword());
sb.Clear();
reader.GetInvalidDocumentPointer().StringifyUriFragment(sb);
printf("Invalid document: %s\n", sb.GetString());
}
}
~~~
### SAX parsing
For using SAX in parsing, it is much simpler. If it only need to validate the JSON without further processing, it is simply:
~~~
SchemaValidator validator(schema);
Reader reader;
if (!reader.Parse(stream, validator)) {
if (!validator.IsValid()) {
// ...
}
}
~~~
This is exactly the method used in the [schemavalidator](example/schemavalidator/schemavalidator.cpp) example. The distinct advantage is low memory usage, no matter how big the JSON was (the memory usage depends on the complexity of the schema).
If you need to handle the SAX events further, then you need to use the template class `GenericSchemaValidator` to set the output handler of the validator:
~~~
MyHandler handler;
GenericSchemaValidator<SchemaDocument, MyHandler> validator(schema, handler);
Reader reader;
if (!reader.Parse(ss, validator)) {
if (!validator.IsValid()) {
// ...
}
}
~~~
### Serialization
It is also possible to do validation during serializing. This can ensure the result JSON is valid according to the JSON schema.
~~~
StringBuffer sb;
Writer<StringBuffer> writer(sb);
GenericSchemaValidator<SchemaDocument, Writer<StringBuffer> > validator(s, writer);
if (!d.Accept(validator)) {
// Some problem during Accept(), it may be validation or encoding issues.
if (!validator.IsValid()) {
// ...
}
}
~~~
Of course, if your application only needs SAX-style serialization, it can simply send SAX events to `SchemaValidator` instead of `Writer`.
## Remote Schema
JSON Schema supports [`$ref` keyword](http://spacetelescope.github.io/understanding-json-schema/structuring.html), which is a [JSON pointer](doc/pointer.md) referencing to a local or remote schema. Local pointer is prefixed with `#`, while remote pointer is an relative or absolute URI. For example:
~~~js
{ "$ref": "definitions.json#/address" }
~~~
As `SchemaDocument` does not know how to resolve such URI, it needs a user-provided `IRemoteSchemaDocumentProvider` instance to do so.
~~~
class MyRemoteSchemaDocumentProvider : public IRemoteSchemaDocumentProvider {
public:
virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeTyp length) {
// Resolve the uri and returns a pointer to that schema.
}
};
// ...
MyRemoteSchemaDocumentProvider provider;
SchemaDocument schema(sd, &provider);
~~~
## Conformance
RapidJSON passed 262 out of 263 tests in [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) (Json Schema draft 4).
The failed test is "changed scope ref invalid" of "change resolution scope" in `refRemote.json`. It is due to that `id` schema keyword and URI combining function are not implemented.
Besides, the `format` schema keyword for string values is ignored, since it is not required by the specification.
### Regular Expression
The schema keyword `pattern` and `patternProperties` uses regular expression to match the required pattern.
RapidJSON implemented a simple NFA regular expression engine, which is used by default. It supports the following syntax.
|Syntax|Description|
|------|-----------|
|`ab` | Concatenation |
|`a|b` | Alternation |
|`a?` | Zero or one |
|`a*` | Zero or more |
|`a+` | One or more |
|`a{3}` | Exactly 3 times |
|`a{3,}` | At least 3 times |
|`a{3,5}`| 3 to 5 times |
|`(ab)` | Grouping |
|`^a` | At the beginning |
|`a$` | At the end |
|`.` | Any character |
|`[abc]` | Character classes |
|`[a-c]` | Character class range |
|`[a-z0-9_]` | Character class combination |
|`[^abc]` | Negated character classes |
|`[^a-c]` | Negated character class range |
|`[\b]` | Backspace (U+0008) |
|`\|`, `\\`, ... | Escape characters |
|`\f` | Form feed (U+000C) |
|`\n` | Line feed (U+000A) |
|`\r` | Carriage return (U+000D) |
|`\t` | Tab (U+0009) |
|`\v` | Vertical tab (U+000B) |
For C++11 compiler, it is also possible to use the `std::regex` by defining `RAPIDJSON_SCHEMA_USE_INTERNALREGEX=0` and `RAPIDJSON_SCHEMA_USE_STDREGEX=1`. If your schemas do not need `pattern` and `patternProperties`, you can set both macros to zero to disable this feature, which will reduce some code size.
## Performance
Most C++ JSON libraries do not yet support JSON Schema. So we tried to evaluate the performance of RapidJSON's JSON Schema validator according to [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark), which tests 11 JavaScript libraries running on Node.js.
That benchmark runs validations on [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite), in which some test suites and tests are excluded. We made the same benchmarking procedure in [`schematest.cpp`](test/perftest/schematest.cpp).
On a Mac Book Pro (2.8 GHz Intel Core i7), the following results are collected.
|Validator|Relative speed|Number of test runs per second|
|---------|:------------:|:----------------------------:|
|RapidJSON|155%|30682|
|[`ajv`](https://github.com/epoberezkin/ajv)|100%|19770 (± 1.31%)|
|[`is-my-json-valid`](https://github.com/mafintosh/is-my-json-valid)|70%|13835 (± 2.84%)|
|[`jsen`](https://github.com/bugventure/jsen)|57.7%|11411 (± 1.27%)|
|[`schemasaurus`](https://github.com/AlexeyGrishin/schemasaurus)|26%|5145 (± 1.62%)|
|[`themis`](https://github.com/playlyfe/themis)|19.9%|3935 (± 2.69%)|
|[`z-schema`](https://github.com/zaggino/z-schema)|7%|1388 (± 0.84%)|
|[`jsck`](https://github.com/pandastrike/jsck#readme)|3.1%|606 (± 2.84%)|
|[`jsonschema`](https://github.com/tdegrunt/jsonschema#readme)|0.9%|185 (± 1.01%)|
|[`skeemas`](https://github.com/Prestaul/skeemas#readme)|0.8%|154 (± 0.79%)|
|tv4|0.5%|93 (± 0.94%)|
|[`jayschema`](https://github.com/natesilva/jayschema)|0.1%|21 (± 1.14%)|
That is, RapidJSON is about 1.5x faster than the fastest JavaScript library (ajv). And 1400x faster than the slowest one.

View File

@ -0,0 +1,237 @@
# Schema
(本功能于 v1.1.0 发布)
JSON Schema 是描述 JSON 格式的一个标准草案。一个 schema 本身也是一个 JSON。使用 JSON Schema 去校验 JSON可以让你的代码安全地访问 DOM而无须检查类型或键值是否存在等。这也能确保输出的 JSON 是符合指定的 schema。
RapidJSON 实现了一个 [JSON Schema Draft v4](http://json-schema.org/documentation.html) 的校验器。若你不熟悉 JSON Schema可以参考 [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/)。
[TOC]
## 基本用法
首先,你要把 JSON Schema 解析成 `Document`,再把它编译成一个 `SchemaDocument`
然后,利用该 `SchemaDocument` 创建一个 `SchemaValidator`。它与 `Writer` 相似,都是能够处理 SAX 事件的。因此,你可以用 `document.Accept(validator)` 去校验一个 JSON然后再获取校验结果。
~~~cpp
#include "rapidjson/schema.h"
// ...
Document sd;
if (!sd.Parse(schemaJson).HasParseError()) {
// 此 schema 不是合法的 JSON
// ...
}
SchemaDocument schema(sd); // 把一个 Document 编译至 SchemaDocument
// 之后不再需要 sd
Document d;
if (!d.Parse(inputJson).HasParseError()) {
// 输入不是一个合法的 JSON
// ...
}
SchemaValidator validator(schema);
if (!d.Accept(validator)) {
// 输入的 JSON 不合乎 schema
// 打印诊断信息
StringBuffer sb;
validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
printf("Invalid schema: %s\n", sb.GetString());
printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());
sb.Clear();
validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
printf("Invalid document: %s\n", sb.GetString());
}
~~~
一些注意点:
* 一个 `SchemaDocment` 能被多个 `SchemaValidator` 引用。它不会被 `SchemaValidator` 修改。
* 可以重复使用一个 `SchemaValidator` 来校验多个文件。在校验其他文件前,须先调用 `validator.Reset()`
## 在解析/生成时进行校验
与大部分 JSON Schema 校验器有所不同RapidJSON 提供了一个基于 SAX 的 schema 校验器实现。因此,你可以在输入流解析 JSON 的同时进行校验。若校验器遇到一个与 schema 不符的值,就会立即终止解析。这设计对于解析大型 JSON 文件时特别有用。
### DOM 解析
在使用 DOM 进行解析时,`Document` 除了接收 SAX 事件外,还需做一些准备及结束工作,因此,为了连接 `Reader``SchemaValidator``Document` 要做多一点事情。`SchemaValidatingReader` 是一个辅助类去做那些工作。
~~~cpp
#include "rapidjson/filereadstream.h"
// ...
SchemaDocument schema(sd); // 把一个 Document 编译至 SchemaDocument
// 使用 reader 解析 JSON
FILE* fp = fopen("big.json", "r");
FileReadStream is(fp, buffer, sizeof(buffer));
// 用 reader 解析 JSON校验它的 SAX 事件,并存储至 d
Document d;
SchemaValidatingReader<kParseDefaultFlags, FileReadStream, UTF8<> > reader(is, schema);
d.Populate(reader);
if (!reader.GetParseResult()) {
// 不是一个合法的 JSON
// 当 reader.GetParseResult().Code() == kParseErrorTermination,
// 它可能是被以下原因中止:
// (1) 校验器发现 JSON 不合乎 schema
// (2) 输入流有 I/O 错误。
// 检查校验结果
if (!reader.IsValid()) {
// 输入的 JSON 不合乎 schema
// 打印诊断信息
StringBuffer sb;
reader.GetInvalidSchemaPointer().StringifyUriFragment(sb);
printf("Invalid schema: %s\n", sb.GetString());
printf("Invalid keyword: %s\n", reader.GetInvalidSchemaKeyword());
sb.Clear();
reader.GetInvalidDocumentPointer().StringifyUriFragment(sb);
printf("Invalid document: %s\n", sb.GetString());
}
}
~~~
### SAX 解析
使用 SAX 解析时,情况就简单得多。若只需要校验 JSON 而无需进一步处理,那么仅需要:
~~~
SchemaValidator validator(schema);
Reader reader;
if (!reader.Parse(stream, validator)) {
if (!validator.IsValid()) {
// ...
}
}
~~~
这种方式和 [schemavalidator](example/schemavalidator/schemavalidator.cpp) 例子完全相同。这带来的独特优势是,无论 JSON 多巨大,永远维持低内存用量(内存用量只与 Schema 的复杂度相关)。
若你需要进一步处理 SAX 事件,便可使用模板类 `GenericSchemaValidator` 去设置校验器的输出 `Handler`
~~~
MyHandler handler;
GenericSchemaValidator<SchemaDocument, MyHandler> validator(schema, handler);
Reader reader;
if (!reader.Parse(ss, validator)) {
if (!validator.IsValid()) {
// ...
}
}
~~~
### 生成
我们也可以在生成serialization的时候进行校验。这能确保输出的 JSON 符合一个 JSON Schema。
~~~
StringBuffer sb;
Writer<StringBuffer> writer(sb);
GenericSchemaValidator<SchemaDocument, Writer<StringBuffer> > validator(s, writer);
if (!d.Accept(validator)) {
// Some problem during Accept(), it may be validation or encoding issues.
if (!validator.IsValid()) {
// ...
}
}
~~~
当然,如果你的应用仅需要 SAX 风格的生成,那么只需要把 SAX 事件由原来发送到 `Writer`,改为发送到 `SchemaValidator`
## 远程 Schema
JSON Schema 支持 [`$ref` 关键字](http://spacetelescope.github.io/understanding-json-schema/structuring.html),它是一个 [JSON pointer](doc/pointer.zh-cn.md) 引用至一个本地local或远程remote schema。本地指针的首字符是 `#`,而远程指针是一个相对或绝对 URI。例如
~~~js
{ "$ref": "definitions.json#/address" }
~~~
由于 `SchemaDocument` 并不知道如何处理那些 URI它需要使用者提供一个 `IRemoteSchemaDocumentProvider` 的实例去处理。
~~~
class MyRemoteSchemaDocumentProvider : public IRemoteSchemaDocumentProvider {
public:
virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeTyp length) {
// Resolve the uri and returns a pointer to that schema.
}
};
// ...
MyRemoteSchemaDocumentProvider provider;
SchemaDocument schema(sd, &provider);
~~~
## 标准的符合程度
RapidJSON 通过了 [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) (Json Schema draft 4) 中 263 个测试的 262 个。
没通过的测试是 `refRemote.json` 中的 "change resolution scope" - "changed scope ref invalid"。这是由于未实现 `id` schema 关键字及 URI 合并功能。
除此以外,关于字符串类型的 `format` schema 关键字也会被忽略,因为标准中并没需求必须实现。
### 正则表达式
`pattern``patternProperties` 这两个 schema 关键字使用了正则表达式去匹配所需的模式。
RapidJSON 实现了一个简单的 NFA 正则表达式引擎,并预设使用。它支持以下语法。
|语法|描述|
|------|-----------|
|`ab` | 串联 |
|`a|b` | 交替 |
|`a?` | 零或一次 |
|`a*` | 零或多次 |
|`a+` | 一或多次 |
|`a{3}` | 刚好 3 次 |
|`a{3,}` | 至少 3 次 |
|`a{3,5}`| 3 至 5 次 |
|`(ab)` | 分组 |
|`^a` | 在开始处 |
|`a$` | 在结束处 |
|`.` | 任何字符 |
|`[abc]` | 字符组 |
|`[a-c]` | 字符组范围 |
|`[a-z0-9_]` | 字符组组合 |
|`[^abc]` | 字符组取反 |
|`[^a-c]` | 字符组范围取反 |
|`[\b]` | 退格符 (U+0008) |
|`\|`, `\\`, ... | 转义字符 |
|`\f` | 馈页 (U+000C) |
|`\n` | 馈行 (U+000A) |
|`\r` | 回车 (U+000D) |
|`\t` | 制表 (U+0009) |
|`\v` | 垂直制表 (U+000B) |
对于使用 C++11 编译器的使用者,也可使用 `std::regex`,只需定义 `RAPIDJSON_SCHEMA_USE_INTERNALREGEX=0``RAPIDJSON_SCHEMA_USE_STDREGEX=1`。若你的 schema 无需使用 `pattern``patternProperties`,可以把两个宏都设为零,以禁用此功能,这样做可节省一些代码体积。
## 性能
大部分 C++ JSON 库都未支持 JSON Schema。因此我们尝试按照 [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark) 去评估 RapidJSON 的 JSON Schema 校验器。该评测测试了 11 个运行在 node.js 上的 JavaScript 库。
该评测校验 [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) 中的测试,当中排除了一些测试套件及个别测试。我们在 [`schematest.cpp`](test/perftest/schematest.cpp) 实现了相同的评测。
在 MacBook Pro (2.8 GHz Intel Core i7) 上收集到以下结果。
|校验器|相对速度|每秒执行的测试数目|
|---------|:------------:|:----------------------------:|
|RapidJSON|155%|30682|
|[`ajv`](https://github.com/epoberezkin/ajv)|100%|19770 (± 1.31%)|
|[`is-my-json-valid`](https://github.com/mafintosh/is-my-json-valid)|70%|13835 (± 2.84%)|
|[`jsen`](https://github.com/bugventure/jsen)|57.7%|11411 (± 1.27%)|
|[`schemasaurus`](https://github.com/AlexeyGrishin/schemasaurus)|26%|5145 (± 1.62%)|
|[`themis`](https://github.com/playlyfe/themis)|19.9%|3935 (± 2.69%)|
|[`z-schema`](https://github.com/zaggino/z-schema)|7%|1388 (± 0.84%)|
|[`jsck`](https://github.com/pandastrike/jsck#readme)|3.1%|606 (± 2.84%)|
|[`jsonschema`](https://github.com/tdegrunt/jsonschema#readme)|0.9%|185 (± 1.01%)|
|[`skeemas`](https://github.com/Prestaul/skeemas#readme)|0.8%|154 (± 0.79%)|
|tv4|0.5%|93 (± 0.94%)|
|[`jayschema`](https://github.com/natesilva/jayschema)|0.1%|21 (± 1.14%)|
换言之RapidJSON 比最快的 JavaScript 库ajv快约 1.5x。比最慢的快 1400x。

426
thirdparty/rapidjson-1.1.0/doc/stream.md vendored Normal file
View File

@ -0,0 +1,426 @@
# Stream
In RapidJSON, `rapidjson::Stream` is a concept for reading/writing JSON. Here we first show how to use streams provided. And then see how to create a custom stream.
[TOC]
# Memory Streams {#MemoryStreams}
Memory streams store JSON in memory.
## StringStream (Input) {#StringStream}
`StringStream` is the most basic input stream. It represents a complete, read-only JSON stored in memory. It is defined in `rapidjson/rapidjson.h`.
~~~~~~~~~~cpp
#include "rapidjson/document.h" // will include "rapidjson/rapidjson.h"
using namespace rapidjson;
// ...
const char json[] = "[1, 2, 3, 4]";
StringStream s(json);
Document d;
d.ParseStream(s);
~~~~~~~~~~
Since this is very common usage, `Document::Parse(const char*)` is provided to do exactly the same as above:
~~~~~~~~~~cpp
// ...
const char json[] = "[1, 2, 3, 4]";
Document d;
d.Parse(json);
~~~~~~~~~~
Note that, `StringStream` is a typedef of `GenericStringStream<UTF8<> >`, user may use another encodings to represent the character set of the stream.
## StringBuffer (Output) {#StringBuffer}
`StringBuffer` is a simple output stream. It allocates a memory buffer for writing the whole JSON. Use `GetString()` to obtain the buffer.
~~~~~~~~~~cpp
#include "rapidjson/stringbuffer.h"
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
const char* output = buffer.GetString();
~~~~~~~~~~
When the buffer is full, it will increases the capacity automatically. The default capacity is 256 characters (256 bytes for UTF8, 512 bytes for UTF16, etc.). User can provide an allocator and a initial capacity.
~~~~~~~~~~cpp
StringBuffer buffer1(0, 1024); // Use its allocator, initial size = 1024
StringBuffer buffer2(allocator, 1024);
~~~~~~~~~~
By default, `StringBuffer` will instantiate an internal allocator.
Similarly, `StringBuffer` is a typedef of `GenericStringBuffer<UTF8<> >`.
# File Streams {#FileStreams}
When parsing a JSON from file, you may read the whole JSON into memory and use ``StringStream`` above.
However, if the JSON is big, or memory is limited, you can use `FileReadStream`. It only read a part of JSON from file into buffer, and then let the part be parsed. If it runs out of characters in the buffer, it will read the next part from file.
## FileReadStream (Input) {#FileReadStream}
`FileReadStream` reads the file via a `FILE` pointer. And user need to provide a buffer.
~~~~~~~~~~cpp
#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));
Document d;
d.ParseStream(is);
fclose(fp);
~~~~~~~~~~
Different from string streams, `FileReadStream` is byte stream. It does not handle encodings. If the file is not UTF-8, the byte stream can be wrapped in a `EncodedInputStream`. It will be discussed very soon.
Apart from reading file, user can also use `FileReadStream` to read `stdin`.
## FileWriteStream (Output) {#FileWriteStream}
`FileWriteStream` is buffered output stream. Its usage is very similar to `FileReadStream`.
~~~~~~~~~~cpp
#include "rapidjson/filewritestream.h"
#include <cstdio>
using namespace rapidjson;
Document d;
d.Parse(json);
// ...
FILE* fp = fopen("output.json", "wb"); // non-Windows use "w"
char writeBuffer[65536];
FileWriteStream os(fp, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os);
d.Accept(writer);
fclose(fp);
~~~~~~~~~~
It can also directs the output to `stdout`.
# iostream Wrapper {#iostreamWrapper}
Due to users' requests, RapidJSON provided official wrappers for `std::basic_istream` and `std::basic_ostream`. However, please note that the performance will be much lower than the other streams above.
## IStreamWrapper {#IStreamWrapper}
`IStreamWrapper` wraps any class drived from `std::istream`, such as `std::istringstream`, `std::stringstream`, `std::ifstream`, `std::fstream`, into RapidJSON's input stream.
~~~cpp
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <fstream>
using namespace rapidjson;
using namespace std;
ifstream ifs("test.json");
IStreamWrapper isw(ifs);
Document d;
d.ParseStream(isw);
~~~
For classes derived from `std::wistream`, use `WIStreamWrapper`.
## OStreamWrapper {#OStreamWrapper}
Similarly, `OStreamWrapper` wraps any class derived from `std::ostream`, such as `std::ostringstream`, `std::stringstream`, `std::ofstream`, `std::fstream`, into RapidJSON's input stream.
~~~cpp
#include <rapidjson/document.h>
#include <rapidjson/ostreamwrapper.h>
#include <rapidjson/writer.h>
#include <fstream>
using namespace rapidjson;
using namespace std;
Document d;
d.Parse(json);
// ...
ofstream ofs("output.json");
OStreamWrapper osw(ofs);
Writer<OStreamWrapper> writer(osw);
d.Accept(writer);
~~~
For classes derived from `std::wostream`, use `WOStreamWrapper`.
# Encoded Streams {#EncodedStreams}
Encoded streams do not contain JSON itself, but they wrap byte streams to provide basic encoding/decoding function.
As mentioned above, UTF-8 byte streams can be read directly. However, UTF-16 and UTF-32 have endian issue. To handle endian correctly, it needs to convert bytes into characters (e.g. `wchar_t` for UTF-16) while reading, and characters into bytes while writing.
Besides, it also need to handle [byte order mark (BOM)](http://en.wikipedia.org/wiki/Byte_order_mark). When reading from a byte stream, it is needed to detect or just consume the BOM if exists. When writing to a byte stream, it can optionally write BOM.
If the encoding of stream is known in compile-time, you may use `EncodedInputStream` and `EncodedOutputStream`. If the stream can be UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE JSON, and it is only known in runtime, you may use `AutoUTFInputStream` and `AutoUTFOutputStream`. These streams are defined in `rapidjson/encodedstream.h`.
Note that, these encoded streams can be applied to streams other than file. For example, you may have a file in memory, or a custom byte stream, be wrapped in encoded streams.
## EncodedInputStream {#EncodedInputStream}
`EncodedInputStream` has two template parameters. The first one is a `Encoding` class, such as `UTF8`, `UTF16LE`, defined in `rapidjson/encodings.h`. The second one is the class of stream to be wrapped.
~~~~~~~~~~cpp
#include "rapidjson/document.h"
#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);
~~~~~~~~~~
## EncodedOutputStream {#EncodedOutputStream}
`EncodedOutputStream` is similar but it has a `bool putBOM` parameter in the constructor, controlling whether to write BOM into output byte stream.
~~~~~~~~~~cpp
#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));
typedef EncodedOutputStream<UTF32LE<>, FileWriteStream> OutputStream;
OutputStream eos(bos, true); // Write BOM
Writer<OutputStream, UTF32LE<>, UTF8<>> writer(eos);
d.Accept(writer); // This generates UTF32-LE file from UTF-8 in memory
fclose(fp);
~~~~~~~~~~
## AutoUTFInputStream {#AutoUTFInputStream}
Sometimes an application may want to handle all supported JSON encoding. `AutoUTFInputStream` will detection encoding by BOM first. If BOM is unavailable, it will use characteristics of valid JSON to make detection. If neither method success, it falls back to the UTF type provided in constructor.
Since the characters (code units) may be 8-bit, 16-bit or 32-bit. `AutoUTFInputStream` requires a character type which can hold at least 32-bit. We may use `unsigned`, as in the template parameter:
~~~~~~~~~~cpp
#include "rapidjson/document.h"
#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);
~~~~~~~~~~
When specifying the encoding of stream, uses `AutoUTF<CharType>` as in `ParseStream()` above.
You can obtain the type of UTF via `UTFType GetType()`. And check whether a BOM is found by `HasBOM()`
## AutoUTFOutputStream {#AutoUTFOutputStream}
Similarly, to choose encoding for output during runtime, we can use `AutoUTFOutputStream`. This class is not automatic *per se*. You need to specify the UTF type and whether to write BOM in runtime.
~~~~~~~~~~cpp
using namespace rapidjson;
void WriteJSONFile(FILE* fp, UTFType type, bool putBOM, const Document& d) {
char writeBuffer[256];
FileWriteStream bos(fp, writeBuffer, sizeof(writeBuffer));
typedef AutoUTFOutputStream<unsigned, FileWriteStream> OutputStream;
OutputStream eos(bos, type, putBOM);
Writer<OutputStream, UTF8<>, AutoUTF<> > writer;
d.Accept(writer);
}
~~~~~~~~~~
`AutoUTFInputStream` and `AutoUTFOutputStream` is more convenient than `EncodedInputStream` and `EncodedOutputStream`. They just incur a little bit runtime overheads.
# Custom Stream {#CustomStream}
In addition to memory/file streams, user can create their own stream classes which fits RapidJSON's API. For example, you may create network stream, stream from compressed file, etc.
RapidJSON combines different types using templates. A class containing all required interface can be a stream. The Stream interface is defined in comments of `rapidjson/rapidjson.h`:
~~~~~~~~~~cpp
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);
}
~~~~~~~~~~
For input stream, they must implement `Peek()`, `Take()` and `Tell()`.
For output stream, they must implement `Put()` and `Flush()`.
There are two special interface, `PutBegin()` and `PutEnd()`, which are only for *in situ* parsing. Normal streams do not implement them. However, if the interface is not needed for a particular stream, it is still need to a dummy implementation, otherwise will generate compilation error.
## Example: istream wrapper {#ExampleIStreamWrapper}
The following example is a simple wrapper of `std::istream`, which only implements 3 functions.
~~~~~~~~~~cpp
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_;
};
~~~~~~~~~~
User can use it to wrap instances of `std::stringstream`, `std::ifstream`.
~~~~~~~~~~cpp
const char* json = "[1,2,3,4]";
std::stringstream ss(json);
MyIStreamWrapper is(ss);
Document d;
d.ParseStream(is);
~~~~~~~~~~
Note that, this implementation may not be as efficient as RapidJSON's memory or file streams, due to internal overheads of the standard library.
## Example: ostream wrapper {#ExampleOStreamWrapper}
The following example is a simple wrapper of `std::istream`, which only implements 2 functions.
~~~~~~~~~~cpp
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_;
};
~~~~~~~~~~
User can use it to wrap instances of `std::stringstream`, `std::ofstream`.
~~~~~~~~~~cpp
Document d;
// ...
std::stringstream ss;
MyOStreamWrapper os(ss);
Writer<MyOStreamWrapper> writer(os);
d.Accept(writer);
~~~~~~~~~~
Note that, this implementation may not be as efficient as RapidJSON's memory or file streams, due to internal overheads of the standard library.
# Summary {#Summary}
This section describes stream classes available in RapidJSON. Memory streams are simple. File stream can reduce the memory required during JSON parsing and generation, if the JSON is stored in file system. Encoded streams converts between byte streams and character streams. Finally, user may create custom streams using a simple interface.

View File

@ -0,0 +1,426 @@
# 流
在 RapidJSON 中,`rapidjson::Stream` 是用於读写 JSON 的概念(概念是指 C++ 的 concept。在这里我们先介绍如何使用 RapidJSON 提供的各种流。然后再看看如何自行定义流。
[TOC]
# 内存流 {#MemoryStreams}
内存流把 JSON 存储在内存之中。
## StringStream输入{#StringStream}
`StringStream` 是最基本的输入流,它表示一个完整的、只读的、存储于内存的 JSON。它在 `rapidjson/rapidjson.h` 中定义。
~~~~~~~~~~cpp
#include "rapidjson/document.h" // 会包含 "rapidjson/rapidjson.h"
using namespace rapidjson;
// ...
const char json[] = "[1, 2, 3, 4]";
StringStream s(json);
Document d;
d.ParseStream(s);
~~~~~~~~~~
由于这是非常常用的用法RapidJSON 提供 `Document::Parse(const char*)` 去做完全相同的事情:
~~~~~~~~~~cpp
// ...
const char json[] = "[1, 2, 3, 4]";
Document d;
d.Parse(json);
~~~~~~~~~~
需要注意,`StringStream``GenericStringStream<UTF8<> >` 的 typedef使用者可用其他编码类去代表流所使用的字符集。
## StringBuffer输出{#StringBuffer}
`StringBuffer` 是一个简单的输出流。它分配一个内存缓冲区,供写入整个 JSON。可使用 `GetString()` 来获取该缓冲区。
~~~~~~~~~~cpp
#include "rapidjson/stringbuffer.h"
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
const char* output = buffer.GetString();
~~~~~~~~~~
当缓冲区满溢,它将自动增加容量。缺省容量是 256 个字符UTF8 是 256 字节UTF16 是 512 字节等)。使用者能自行提供分配器及初始容量。
~~~~~~~~~~cpp
StringBuffer buffer1(0, 1024); // 使用它的分配器,初始大小 = 1024
StringBuffer buffer2(allocator, 1024);
~~~~~~~~~~
如无设置分配器,`StringBuffer` 会自行实例化一个内部分配器。
相似地,`StringBuffer``GenericStringBuffer<UTF8<> >` 的 typedef。
# 文件流 {#FileStreams}
当要从文件解析一个 JSON你可以把整个 JSON 读入内存并使用上述的 `StringStream`
然而,若 JSON 很大,或是内存有限,你可以改用 `FileReadStream`。它只会从文件读取一部分至缓冲区,然后让那部分被解析。若缓冲区的字符都被读完,它会再从文件读取下一部分。
## FileReadStream输入 {#FileReadStream}
`FileReadStream` 通过 `FILE` 指针读取文件。使用者需要提供一个缓冲区。
~~~~~~~~~~cpp
#include "rapidjson/filereadstream.h"
#include <cstdio>
using namespace rapidjson;
FILE* fp = fopen("big.json", "rb"); // 非 Windows 平台使用 "r"
char readBuffer[65536];
FileReadStream is(fp, readBuffer, sizeof(readBuffer));
Document d;
d.ParseStream(is);
fclose(fp);
~~~~~~~~~~
`StringStreams` 不一样,`FileReadStream` 是一个字节流。它不处理编码。若文件并非 UTF-8 编码,可以把字节流用 `EncodedInputStream` 包装。我们很快会讨论这个问题。
除了读取文件,使用者也可以使用 `FileReadStream` 来读取 `stdin`
## FileWriteStream输出{#FileWriteStream}
`FileWriteStream` 是一个含缓冲功能的输出流。它的用法与 `FileReadStream` 非常相似。
~~~~~~~~~~cpp
#include "rapidjson/filewritestream.h"
#include <cstdio>
using namespace rapidjson;
Document d;
d.Parse(json);
// ...
FILE* fp = fopen("output.json", "wb"); // 非 Windows 平台使用 "w"
char writeBuffer[65536];
FileWriteStream os(fp, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os);
d.Accept(writer);
fclose(fp);
~~~~~~~~~~
它也可以把输出导向 `stdout`
# iostream 包装类 {#iostreamWrapper}
基于用户的要求RapidJSON 提供了正式的 `std::basic_istream``std::basic_ostream` 包装类。然而,请注意其性能会大大低于以上的其他流。
## IStreamWrapper {#IStreamWrapper}
`IStreamWrapper` 把任何继承自 `std::istream` 的类(如 `std::istringstream``std::stringstream``std::ifstream``std::fstream`)包装成 RapidJSON 的输入流。
~~~cpp
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <fstream>
using namespace rapidjson;
using namespace std;
ifstream ifs("test.json");
IStreamWrapper isw(ifs);
Document d;
d.ParseStream(isw);
~~~
对于继承自 `std::wistream` 的类,则使用 `WIStreamWrapper`
## OStreamWrapper {#OStreamWrapper}
相似地,`OStreamWrapper` 把任何继承自 `std::ostream` 的类(如 `std::ostringstream``std::stringstream``std::ofstream``std::fstream`)包装成 RapidJSON 的输出流。
~~~cpp
#include <rapidjson/document.h>
#include <rapidjson/ostreamwrapper.h>
#include <rapidjson/writer.h>
#include <fstream>
using namespace rapidjson;
using namespace std;
Document d;
d.Parse(json);
// ...
ofstream ofs("output.json");
OStreamWrapper osw(ofs);
Writer<OStreamWrapper> writer(osw);
d.Accept(writer);
~~~
对于继承自 `std::wistream` 的类,则使用 `WIStreamWrapper`
# 编码流 {#EncodedStreams}
编码流encoded streams本身不存储 JSON它们是通过包装字节流来提供基本的编码解码功能。
如上所述,我们可以直接读入 UTF-8 字节流。然而UTF-16 及 UTF-32 有字节序endian问题。要正确地处理字节序需要在读取时把字节转换成字符如对 UTF-16 使用 `wchar_t`),以及在写入时把字符转换为字节。
除此以外,我们也需要处理 [字节顺序标记byte order mark, BOM](http://en.wikipedia.org/wiki/Byte_order_mark)。当从一个字节流读取时,需要检测 BOM或者仅仅是把存在的 BOM 消去。当把 JSON 写入字节流时,也可选择写入 BOM。
若一个流的编码在编译期已知,你可使用 `EncodedInputStream``EncodedOutputStream`。若一个流可能存储 UTF-8、UTF-16LE、UTF-16BE、UTF-32LE、UTF-32BE 的 JSON并且编码只能在运行时得知你便可以使用 `AutoUTFInputStream``AutoUTFOutputStream`。这些流定义在 `rapidjson/encodedstream.h`
注意到,这些编码流可以施于文件以外的流。例如,你可以用编码流包装内存中的文件或自定义的字节流。
## EncodedInputStream {#EncodedInputStream}
`EncodedInputStream` 含两个模板参数。第一个是 `Encoding` 类型,例如定义于 `rapidjson/encodings.h``UTF8``UTF16LE`。第二个参数是被包装的流的类型。
~~~~~~~~~~cpp
#include "rapidjson/document.h"
#include "rapidjson/filereadstream.h" // FileReadStream
#include "rapidjson/encodedstream.h" // EncodedInputStream
#include <cstdio>
using namespace rapidjson;
FILE* fp = fopen("utf16le.json", "rb"); // 非 Windows 平台使用 "r"
char readBuffer[256];
FileReadStream bis(fp, readBuffer, sizeof(readBuffer));
EncodedInputStream<UTF16LE<>, FileReadStream> eis(bis); // 用 eis 包装 bis
Document d; // Document 为 GenericDocument<UTF8<> >
d.ParseStream<0, UTF16LE<> >(eis); // 把 UTF-16LE 文件解析至内存中的 UTF-8
fclose(fp);
~~~~~~~~~~
## EncodedOutputStream {#EncodedOutputStream}
`EncodedOutputStream` 也是相似的,但它的构造函数有一个 `bool putBOM` 参数,用于控制是否在输出字节流写入 BOM。
~~~~~~~~~~cpp
#include "rapidjson/filewritestream.h" // FileWriteStream
#include "rapidjson/encodedstream.h" // EncodedOutputStream
#include <cstdio>
Document d; // Document 为 GenericDocument<UTF8<> >
// ...
FILE* fp = fopen("output_utf32le.json", "wb"); // 非 Windows 平台使用 "w"
char writeBuffer[256];
FileWriteStream bos(fp, writeBuffer, sizeof(writeBuffer));
typedef EncodedOutputStream<UTF32LE<>, FileWriteStream> OutputStream;
OutputStream eos(bos, true); // 写入 BOM
Writer<OutputStream, UTF32LE<>, UTF8<>> writer(eos);
d.Accept(writer); // 这里从内存的 UTF-8 生成 UTF32-LE 文件
fclose(fp);
~~~~~~~~~~
## AutoUTFInputStream {#AutoUTFInputStream}
有时候,应用软件可能需要㲃理所有可支持的 JSON 编码。`AutoUTFInputStream` 会先使用 BOM 来检测编码。若 BOM 不存在,它便会使用合法 JSON 的特性来检测。若两种方法都失败,它就会倒退至构造函数提供的 UTF 类型。
由于字符编码单元code unit可能是 8 位、16 位或 32 位,`AutoUTFInputStream` 需要一个能至少储存 32 位的字符类型。我们可以使用 `unsigned` 作为模板参数:
~~~~~~~~~~cpp
#include "rapidjson/document.h"
#include "rapidjson/filereadstream.h" // FileReadStream
#include "rapidjson/encodedstream.h" // AutoUTFInputStream
#include <cstdio>
using namespace rapidjson;
FILE* fp = fopen("any.json", "rb"); // 非 Windows 平台使用 "r"
char readBuffer[256];
FileReadStream bis(fp, readBuffer, sizeof(readBuffer));
AutoUTFInputStream<unsigned, FileReadStream> eis(bis); // 用 eis 包装 bis
Document d; // Document 为 GenericDocument<UTF8<> >
d.ParseStream<0, AutoUTF<unsigned> >(eis); // 把任何 UTF 编码的文件解析至内存中的 UTF-8
fclose(fp);
~~~~~~~~~~
当要指定流的编码,可使用上面例子中 `ParseStream()` 的参数 `AutoUTF<CharType>`
你可以使用 `UTFType GetType()` 去获取 UTF 类型,并且用 `HasBOM()` 检测输入流是否含有 BOM。
## AutoUTFOutputStream {#AutoUTFOutputStream}
相似地,要在运行时选择输出的编码,我们可使用 `AutoUTFOutputStream`。这个类本身并非「自动」。你需要在运行时指定 UTF 类型,以及是否写入 BOM。
~~~~~~~~~~cpp
using namespace rapidjson;
void WriteJSONFile(FILE* fp, UTFType type, bool putBOM, const Document& d) {
char writeBuffer[256];
FileWriteStream bos(fp, writeBuffer, sizeof(writeBuffer));
typedef AutoUTFOutputStream<unsigned, FileWriteStream> OutputStream;
OutputStream eos(bos, type, putBOM);
Writer<OutputStream, UTF8<>, AutoUTF<> > writer;
d.Accept(writer);
}
~~~~~~~~~~
`AutoUTFInputStream``AutoUTFOutputStream` 是比 `EncodedInputStream``EncodedOutputStream` 方便。但前者会产生一点运行期额外开销。
# 自定义流 {#CustomStream}
除了内存/文件流,使用者可创建自行定义适配 RapidJSON API 的流类。例如,你可以创建网络流、从压缩文件读取的流等等。
RapidJSON 利用模板结合不同的类型。只要一个类包含所有所需的接口,就可以作为一个流。流的接合定义在 `rapidjson/rapidjson.h` 的注释里:
~~~~~~~~~~cpp
concept Stream {
typename Ch; //!< 流的字符类型
//! 从流读取当前字符不移动读取指针read cursor
Ch Peek() const;
//! 从流读取当前字符,移动读取指针至下一字符。
Ch Take();
//! 获取读取指针。
//! \return 从开始以来所读过的字符数量。
size_t Tell();
//! 从当前读取指针开始写入操作。
//! \return 返回开始写入的指针。
Ch* PutBegin();
//! 写入一个字符。
void Put(Ch c);
//! 清空缓冲区。
void Flush();
//! 完成写作操作。
//! \param begin PutBegin() 返回的开始写入指针。
//! \return 已写入的字符数量。
size_t PutEnd(Ch* begin);
}
~~~~~~~~~~
输入流必须实现 `Peek()``Take()``Tell()`
输出流必须实现 `Put()``Flush()`
`PutBegin()``PutEnd()` 是特殊的接口,仅用于原位(*in situ*)解析。一般的流不需实现它们。然而,即使接口不需用于某些流,仍然需要提供空实现,否则会产生编译错误。
## 例子istream 的包装类 {#ExampleIStreamWrapper}
以下的简单例子是 `std::istream` 的包装类,它只需现 3 个函数。
~~~~~~~~~~cpp
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` 的实例。
~~~~~~~~~~cpp
const char* json = "[1,2,3,4]";
std::stringstream ss(json);
MyIStreamWrapper is(ss);
Document d;
d.ParseStream(is);
~~~~~~~~~~
但要注意,由于标准库的内部开销问,此实现的性能可能不如 RapidJSON 的内存/文件流。
## 例子ostream 的包装类 {#ExampleOStreamWrapper}
以下的例子是 `std::istream` 的包装类,它只需实现 2 个函数。
~~~~~~~~~~cpp
class MyOStreamWrapper {
public:
typedef char Ch;
OStreamWrapper(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` 的实例。
~~~~~~~~~~cpp
Document d;
// ...
std::stringstream ss;
MyOStreamWrapper os(ss);
Writer<MyOStreamWrapper> writer(os);
d.Accept(writer);
~~~~~~~~~~
但要注意,由于标准库的内部开销问,此实现的性能可能不如 RapidJSON 的内存/文件流。
# 总结 {#Summary}
本节描述了 RapidJSON 提供的各种流的类。内存流很简单。若 JSON 存储在文件中,文件流可减少 JSON 解析及生成所需的内存量。编码流在字节流和字符流之间作转换。最后,使用者可使用一个简单接口创建自定义的流。

View File

@ -0,0 +1,536 @@
# Tutorial
This tutorial introduces the basics of the Document Object Model(DOM) API.
As shown in [Usage at a glance](@ref index), a JSON can be parsed into DOM, and then the DOM can be queried and modified easily, and finally be converted back to JSON.
[TOC]
# Value & Document {#ValueDocument}
Each JSON value is stored in a type called `Value`. A `Document`, representing the DOM, contains the root `Value` of the DOM tree. All public types and functions of RapidJSON are defined in the `rapidjson` namespace.
# Query Value {#QueryValue}
In this section, we will use excerpt of `example/tutorial/tutorial.cpp`.
Assumes we have a JSON stored in a C string (`const char* json`):
~~~~~~~~~~js
{
"hello": "world",
"t": true ,
"f": false,
"n": null,
"i": 123,
"pi": 3.1416,
"a": [1, 2, 3, 4]
}
~~~~~~~~~~
Parse it into a `Document`:
~~~~~~~~~~cpp
#include "rapidjson/document.h"
using namespace rapidjson;
// ...
Document document;
document.Parse(json);
~~~~~~~~~~
The JSON is now parsed into `document` as a *DOM tree*:
![DOM in the tutorial](diagram/tutorial.png)
Since the update to RFC 7159, the root of a conforming JSON document can be any JSON value. In earlier RFC 4627, only objects or arrays were allowed as root values. In this case, the root is an object.
~~~~~~~~~~cpp
assert(document.IsObject());
~~~~~~~~~~
Let's query whether a `"hello"` member exists in the root object. Since a `Value` can contain different types of value, we may need to verify its type and use suitable API to obtain the value. In this example, `"hello"` member associates with a JSON string.
~~~~~~~~~~cpp
assert(document.HasMember("hello"));
assert(document["hello"].IsString());
printf("hello = %s\n", document["hello"].GetString());
~~~~~~~~~~
~~~~~~~~~~
world
~~~~~~~~~~
JSON true/false values are represented as `bool`.
~~~~~~~~~~cpp
assert(document["t"].IsBool());
printf("t = %s\n", document["t"].GetBool() ? "true" : "false");
~~~~~~~~~~
~~~~~~~~~~
true
~~~~~~~~~~
JSON null can be queryed by `IsNull()`.
~~~~~~~~~~cpp
printf("n = %s\n", document["n"].IsNull() ? "null" : "?");
~~~~~~~~~~
~~~~~~~~~~
null
~~~~~~~~~~
JSON number type represents all numeric values. However, C++ needs more specific type for manipulation.
~~~~~~~~~~cpp
assert(document["i"].IsNumber());
// In this case, IsUint()/IsInt64()/IsUInt64() also return true.
assert(document["i"].IsInt());
printf("i = %d\n", document["i"].GetInt());
// Alternative (int)document["i"]
assert(document["pi"].IsNumber());
assert(document["pi"].IsDouble());
printf("pi = %g\n", document["pi"].GetDouble());
~~~~~~~~~~
~~~~~~~~~~
i = 123
pi = 3.1416
~~~~~~~~~~
JSON array contains a number of elements.
~~~~~~~~~~cpp
// Using a reference for consecutive access is handy and faster.
const Value& a = document["a"];
assert(a.IsArray());
for (SizeType i = 0; i < a.Size(); i++) // Uses SizeType instead of size_t
printf("a[%d] = %d\n", i, a[i].GetInt());
~~~~~~~~~~
~~~~~~~~~~
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
~~~~~~~~~~
Note that, RapidJSON does not automatically convert values between JSON types. If a value is a string, it is invalid to call `GetInt()`, for example. In debug mode it will fail an assertion. In release mode, the behavior is undefined.
In the following, details about querying individual types are discussed.
## Query Array {#QueryArray}
By default, `SizeType` is typedef of `unsigned`. In most systems, array is limited to store up to 2^32-1 elements.
You may access the elements in array by integer literal, for example, `a[0]`, `a[1]`, `a[2]`.
Array is similar to `std::vector`, instead of using indices, you may also use iterator to access all the elements.
~~~~~~~~~~cpp
for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
printf("%d ", itr->GetInt());
~~~~~~~~~~
And other familiar query functions:
* `SizeType Capacity() const`
* `bool Empty() const`
### Range-based For Loop (New in v1.1.0)
When C++11 is enabled, you can use range-based for loop to access all elements in an array.
~~~~~~~~~~cpp
for (auto& v : a.GetArray())
printf("%d ", v.GetInt());
~~~~~~~~~~
## Query Object {#QueryObject}
Similar to array, we can access all object members by iterator:
~~~~~~~~~~cpp
static const char* kTypeNames[] =
{ "Null", "False", "True", "Object", "Array", "String", "Number" };
for (Value::ConstMemberIterator itr = document.MemberBegin();
itr != document.MemberEnd(); ++itr)
{
printf("Type of member %s is %s\n",
itr->name.GetString(), kTypeNames[itr->value.GetType()]);
}
~~~~~~~~~~
~~~~~~~~~~
Type of member hello is String
Type of member t is True
Type of member f is False
Type of member n is Null
Type of member i is Number
Type of member pi is Number
Type of member a is Array
~~~~~~~~~~
Note that, when `operator[](const char*)` cannot find the member, it will fail an assertion.
If we are unsure whether a member exists, we need to call `HasMember()` before calling `operator[](const char*)`. However, this incurs two lookup. A better way is to call `FindMember()`, which can check the existence of member and obtain its value at once:
~~~~~~~~~~cpp
Value::ConstMemberIterator itr = document.FindMember("hello");
if (itr != document.MemberEnd())
printf("%s\n", itr->value.GetString());
~~~~~~~~~~
### Range-based For Loop (New in v1.1.0)
When C++11 is enabled, you can use range-based for loop to access all members in an object.
~~~~~~~~~~cpp
for (auto& m : document.GetObject())
printf("Type of member %s is %s\n",
m.name.GetString(), kTypeNames[m.value.GetType()]);
~~~~~~~~~~
## Querying Number {#QueryNumber}
JSON provide a single numerical type called Number. Number can be integer or real numbers. RFC 4627 says the range of Number is specified by parser.
As C++ provides several integer and floating point number types, the DOM tries to handle these with widest possible range and good performance.
When a Number is parsed, it is stored in the DOM as either one of the following type:
Type | Description
-----------|---------------------------------------
`unsigned` | 32-bit unsigned integer
`int` | 32-bit signed integer
`uint64_t` | 64-bit unsigned integer
`int64_t` | 64-bit signed integer
`double` | 64-bit double precision floating point
When querying a number, you can check whether the number can be obtained as target type:
Checking | Obtaining
------------------|---------------------
`bool IsNumber()` | N/A
`bool IsUint()` | `unsigned GetUint()`
`bool IsInt()` | `int GetInt()`
`bool IsUint64()` | `uint64_t GetUint64()`
`bool IsInt64()` | `int64_t GetInt64()`
`bool IsDouble()` | `double GetDouble()`
Note that, an integer value may be obtained in various ways without conversion. For example, A value `x` containing 123 will make `x.IsInt() == x.IsUint() == x.IsInt64() == x.IsUint64() == true`. But a value `y` containing -3000000000 will only makes `x.IsInt64() == true`.
When obtaining the numeric values, `GetDouble()` will convert internal integer representation to a `double`. Note that, `int` and `unsigned` can be safely convert to `double`, but `int64_t` and `uint64_t` may lose precision (since mantissa of `double` is only 52-bits).
## Query String {#QueryString}
In addition to `GetString()`, the `Value` class also contains `GetStringLength()`. Here explains why.
According to RFC 4627, JSON strings can contain Unicode character `U+0000`, which must be escaped as `"\u0000"`. The problem is that, C/C++ often uses null-terminated string, which treats ``\0'` as the terminator symbol.
To conform RFC 4627, RapidJSON supports string containing `U+0000`. If you need to handle this, you can use `GetStringLength()` API to obtain the correct length of string.
For example, after parsing a the following JSON to `Document d`:
~~~~~~~~~~js
{ "s" : "a\u0000b" }
~~~~~~~~~~
The correct length of the value `"a\u0000b"` is 3. But `strlen()` returns 1.
`GetStringLength()` can also improve performance, as user may often need to call `strlen()` for allocating buffer.
Besides, `std::string` also support a constructor:
~~~~~~~~~~cpp
string(const char* s, size_t count);
~~~~~~~~~~
which accepts the length of string as parameter. This constructor supports storing null character within the string, and should also provide better performance.
## Comparing values
You can use `==` and `!=` to compare values. Two values are equal if and only if they are have same type and contents. You can also compare values with primitive types. Here is an example.
~~~~~~~~~~cpp
if (document["hello"] == document["n"]) /*...*/; // Compare values
if (document["hello"] == "world") /*...*/; // Compare value with literal string
if (document["i"] != 123) /*...*/; // Compare with integers
if (document["pi"] != 3.14) /*...*/; // Compare with double.
~~~~~~~~~~
Array/object compares their elements/members in order. They are equal if and only if their whole subtrees are equal.
Note that, currently if an object contains duplicated named member, comparing equality with any object is always `false`.
# Create/Modify Values {#CreateModifyValues}
There are several ways to create values. After a DOM tree is created and/or modified, it can be saved as JSON again using `Writer`.
## Change Value Type {#ChangeValueType}
When creating a Value or Document by default constructor, its type is Null. To change its type, call `SetXXX()` or assignment operator, for example:
~~~~~~~~~~cpp
Document d; // Null
d.SetObject();
Value v; // Null
v.SetInt(10);
v = 10; // Shortcut, same as above
~~~~~~~~~~
### Overloaded Constructors
There are also overloaded constructors for several types:
~~~~~~~~~~cpp
Value b(true); // calls Value(bool)
Value i(-123); // calls Value(int)
Value u(123u); // calls Value(unsigned)
Value d(1.5); // calls Value(double)
~~~~~~~~~~
To create empty object or array, you may use `SetObject()`/`SetArray()` after default constructor, or using the `Value(Type)` in one shot:
~~~~~~~~~~cpp
Value o(kObjectType);
Value a(kArrayType);
~~~~~~~~~~
## Move Semantics {#MoveSemantics}
A very special decision during design of RapidJSON is that, assignment of value does not copy the source value to destination value. Instead, the value from source is moved to the destination. For example,
~~~~~~~~~~cpp
Value a(123);
Value b(456);
b = a; // a becomes a Null value, b becomes number 123.
~~~~~~~~~~
![Assignment with move semantics.](diagram/move1.png)
Why? What is the advantage of this semantics?
The simple answer is performance. For fixed size JSON types (Number, True, False, Null), copying them is fast and easy. However, For variable size JSON types (String, Array, Object), copying them will incur a lot of overheads. And these overheads are often unnoticed. Especially when we need to create temporary object, copy it to another variable, and then destruct it.
For example, if normal *copy* semantics was used:
~~~~~~~~~~cpp
Document d;
Value o(kObjectType);
{
Value contacts(kArrayType);
// adding elements to contacts array.
// ...
o.AddMember("contacts", contacts, d.GetAllocator()); // deep clone contacts (may be with lots of allocations)
// destruct contacts.
}
~~~~~~~~~~
![Copy semantics makes a lots of copy operations.](diagram/move2.png)
The object `o` needs to allocate a buffer of same size as contacts, makes a deep clone of it, and then finally contacts is destructed. This will incur a lot of unnecessary allocations/deallocations and memory copying.
There are solutions to prevent actual copying these data, such as reference counting and garbage collection(GC).
To make RapidJSON simple and fast, we chose to use *move* semantics for assignment. It is similar to `std::auto_ptr` which transfer ownership during assignment. Move is much faster and simpler, it just destructs the original value, `memcpy()` the source to destination, and finally sets the source as Null type.
So, with move semantics, the above example becomes:
~~~~~~~~~~cpp
Document d;
Value o(kObjectType);
{
Value contacts(kArrayType);
// adding elements to contacts array.
o.AddMember("contacts", contacts, d.GetAllocator()); // just memcpy() of contacts itself to the value of new member (16 bytes)
// contacts became Null here. Its destruction is trivial.
}
~~~~~~~~~~
![Move semantics makes no copying.](diagram/move3.png)
This is called move assignment operator in C++11. As RapidJSON supports C++03, it adopts move semantics using assignment operator, and all other modifying function like `AddMember()`, `PushBack()`.
### Move semantics and temporary values {#TemporaryValues}
Sometimes, it is convenient to construct a Value in place, before passing it to one of the "moving" functions, like `PushBack()` or `AddMember()`. As temporary objects can't be converted to proper Value references, the convenience function `Move()` is available:
~~~~~~~~~~cpp
Value a(kArrayType);
Document::AllocatorType& allocator = document.GetAllocator();
// a.PushBack(Value(42), allocator); // will not compile
a.PushBack(Value().SetInt(42), allocator); // fluent API
a.PushBack(Value(42).Move(), allocator); // same as above
~~~~~~~~~~
## Create String {#CreateString}
RapidJSON provide two strategies for storing string.
1. copy-string: allocates a buffer, and then copy the source data into it.
2. const-string: simply store a pointer of string.
Copy-string is always safe because it owns a copy of the data. Const-string can be used for storing string literal, and in-situ parsing which we will mentioned in Document section.
To make memory allocation customizable, RapidJSON requires user to pass an instance of allocator, whenever an operation may require allocation. This design is needed to prevent storing a allocator (or Document) pointer per Value.
Therefore, when we assign a copy-string, we call this overloaded `SetString()` with allocator:
~~~~~~~~~~cpp
Document document;
Value author;
char buffer[10];
int len = sprintf(buffer, "%s %s", "Milo", "Yip"); // dynamically created string.
author.SetString(buffer, len, document.GetAllocator());
memset(buffer, 0, sizeof(buffer));
// author.GetString() still contains "Milo Yip" after buffer is destroyed
~~~~~~~~~~
In this example, we get the allocator from a `Document` instance. This is a common idiom when using RapidJSON. But you may use other instances of allocator.
Besides, the above `SetString()` requires length. This can handle null characters within a string. There is another `SetString()` overloaded function without the length parameter. And it assumes the input is null-terminated and calls a `strlen()`-like function to obtain the length.
Finally, for string literal or string with safe life-cycle can use const-string version of `SetString()`, which lacks allocator parameter. For string literals (or constant character arrays), simply passing the literal as parameter is safe and efficient:
~~~~~~~~~~cpp
Value s;
s.SetString("rapidjson"); // can contain null character, length derived at compile time
s = "rapidjson"; // shortcut, same as above
~~~~~~~~~~
For character pointer, the RapidJSON requires to mark it as safe before using it without copying. This can be achieved by using the `StringRef` function:
~~~~~~~~~cpp
const char * cstr = getenv("USER");
size_t cstr_len = ...; // in case length is available
Value s;
// s.SetString(cstr); // will not compile
s.SetString(StringRef(cstr)); // ok, assume safe lifetime, null-terminated
s = StringRef(cstr); // shortcut, same as above
s.SetString(StringRef(cstr,cstr_len)); // faster, can contain null character
s = StringRef(cstr,cstr_len); // shortcut, same as above
~~~~~~~~~
## Modify Array {#ModifyArray}
Value with array type provides similar APIs as `std::vector`.
* `Clear()`
* `Reserve(SizeType, Allocator&)`
* `Value& PushBack(Value&, Allocator&)`
* `template <typename T> GenericValue& PushBack(T, Allocator&)`
* `Value& PopBack()`
* `ValueIterator Erase(ConstValueIterator pos)`
* `ValueIterator Erase(ConstValueIterator first, ConstValueIterator last)`
Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore require an allocator.
Here is an example of `PushBack()`:
~~~~~~~~~~cpp
Value a(kArrayType);
Document::AllocatorType& allocator = document.GetAllocator();
for (int i = 5; i <= 10; i++)
a.PushBack(i, allocator); // allocator is needed for potential realloc().
// Fluent interface
a.PushBack("Lua", allocator).PushBack("Mio", allocator);
~~~~~~~~~~
Differs from STL, `PushBack()`/`PopBack()` returns the array reference itself. This is called _fluent interface_.
If you want to add a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)) to the array, you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place:
~~~~~~~~~~cpp
// in-place Value parameter
contact.PushBack(Value("copy", document.GetAllocator()).Move(), // copy string
document.GetAllocator());
// explicit parameters
Value val("key", document.GetAllocator()); // copy string
contact.PushBack(val, document.GetAllocator());
~~~~~~~~~~
## Modify Object {#ModifyObject}
Object is a collection of key-value pairs (members). Each key must be a string value. To modify an object, either add or remove members. THe following APIs are for adding members:
* `Value& AddMember(Value&, Value&, Allocator& allocator)`
* `Value& AddMember(StringRefType, Value&, Allocator&)`
* `template <typename T> Value& AddMember(StringRefType, T value, Allocator&)`
Here is an example.
~~~~~~~~~~cpp
Value contact(kObject);
contact.AddMember("name", "Milo", document.GetAllocator());
contact.AddMember("married", true, document.GetAllocator());
~~~~~~~~~~
The name parameter with `StringRefType` is similar to the interface of `SetString` function for string values. These overloads are used to avoid the need for copying the `name` string, as constant key names are very common in JSON objects.
If you need to create a name from a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)), you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place:
~~~~~~~~~~cpp
// in-place Value parameter
contact.AddMember(Value("copy", document.GetAllocator()).Move(), // copy string
Value().Move(), // null value
document.GetAllocator());
// explicit parameters
Value key("key", document.GetAllocator()); // copy string name
Value val(42); // some value
contact.AddMember(key, val, document.GetAllocator());
~~~~~~~~~~
For removing members, there are several choices:
* `bool RemoveMember(const Ch* name)`: Remove a member by search its name (linear time complexity).
* `bool RemoveMember(const Value& name)`: same as above but `name` is a Value.
* `MemberIterator RemoveMember(MemberIterator)`: Remove a member by iterator (_constant_ time complexity).
* `MemberIterator EraseMember(MemberIterator)`: similar to the above but it preserves order of members (linear time complexity).
* `MemberIterator EraseMember(MemberIterator first, MemberIterator last)`: remove a range of members, preserves order (linear time complexity).
`MemberIterator RemoveMember(MemberIterator)` uses a "move-last" trick to achieve constant time complexity. Basically the member at iterator is destructed, and then the last element is moved to that position. So the order of the remaining members are changed.
## Deep Copy Value {#DeepCopyValue}
If we really need to copy a DOM tree, we can use two APIs for deep copy: constructor with allocator, and `CopyFrom()`.
~~~~~~~~~~cpp
Document d;
Document::AllocatorType& a = d.GetAllocator();
Value v1("foo");
// Value v2(v1); // not allowed
Value v2(v1, a); // make a copy
assert(v1.IsString()); // v1 untouched
d.SetArray().PushBack(v1, a).PushBack(v2, a);
assert(v1.IsNull() && v2.IsNull()); // both moved to d
v2.CopyFrom(d, a); // copy whole document to v2
assert(d.IsArray() && d.Size() == 2); // d untouched
v1.SetObject().AddMember("array", v2, a);
d.PushBack(v1, a);
~~~~~~~~~~
## Swap Values {#SwapValues}
`Swap()` is also provided.
~~~~~~~~~~cpp
Value a(123);
Value b("Hello");
a.Swap(b);
assert(a.IsString());
assert(b.IsInt());
~~~~~~~~~~
Swapping two DOM trees is fast (constant time), despite the complexity of the trees.
# What's next {#WhatsNext}
This tutorial shows the basics of DOM tree query and manipulation. There are several important concepts in RapidJSON:
1. [Streams](doc/stream.md) are channels for reading/writing JSON, which can be a in-memory string, or file stream, etc. User can also create their streams.
2. [Encoding](doc/encoding.md) defines which character encoding is used in streams and memory. RapidJSON also provide Unicode conversion/validation internally.
3. [DOM](doc/dom.md)'s basics are already covered in this tutorial. Uncover more advanced features such as *in situ* parsing, other parsing options and advanced usages.
4. [SAX](doc/sax.md) is the foundation of parsing/generating facility in RapidJSON. Learn how to use `Reader`/`Writer` to implement even faster applications. Also try `PrettyWriter` to format the JSON.
5. [Performance](doc/performance.md) shows some in-house and third-party benchmarks.
6. [Internals](doc/internals.md) describes some internal designs and techniques of RapidJSON.
You may also refer to the [FAQ](doc/faq.md), API documentation, examples and unit tests.

View File

@ -0,0 +1,534 @@
# 教程
本教程简介文件对象模型Document Object Model, DOMAPI。
如 [用法一览](../readme.zh-cn.md#用法一览) 中所示,可以解析一个 JSON 至 DOM然后就可以轻松查询及修改 DOM并最终转换回 JSON。
[TOC]
# Value 及 Document {#ValueDocument}
每个 JSON 值都储存为 `Value` 类,而 `Document` 类则表示整个 DOM它存储了一个 DOM 树的根 `Value`。RapidJSON 的所有公开类型及函数都在 `rapidjson` 命名空间中。
# 查询 Value {#QueryValue}
在本节中,我们会使用到 `example/tutorial/tutorial.cpp` 中的代码片段。
假设我们用 C 语言的字符串储存一个 JSON`const char* json`
~~~~~~~~~~js
{
"hello": "world",
"t": true ,
"f": false,
"n": null,
"i": 123,
"pi": 3.1416,
"a": [1, 2, 3, 4]
}
~~~~~~~~~~
把它解析至一个 `Document`
~~~~~~~~~~cpp
#include "rapidjson/document.h"
using namespace rapidjson;
// ...
Document document;
document.Parse(json);
~~~~~~~~~~
那么现在该 JSON 就会被解析至 `document` 中,成为一棵 *DOM 树 *:
![教程中的 DOM](diagram/tutorial.png)
自从 RFC 7159 作出更新,合法 JSON 文件的根可以是任何类型的 JSON 值。而在较早的 RFC 4627 中,根值只允许是 Object 或 Array。而在上述例子中根是一个 Object。
~~~~~~~~~~cpp
assert(document.IsObject());
~~~~~~~~~~
让我们查询一下根 Object 中有没有 `"hello"` 成员。由于一个 `Value` 可包含不同类型的值,我们可能需要验证它的类型,并使用合适的 API 去获取其值。在此例中,`"hello"` 成员关联到一个 JSON String。
~~~~~~~~~~cpp
assert(document.HasMember("hello"));
assert(document["hello"].IsString());
printf("hello = %s\n", document["hello"].GetString());
~~~~~~~~~~
~~~~~~~~~~
world
~~~~~~~~~~
JSON True/False 值是以 `bool` 表示的。
~~~~~~~~~~cpp
assert(document["t"].IsBool());
printf("t = %s\n", document["t"].GetBool() ? "true" : "false");
~~~~~~~~~~
~~~~~~~~~~
true
~~~~~~~~~~
JSON Null 值可用 `IsNull()` 查询。
~~~~~~~~~~cpp
printf("n = %s\n", document["n"].IsNull() ? "null" : "?");
~~~~~~~~~~
~~~~~~~~~~
null
~~~~~~~~~~
JSON Number 类型表示所有数值。然而C++ 需要使用更专门的类型。
~~~~~~~~~~cpp
assert(document["i"].IsNumber());
// 在此情况下IsUint()/IsInt64()/IsUInt64() 也会返回 true
assert(document["i"].IsInt());
printf("i = %d\n", document["i"].GetInt());
// 另一种用法: (int)document["i"]
assert(document["pi"].IsNumber());
assert(document["pi"].IsDouble());
printf("pi = %g\n", document["pi"].GetDouble());
~~~~~~~~~~
~~~~~~~~~~
i = 123
pi = 3.1416
~~~~~~~~~~
JSON Array 包含一些元素。
~~~~~~~~~~cpp
// 使用引用来连续访问,方便之余还更高效。
const Value& a = document["a"];
assert(a.IsArray());
for (SizeType i = 0; i < a.Size(); i++) // 使用 SizeType 而不是 size_t
printf("a[%d] = %d\n", i, a[i].GetInt());
~~~~~~~~~~
~~~~~~~~~~
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
~~~~~~~~~~
注意RapidJSON 并不自动转换各种 JSON 类型。例如,对一个 String 的 Value 调用 `GetInt()` 是非法的。在调试模式下,它会被断言失败。在发布模式下,其行为是未定义的。
以下将会讨论有关查询各类型的细节。
## 查询 Array {#QueryArray}
缺省情况下,`SizeType``unsigned` 的 typedef。在多数系统中Array 最多能存储 2^32-1 个元素。
你可以用整数字面量访问元素,如 `a[0]``a[1]``a[2]`
Array 与 `std::vector` 相似,除了使用索引,也可使用迭代器来访问所有元素。
~~~~~~~~~~cpp
for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
printf("%d ", itr->GetInt());
~~~~~~~~~~
还有一些熟悉的查询函数:
* `SizeType Capacity() const`
* `bool Empty() const`
### 范围 for 循环 (v1.1.0 中的新功能)
当使用 C++11 功能时,你可使用范围 for 循环去访问 Array 内的所有元素。
~~~~~~~~~~cpp
for (auto& v : a.GetArray())
printf("%d ", v.GetInt());
~~~~~~~~~~
## 查询 Object {#QueryObject}
和 Array 相似,我们可以用迭代器去访问所有 Object 成员:
~~~~~~~~~~cpp
static const char* kTypeNames[] =
{ "Null", "False", "True", "Object", "Array", "String", "Number" };
for (Value::ConstMemberIterator itr = document.MemberBegin();
itr != document.MemberEnd(); ++itr)
{
printf("Type of member %s is %s\n",
itr->name.GetString(), kTypeNames[itr->value.GetType()]);
}
~~~~~~~~~~
~~~~~~~~~~
Type of member hello is String
Type of member t is True
Type of member f is False
Type of member n is Null
Type of member i is Number
Type of member pi is Number
Type of member a is Array
~~~~~~~~~~
注意,当 `operator[](const char*)` 找不到成员,它会断言失败。
若我们不确定一个成员是否存在,便需要在调用 `operator[](const char*)` 前先调用 `HasMember()`。然而,这会导致两次查找。更好的做法是调用 `FindMember()`,它能同时检查成员是否存在并返回它的 Value
~~~~~~~~~~cpp
Value::ConstMemberIterator itr = document.FindMember("hello");
if (itr != document.MemberEnd())
printf("%s\n", itr->value.GetString());
~~~~~~~~~~
### 范围 for 循环 (v1.1.0 中的新功能)
当使用 C++11 功能时,你可使用范围 for 循环去访问 Object 内的所有成员。
~~~~~~~~~~cpp
for (auto& m : document.GetObject())
printf("Type of member %s is %s\n",
m.name.GetString(), kTypeNames[m.value.GetType()]);
~~~~~~~~~~
## 查询 Number {#QueryNumber}
JSON 只提供一种数值类型──Number。数字可以是整数或实数。RFC 4627 规定数字的范围由解析器指定。
由于 C++ 提供多种整数及浮点数类型DOM 尝试尽量提供最广的范围及良好性能。
当解析一个 Number 时, 它会被存储在 DOM 之中,成为下列其中一个类型:
类型 | 描述
-----------|---------------------------------------
`unsigned` | 32 位无号整数
`int` | 32 位有号整数
`uint64_t` | 64 位无号整数
`int64_t` | 64 位有号整数
`double` | 64 位双精度浮点数
当查询一个 Number 时, 你可以检查该数字是否能以目标类型来提取:
查检 | 提取
------------------|---------------------
`bool IsNumber()` | 不适用
`bool IsUint()` | `unsigned GetUint()`
`bool IsInt()` | `int GetInt()`
`bool IsUint64()` | `uint64_t GetUint64()`
`bool IsInt64()` | `int64_t GetInt64()`
`bool IsDouble()` | `double GetDouble()`
注意,一个整数可能用几种类型来提取,而无需转换。例如,一个名为 `x` 的 Value 包含 123那么 `x.IsInt() == x.IsUint() == x.IsInt64() == x.IsUint64() == true`。但如果一个名为 `y` 的 Value 包含 -3000000000那么仅会令 `x.IsInt64() == true`
当要提取 Number 类型,`GetDouble()` 是会把内部整数的表示转换成 `double`。注意 `int``unsigned` 可以安全地转换至 `double`,但 `int64_t``uint64_t` 可能会丧失精度(因为 `double` 的尾数只有 52 位)。
## 查询 String {#QueryString}
除了 `GetString()``Value` 类也有一个 `GetStringLength()`。这里会解释个中原因。
根据 RFC 4627JSON String 可包含 Unicode 字符 `U+0000`,在 JSON 中会表示为 `"\u0000"`。问题是C/C++ 通常使用空字符结尾字符串null-terminated string这种字符串把 ``\0'` 作为结束符号。
为了符合 RFC 4627RapidJSON 支持包含 `U+0000` 的 String。若你需要处理这些 String便可使用 `GetStringLength()` 去获得正确的字符串长度。
例如,当解析以下的 JSON 至 `Document d` 之后:
~~~~~~~~~~js
{ "s" : "a\u0000b" }
~~~~~~~~~~
`"a\u0000b"` 值的正确长度应该是 3。但 `strlen()` 会返回 1。
`GetStringLength()` 也可以提高性能,因为用户可能需要调用 `strlen()` 去分配缓冲。
此外,`std::string` 也支持这个构造函数:
~~~~~~~~~~cpp
string(const char* s, size_t count);
~~~~~~~~~~
此构造函数接受字符串长度作为参数。它支持在字符串中存储空字符,也应该会有更好的性能。
## 比较两个 Value
你可使用 `==``!=` 去比较两个 Value。当且仅当两个 Value 的类型及内容相同,它们才当作相等。你也可以比较 Value 和它的原生类型值。以下是一个例子。
~~~~~~~~~~cpp
if (document["hello"] == document["n"]) /*...*/; // 比较两个值
if (document["hello"] == "world") /*...*/; // 与字符串家面量作比较
if (document["i"] != 123) /*...*/; // 与整数作比较
if (document["pi"] != 3.14) /*...*/; // 与 double 作比较
~~~~~~~~~~
ArrayObject 顺序以它们的元素/成员作比较。当且仅当它们的整个子树相等,它们才当作相等。
注意,现时若一个 Object 含有重复命名的成员,它与任何 Object 作比较都总会返回 `false`
# 创建/修改值 {#CreateModifyValues}
有多种方法去创建值。 当一个 DOM 树被创建或修改后,可使用 `Writer` 再次存储为 JSON。
## 改变 Value 类型 {#ChangeValueType}
当使用默认构造函数创建一个 Value 或 Document它的类型便会是 Null。要改变其类型需调用 `SetXXX()` 或赋值操作,例如:
~~~~~~~~~~cpp
Document d; // Null
d.SetObject();
Value v; // Null
v.SetInt(10);
v = 10; // 简写,和上面的相同
~~~~~~~~~~
### 构造函数的各个重载
几个类型也有重载构造函数:
~~~~~~~~~~cpp
Value b(true); // 调用 Value(bool)
Value i(-123); // 调用 Value(int)
Value u(123u); // 调用 Value(unsigned)
Value d(1.5); // 调用 Value(double)
~~~~~~~~~~
要重建空 Object 或 Array可在默认构造函数后使用 `SetObject()`/`SetArray()`,或一次性使用 `Value(Type)`
~~~~~~~~~~cpp
Value o(kObjectType);
Value a(kArrayType);
~~~~~~~~~~
## 转移语意Move Semantics {#MoveSemantics}
在设计 RapidJSON 时有一个非常特别的决定,就是 Value 赋值并不是把来源 Value 复制至目的 Value而是把把来源 Value 转移move至目的 Value。例如
~~~~~~~~~~cpp
Value a(123);
Value b(456);
b = a; // a 变成 Nullb 变成数字 123。
~~~~~~~~~~
![使用移动语意赋值。](diagram/move1.png)
为什么?此语意有何优点?
最简单的答案就是性能。对于固定大小的 JSON 类型Number、True、False、Null复制它们是简单快捷。然而对于可变大小的 JSON 类型String、Array、Object复制它们会产生大量开销而且这些开销常常不被察觉。尤其是当我们需要创建临时 Object把它复制至另一变量然后再析构它。
例如,若使用正常 * 复制 * 语意:
~~~~~~~~~~cpp
Value o(kObjectType);
{
Value contacts(kArrayType);
// 把元素加进 contacts 数组。
// ...
o.AddMember("contacts", contacts, d.GetAllocator()); // 深度复制 contacts (可能有大量内存分配)
// 析构 contacts。
}
~~~~~~~~~~
![复制语意产生大量的复制操作。](diagram/move2.png)
那个 `o` Object 需要分配一个和 contacts 相同大小的缓冲区,对 conacts 做深度复制,并最终要析构 contacts。这样会产生大量无必要的内存分配释放以及内存复制。
有一些方案可避免实质地复制这些数据例如引用计数reference counting、垃圾回收garbage collection, GC
为了使 RapidJSON 简单及快速,我们选择了对赋值采用 * 转移 * 语意。这方法与 `std::auto_ptr` 相似,都是在赋值时转移拥有权。转移快得多简单得多,只需要析构原来的 Value把来源 `memcpy()` 至目标,最后把来源设置为 Null 类型。
因此,使用转移语意后,上面的例子变成:
~~~~~~~~~~cpp
Value o(kObjectType);
{
Value contacts(kArrayType);
// adding elements to contacts array.
o.AddMember("contacts", contacts, d.GetAllocator()); // 只需 memcpy() contacts 本身至新成员的 Value16 字节)
// contacts 在这里变成 Null。它的析构是平凡的。
}
~~~~~~~~~~
![转移语意不需复制。](diagram/move3.png)
在 C++11 中这称为转移赋值操作move assignment operator。由于 RapidJSON 支持 C++03它在赋值操作采用转移语意其它修改形函数如 `AddMember()`, `PushBack()` 也采用转移语意。
### 转移语意及临时值 {#TemporaryValues}
有时候,我们想直接构造一个 Value 并传递给一个“转移”函数(如 `PushBack()``AddMember()`)。由于临时对象是不能转换为正常的 Value 引用,我们加入了一个方便的 `Move()` 函数:
~~~~~~~~~~cpp
Value a(kArrayType);
Document::AllocatorType& allocator = document.GetAllocator();
// a.PushBack(Value(42), allocator); // 不能通过编译
a.PushBack(Value().SetInt(42), allocator); // fluent API
a.PushBack(Value(42).Move(), allocator); // 和上一行相同
~~~~~~~~~~
## 创建 String {#CreateString}
RapidJSON 提供两个 String 的存储策略。
1. copy-string: 分配缓冲区,然后把来源数据复制至它。
2. const-string: 简单地储存字符串的指针。
Copy-string 总是安全的因为它拥有数据的克隆。Const-string 可用于存储字符串字面量,以及用于在 DOM 一节中将会提到的 in-situ 解析中。
为了让用户自定义内存分配方式当一个操作可能需要内存分配时RapidJSON 要求用户传递一个 allocator 实例作为 API 参数。此设计避免了在每个 Value 存储 allocator或 document的指针。
因此,当我们把一个 copy-string 赋值时, 调用含有 allocator 的 `SetString()` 重载函数:
~~~~~~~~~~cpp
Document document;
Value author;
char buffer[10];
int len = sprintf(buffer, "%s %s", "Milo", "Yip"); // 动态创建的字符串。
author.SetString(buffer, len, document.GetAllocator());
memset(buffer, 0, sizeof(buffer));
// 清空 buffer 后 author.GetString() 仍然包含 "Milo Yip"
~~~~~~~~~~
在此例子中,我们使用 `Document` 实例的 allocator。这是使用 RapidJSON 时常用的惯用法。但你也可以用其他 allocator 实例。
另外,上面的 `SetString()` 需要长度参数。这个 API 能处理含有空字符的字符串。另一个 `SetString()` 重载函数没有长度参数,它假设输入是空字符结尾的,并会调用类似 `strlen()` 的函数去获取长度。
最后,对于字符串字面量或有安全生命周期的字符串,可以使用 const-string 版本的 `SetString()`,它没有 allocator 参数。对于字符串家面量(或字符数组常量),只需简单地传递字面量,又安全又高效:
~~~~~~~~~~cpp
Value s;
s.SetString("rapidjson"); // 可包含空字符,长度在编译萁推导
s = "rapidjson"; // 上行的缩写
~~~~~~~~~~
对于字符指针RapidJSON 需要作一个标记,代表它不复制也是安全的。可以使用 `StringRef` 函数:
~~~~~~~~~cpp
const char * cstr = getenv("USER");
size_t cstr_len = ...; // 如果有长度
Value s;
// s.SetString(cstr); // 这不能通过编译
s.SetString(StringRef(cstr)); // 可以,假设它的生命周期安全,并且是以空字符结尾的
s = StringRef(cstr); // 上行的缩写
s.SetString(StringRef(cstr, cstr_len));// 更快,可处理空字符
s = StringRef(cstr, cstr_len); // 上行的缩写
~~~~~~~~~
## 修改 Array {#ModifyArray}
Array 类型的 Value 提供与 `std::vector` 相似的 API。
* `Clear()`
* `Reserve(SizeType, Allocator&)`
* `Value& PushBack(Value&, Allocator&)`
* `template <typename T> GenericValue& PushBack(T, Allocator&)`
* `Value& PopBack()`
* `ValueIterator Erase(ConstValueIterator pos)`
* `ValueIterator Erase(ConstValueIterator first, ConstValueIterator last)`
注意,`Reserve(...)``PushBack(...)` 可能会为数组元素分配内存,所以需要一个 allocator。
以下是 `PushBack()` 的例子:
~~~~~~~~~~cpp
Value a(kArrayType);
Document::AllocatorType& allocator = document.GetAllocator();
for (int i = 5; i <= 10; i++)
a.PushBack(i, allocator); // 可能需要调用 realloc() 所以需要 allocator
// 流畅接口Fluent interface
a.PushBack("Lua", allocator).PushBack("Mio", allocator);
~~~~~~~~~~
与 STL 不一样的是,`PushBack()`/`PopBack()` 返回 Array 本身的引用。这称为流畅接口_fluent interface_
如果你想在 Array 中加入一个非常量字符串,或是一个没有足够生命周期的字符串(见 [Create String](#CreateString)),你需要使用 copy-string API 去创建一个 String。为了避免加入中间变量可以就地使用一个 [临时值](#TemporaryValues)
~~~~~~~~~~cpp
// 就地 Value 参数
contact.PushBack(Value("copy", document.GetAllocator()).Move(), // copy string
document.GetAllocator());
// 显式 Value 参数
Value val("key", document.GetAllocator()); // copy string
contact.PushBack(val, document.GetAllocator());
~~~~~~~~~~
## 修改 Object {#ModifyObject}
Object 是键值对的集合。每个键必须为 String。要修改 Object方法是增加或移除成员。以下的 API 用来增加城员:
* `Value& AddMember(Value&, Value&, Allocator& allocator)`
* `Value& AddMember(StringRefType, Value&, Allocator&)`
* `template <typename T> Value& AddMember(StringRefType, T value, Allocator&)`
以下是一个例子。
~~~~~~~~~~cpp
Value contact(kObject);
contact.AddMember("name", "Milo", document.GetAllocator());
contact.AddMember("married", true, document.GetAllocator());
~~~~~~~~~~
使用 `StringRefType` 作为 name 参数的重载版本与字符串的 `SetString` 的接口相似。 这些重载是为了避免复制 `name` 字符串,因为 JSON object 中经常会使用常数键名。
如果你需要从非常数字符串或生命周期不足的字符串创建键名(见 [创建 String](#CreateString)),你需要使用 copy-string API。为了避免中间变量可以就地使用 [临时值](#TemporaryValues)
~~~~~~~~~~cpp
// 就地 Value 参数
contact.AddMember(Value("copy", document.GetAllocator()).Move(), // copy string
Value().Move(), // null value
document.GetAllocator());
// 显式参数
Value key("key", document.GetAllocator()); // copy string name
Value val(42); // 某 Value
contact.AddMember(key, val, document.GetAllocator());
~~~~~~~~~~
移除成员有几个选择:
* `bool RemoveMember(const Ch* name)`:使用键名来移除成员(线性时间复杂度)。
* `bool RemoveMember(const Value& name)`:除了 `name` 是一个 Value和上一行相同。
* `MemberIterator RemoveMember(MemberIterator)`使用迭代器移除成员_ 常数 _ 时间复杂度)。
* `MemberIterator EraseMember(MemberIterator)`:和上行相似但维持成员次序(线性时间复杂度)。
* `MemberIterator EraseMember(MemberIterator first, MemberIterator last)`:移除一个范围内的成员,维持次序(线性时间复杂度)。
`MemberIterator RemoveMember(MemberIterator)` 使用了“转移最后”手法来达成常数时间复杂度。基本上就是析构迭代器位置的成员,然后把最后的成员转移至迭代器位置。因此,成员的次序会被改变。
## 深复制 Value {#DeepCopyValue}
若我们真的要复制一个 DOM 树,我们可使用两个 APIs 作深复制:含 allocator 的构造函数及 `CopyFrom()`
~~~~~~~~~~cpp
Document d;
Document::AllocatorType& a = d.GetAllocator();
Value v1("foo");
// Value v2(v1); // 不容许
Value v2(v1, a); // 制造一个克隆
assert(v1.IsString()); // v1 不变
d.SetArray().PushBack(v1, a).PushBack(v2, a);
assert(v1.IsNull() && v2.IsNull()); // 两个都转移动 d
v2.CopyFrom(d, a); // 把整个 document 复制至 v2
assert(d.IsArray() && d.Size() == 2); // d 不变
v1.SetObject().AddMember("array", v2, a);
d.PushBack(v1, a);
~~~~~~~~~~
## 交换 Value {#SwapValues}
RapidJSON 也提供 `Swap()`
~~~~~~~~~~cpp
Value a(123);
Value b("Hello");
a.Swap(b);
assert(a.IsString());
assert(b.IsInt());
~~~~~~~~~~
无论两棵 DOM 树有多复杂,交换是很快的(常数时间)。
# 下一部分 {#WhatsNext}
本教程展示了如何询查及修改 DOM 树。RapidJSON 还有一个重要概念:
1. [](doc/stream.zh-cn.md) 是读写 JSON 的通道。流可以是内存字符串、文件流等。用户也可以自定义流。
2. [编码](doc/encoding.zh-cn.md) 定义在流或内存中使用的字符编码。RapidJSON 也在内部提供 Unicode 转换及校验功能。
3. [DOM](doc/dom.zh-cn.md) 的基本功能已在本教程里介绍。还有更高级的功能,如原位(*in situ*)解析、其他解析选项及高级用法。
4. [SAX](doc/sax.zh-cn.md) 是 RapidJSON 解析/生成功能的基础。学习使用 `Reader`/`Writer` 去实现更高性能的应用程序。也可以使用 `PrettyWriter` 去格式化 JSON。
5. [性能](doc/performance.zh-cn.md) 展示一些我们做的及第三方的性能测试。
6. [技术内幕](doc/internals.md) 讲述一些 RapidJSON 内部的设计及技术。
你也可以参考 [常见问题](doc/faq.zh-cn.md)、API 文档、例子及单元测试。

View File

@ -0,0 +1,8 @@
# BUILD: docker build -t rapidjson-debian .
# RUN: docker run -it -v "$PWD"/../..:/rapidjson rapidjson-debian
FROM debian:jessie
RUN apt-get update && apt-get install -y g++ cmake doxygen valgrind
ENTRYPOINT ["/bin/bash"]

View File

@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 2.8)
if(POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif()
set(EXAMPLES
capitalize
condense
filterkey
filterkeydom
jsonx
messagereader
parsebyparts
pretty
prettyauto
schemavalidator
serialize
simpledom
simplereader
simplewriter
tutorial)
include_directories("../include/")
add_definitions(-D__STDC_FORMAT_MACROS)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -Werror -Wall -Wextra -Weffc++ -Wswitch-default")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal -Wimplicit-fallthrough -Weverything")
endif()
foreach (example ${EXAMPLES})
add_executable(${example} ${example}/${example}.cpp)
endforeach()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_link_libraries(parsebyparts pthread)
endif()
add_custom_target(examples ALL DEPENDS ${EXAMPLES})

View File

@ -0,0 +1,67 @@
// JSON condenser example
// This example parses JSON from stdin with validation,
// and re-output the JSON content to stdout with all string capitalized, and without whitespace.
#include "rapidjson/reader.h"
#include "rapidjson/writer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/error/en.h"
#include <vector>
#include <cctype>
using namespace rapidjson;
template<typename OutputHandler>
struct CapitalizeFilter {
CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() {}
bool Null() { return out_.Null(); }
bool Bool(bool b) { return out_.Bool(b); }
bool Int(int i) { return out_.Int(i); }
bool Uint(unsigned u) { return out_.Uint(u); }
bool Int64(int64_t i) { return out_.Int64(i); }
bool Uint64(uint64_t u) { return out_.Uint64(u); }
bool Double(double d) { return out_.Double(d); }
bool RawNumber(const char* str, SizeType length, bool copy) { return out_.RawNumber(str, length, copy); }
bool String(const char* str, SizeType length, bool) {
buffer_.clear();
for (SizeType i = 0; i < length; i++)
buffer_.push_back(static_cast<char>(std::toupper(str[i])));
return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string
}
bool StartObject() { return out_.StartObject(); }
bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); }
bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); }
bool StartArray() { return out_.StartArray(); }
bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); }
OutputHandler& out_;
std::vector<char> buffer_;
private:
CapitalizeFilter(const CapitalizeFilter&);
CapitalizeFilter& operator=(const CapitalizeFilter&);
};
int main(int, char*[]) {
// Prepare JSON reader and input stream.
Reader reader;
char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare JSON writer and output stream.
char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output.
CapitalizeFilter<Writer<FileWriteStream> > filter(writer);
if (!reader.Parse(is, filter)) {
fprintf(stderr, "\nError(%u): %s\n", static_cast<unsigned>(reader.GetErrorOffset()), GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
return 0;
}

View File

@ -0,0 +1,32 @@
// JSON condenser example
// This example parses JSON text from stdin with validation,
// and re-output the JSON content to stdout without whitespace.
#include "rapidjson/reader.h"
#include "rapidjson/writer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/error/en.h"
using namespace rapidjson;
int main(int, char*[]) {
// Prepare JSON reader and input stream.
Reader reader;
char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare JSON writer and output stream.
char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output.
if (!reader.Parse(is, writer)) {
fprintf(stderr, "\nError(%u): %s\n", static_cast<unsigned>(reader.GetErrorOffset()), GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
return 0;
}

View File

@ -0,0 +1,135 @@
// JSON filterkey example with SAX-style API.
// This example parses JSON text from stdin with validation.
// During parsing, specified key will be filtered using a SAX handler.
// It re-output the JSON content to stdout without whitespace.
#include "rapidjson/reader.h"
#include "rapidjson/writer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/error/en.h"
#include <stack>
using namespace rapidjson;
// This handler forwards event into an output handler, with filtering the descendent events of specified key.
template <typename OutputHandler>
class FilterKeyHandler {
public:
typedef char Ch;
FilterKeyHandler(OutputHandler& outputHandler, const Ch* keyString, SizeType keyLength) :
outputHandler_(outputHandler), keyString_(keyString), keyLength_(keyLength), filterValueDepth_(), filteredKeyCount_()
{}
bool Null() { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Null() && EndValue(); }
bool Bool(bool b) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Bool(b) && EndValue(); }
bool Int(int i) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Int(i) && EndValue(); }
bool Uint(unsigned u) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Uint(u) && EndValue(); }
bool Int64(int64_t i) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Int64(i) && EndValue(); }
bool Uint64(uint64_t u) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Uint64(u) && EndValue(); }
bool Double(double d) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Double(d) && EndValue(); }
bool RawNumber(const Ch* str, SizeType len, bool copy) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.RawNumber(str, len, copy) && EndValue(); }
bool String (const Ch* str, SizeType len, bool copy) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.String (str, len, copy) && EndValue(); }
bool StartObject() {
if (filterValueDepth_ > 0) {
filterValueDepth_++;
return true;
}
else {
filteredKeyCount_.push(0);
return outputHandler_.StartObject();
}
}
bool Key(const Ch* str, SizeType len, bool copy) {
if (filterValueDepth_ > 0)
return true;
else if (len == keyLength_ && std::memcmp(str, keyString_, len) == 0) {
filterValueDepth_ = 1;
return true;
}
else {
++filteredKeyCount_.top();
return outputHandler_.Key(str, len, copy);
}
}
bool EndObject(SizeType) {
if (filterValueDepth_ > 0) {
filterValueDepth_--;
return EndValue();
}
else {
// Use our own filtered memberCount
SizeType memberCount = filteredKeyCount_.top();
filteredKeyCount_.pop();
return outputHandler_.EndObject(memberCount) && EndValue();
}
}
bool StartArray() {
if (filterValueDepth_ > 0) {
filterValueDepth_++;
return true;
}
else
return outputHandler_.StartArray();
}
bool EndArray(SizeType elementCount) {
if (filterValueDepth_ > 0) {
filterValueDepth_--;
return EndValue();
}
else
return outputHandler_.EndArray(elementCount) && EndValue();
}
private:
FilterKeyHandler(const FilterKeyHandler&);
FilterKeyHandler& operator=(const FilterKeyHandler&);
bool EndValue() {
if (filterValueDepth_ == 1) // Just at the end of value after filtered key
filterValueDepth_ = 0;
return true;
}
OutputHandler& outputHandler_;
const char* keyString_;
const SizeType keyLength_;
unsigned filterValueDepth_;
std::stack<SizeType> filteredKeyCount_;
};
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "filterkey key < input.json > output.json\n");
return 1;
}
// Prepare JSON reader and input stream.
Reader reader;
char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare JSON writer and output stream.
char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os);
// Prepare Filter
FilterKeyHandler<Writer<FileWriteStream> > filter(writer, argv[1], static_cast<SizeType>(strlen(argv[1])));
// JSON reader parse from the input stream, filter handler filters the events, and forward to writer.
// i.e. the events flow is: reader -> filter -> writer
if (!reader.Parse(is, filter)) {
fprintf(stderr, "\nError(%u): %s\n", static_cast<unsigned>(reader.GetErrorOffset()), GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
return 0;
}

View File

@ -0,0 +1,170 @@
// JSON filterkey example which populates filtered SAX events into a Document.
// This example parses JSON text from stdin with validation.
// During parsing, specified key will be filtered using a SAX handler.
// And finally the filtered events are used to populate a Document.
// As an example, the document is written to standard output.
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/error/en.h"
#include <stack>
using namespace rapidjson;
// This handler forwards event into an output handler, with filtering the descendent events of specified key.
template <typename OutputHandler>
class FilterKeyHandler {
public:
typedef char Ch;
FilterKeyHandler(OutputHandler& outputHandler, const Ch* keyString, SizeType keyLength) :
outputHandler_(outputHandler), keyString_(keyString), keyLength_(keyLength), filterValueDepth_(), filteredKeyCount_()
{}
bool Null() { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Null() && EndValue(); }
bool Bool(bool b) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Bool(b) && EndValue(); }
bool Int(int i) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Int(i) && EndValue(); }
bool Uint(unsigned u) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Uint(u) && EndValue(); }
bool Int64(int64_t i) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Int64(i) && EndValue(); }
bool Uint64(uint64_t u) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Uint64(u) && EndValue(); }
bool Double(double d) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.Double(d) && EndValue(); }
bool RawNumber(const Ch* str, SizeType len, bool copy) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.RawNumber(str, len, copy) && EndValue(); }
bool String (const Ch* str, SizeType len, bool copy) { return filterValueDepth_ > 0 ? EndValue() : outputHandler_.String (str, len, copy) && EndValue(); }
bool StartObject() {
if (filterValueDepth_ > 0) {
filterValueDepth_++;
return true;
}
else {
filteredKeyCount_.push(0);
return outputHandler_.StartObject();
}
}
bool Key(const Ch* str, SizeType len, bool copy) {
if (filterValueDepth_ > 0)
return true;
else if (len == keyLength_ && std::memcmp(str, keyString_, len) == 0) {
filterValueDepth_ = 1;
return true;
}
else {
++filteredKeyCount_.top();
return outputHandler_.Key(str, len, copy);
}
}
bool EndObject(SizeType) {
if (filterValueDepth_ > 0) {
filterValueDepth_--;
return EndValue();
}
else {
// Use our own filtered memberCount
SizeType memberCount = filteredKeyCount_.top();
filteredKeyCount_.pop();
return outputHandler_.EndObject(memberCount) && EndValue();
}
}
bool StartArray() {
if (filterValueDepth_ > 0) {
filterValueDepth_++;
return true;
}
else
return outputHandler_.StartArray();
}
bool EndArray(SizeType elementCount) {
if (filterValueDepth_ > 0) {
filterValueDepth_--;
return EndValue();
}
else
return outputHandler_.EndArray(elementCount) && EndValue();
}
private:
FilterKeyHandler(const FilterKeyHandler&);
FilterKeyHandler& operator=(const FilterKeyHandler&);
bool EndValue() {
if (filterValueDepth_ == 1) // Just at the end of value after filtered key
filterValueDepth_ = 0;
return true;
}
OutputHandler& outputHandler_;
const char* keyString_;
const SizeType keyLength_;
unsigned filterValueDepth_;
std::stack<SizeType> filteredKeyCount_;
};
// Implements a generator for Document::Populate()
template <typename InputStream>
class FilterKeyReader {
public:
typedef char Ch;
FilterKeyReader(InputStream& is, const Ch* keyString, SizeType keyLength) :
is_(is), keyString_(keyString), keyLength_(keyLength), parseResult_()
{}
// SAX event flow: reader -> filter -> handler
template <typename Handler>
bool operator()(Handler& handler) {
FilterKeyHandler<Handler> filter(handler, keyString_, keyLength_);
Reader reader;
parseResult_ = reader.Parse(is_, filter);
return parseResult_;
}
const ParseResult& GetParseResult() const { return parseResult_; }
private:
FilterKeyReader(const FilterKeyReader&);
FilterKeyReader& operator=(const FilterKeyReader&);
InputStream& is_;
const char* keyString_;
const SizeType keyLength_;
ParseResult parseResult_;
};
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "filterkeydom key < input.json > output.json\n");
return 1;
}
// Prepare input stream.
char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare Filter
FilterKeyReader<FileReadStream> reader(is, argv[1], static_cast<SizeType>(strlen(argv[1])));
// Populates the filtered events from reader
Document document;
document.Populate(reader);
ParseResult pr = reader.GetParseResult();
if (!pr) {
fprintf(stderr, "\nError(%u): %s\n", static_cast<unsigned>(pr.Offset()), GetParseError_En(pr.Code()));
return 1;
}
// Prepare JSON writer and output stream.
char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os);
// Write the document to standard output
document.Accept(writer);
return 0;
}

View File

@ -0,0 +1,207 @@
// JSON to JSONx conversion exmaple, using SAX API.
// JSONx is an IBM standard format to represent JSON as XML.
// https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html
// This example parses JSON text from stdin with validation,
// and convert to JSONx format to stdout.
// Need compile with -D__STDC_FORMAT_MACROS for defining PRId64 and PRIu64 macros.
#include "rapidjson/reader.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/error/en.h"
#include <cstdio>
using namespace rapidjson;
// For simplicity, this example only read/write in UTF-8 encoding
template <typename OutputStream>
class JsonxWriter {
public:
JsonxWriter(OutputStream& os) : os_(os), name_(), level_(0), hasName_(false) {
}
bool Null() {
return WriteStartElement("null", true);
}
bool Bool(bool b) {
return
WriteStartElement("boolean") &&
WriteString(b ? "true" : "false") &&
WriteEndElement("boolean");
}
bool Int(int i) {
char buffer[12];
return WriteNumberElement(buffer, sprintf(buffer, "%d", i));
}
bool Uint(unsigned i) {
char buffer[11];
return WriteNumberElement(buffer, sprintf(buffer, "%u", i));
}
bool Int64(int64_t i) {
char buffer[21];
return WriteNumberElement(buffer, sprintf(buffer, "%" PRId64, i));
}
bool Uint64(uint64_t i) {
char buffer[21];
return WriteNumberElement(buffer, sprintf(buffer, "%" PRIu64, i));
}
bool Double(double d) {
char buffer[30];
return WriteNumberElement(buffer, sprintf(buffer, "%.17g", d));
}
bool RawNumber(const char* str, SizeType length, bool) {
return
WriteStartElement("number") &&
WriteEscapedText(str, length) &&
WriteEndElement("number");
}
bool String(const char* str, SizeType length, bool) {
return
WriteStartElement("string") &&
WriteEscapedText(str, length) &&
WriteEndElement("string");
}
bool StartObject() {
return WriteStartElement("object");
}
bool Key(const char* str, SizeType length, bool) {
// backup key to name_
name_.Clear();
for (SizeType i = 0; i < length; i++)
name_.Put(str[i]);
hasName_ = true;
return true;
}
bool EndObject(SizeType) {
return WriteEndElement("object");
}
bool StartArray() {
return WriteStartElement("array");
}
bool EndArray(SizeType) {
return WriteEndElement("array");
}
private:
bool WriteString(const char* s) {
while (*s)
os_.Put(*s++);
return true;
}
bool WriteEscapedAttributeValue(const char* s, size_t length) {
for (size_t i = 0; i < length; i++) {
switch (s[i]) {
case '&': WriteString("&amp;"); break;
case '<': WriteString("&lt;"); break;
case '"': WriteString("&quot;"); break;
default: os_.Put(s[i]); break;
}
}
return true;
}
bool WriteEscapedText(const char* s, size_t length) {
for (size_t i = 0; i < length; i++) {
switch (s[i]) {
case '&': WriteString("&amp;"); break;
case '<': WriteString("&lt;"); break;
default: os_.Put(s[i]); break;
}
}
return true;
}
bool WriteStartElement(const char* type, bool emptyElement = false) {
if (level_ == 0)
if (!WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
return false;
if (!WriteString("<json:") || !WriteString(type))
return false;
// For root element, need to add declarations
if (level_ == 0) {
if (!WriteString(
" xsi:schemaLocation=\"http://www.datapower.com/schemas/json jsonx.xsd\""
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
" xmlns:json=\"http://www.ibm.com/xmlns/prod/2009/jsonx\""))
return false;
}
if (hasName_) {
hasName_ = false;
if (!WriteString(" name=\"") ||
!WriteEscapedAttributeValue(name_.GetString(), name_.GetSize()) ||
!WriteString("\""))
return false;
}
if (emptyElement)
return WriteString("/>");
else {
level_++;
return WriteString(">");
}
}
bool WriteEndElement(const char* type) {
if (!WriteString("</json:") ||
!WriteString(type) ||
!WriteString(">"))
return false;
// For the last end tag, flush the output stream.
if (--level_ == 0)
os_.Flush();
return true;
}
bool WriteNumberElement(const char* buffer, int length) {
if (!WriteStartElement("number"))
return false;
for (int j = 0; j < length; j++)
os_.Put(buffer[j]);
return WriteEndElement("number");
}
OutputStream& os_;
StringBuffer name_;
unsigned level_;
bool hasName_;
};
int main(int, char*[]) {
// Prepare JSON reader and input stream.
Reader reader;
char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare JSON writer and output stream.
char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
JsonxWriter<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output.
if (!reader.Parse(is, writer)) {
fprintf(stderr, "\nError(%u): %s\n", static_cast<unsigned>(reader.GetErrorOffset()), GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
return 0;
}

View File

@ -0,0 +1,105 @@
// Reading a message JSON with Reader (SAX-style API).
// The JSON should be an object with key-string pairs.
#include "rapidjson/reader.h"
#include "rapidjson/error/en.h"
#include <iostream>
#include <string>
#include <map>
using namespace std;
using namespace rapidjson;
typedef map<string, string> MessageMap;
#if defined(__GNUC__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(switch-enum)
#endif
struct MessageHandler
: public BaseReaderHandler<UTF8<>, MessageHandler> {
MessageHandler() : messages_(), state_(kExpectObjectStart), name_() {}
bool StartObject() {
switch (state_) {
case kExpectObjectStart:
state_ = kExpectNameOrObjectEnd;
return true;
default:
return false;
}
}
bool String(const char* str, SizeType length, bool) {
switch (state_) {
case kExpectNameOrObjectEnd:
name_ = string(str, length);
state_ = kExpectValue;
return true;
case kExpectValue:
messages_.insert(MessageMap::value_type(name_, string(str, length)));
state_ = kExpectNameOrObjectEnd;
return true;
default:
return false;
}
}
bool EndObject(SizeType) { return state_ == kExpectNameOrObjectEnd; }
bool Default() { return false; } // All other events are invalid.
MessageMap messages_;
enum State {
kExpectObjectStart,
kExpectNameOrObjectEnd,
kExpectValue
}state_;
std::string name_;
};
#if defined(__GNUC__)
RAPIDJSON_DIAG_POP
#endif
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
static void ParseMessages(const char* json, MessageMap& messages) {
Reader reader;
MessageHandler handler;
StringStream ss(json);
if (reader.Parse(ss, handler))
messages.swap(handler.messages_); // Only change it if success.
else {
ParseErrorCode e = reader.GetParseErrorCode();
size_t o = reader.GetErrorOffset();
cout << "Error: " << GetParseError_En(e) << endl;;
cout << " at offset " << o << " near '" << string(json).substr(o, 10) << "...'" << endl;
}
}
int main() {
MessageMap messages;
const char* json1 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }";
cout << json1 << endl;
ParseMessages(json1, messages);
for (MessageMap::const_iterator itr = messages.begin(); itr != messages.end(); ++itr)
cout << itr->first << ": " << itr->second << endl;
cout << endl << "Parse a JSON with invalid schema." << endl;
const char* json2 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\", \"foo\" : {} }";
cout << json2 << endl;
ParseMessages(json2, messages);
return 0;
}

View File

@ -0,0 +1,173 @@
// Example of parsing JSON to document by parts.
// Using C++11 threads
// Temporarily disable for clang (older version) due to incompatibility with libstdc++
#if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1700)) && !defined(__clang__)
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/writer.h"
#include "rapidjson/ostreamwrapper.h"
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
using namespace rapidjson;
template<unsigned parseFlags = kParseDefaultFlags>
class AsyncDocumentParser {
public:
AsyncDocumentParser(Document& d)
: stream_(*this)
, d_(d)
, parseThread_(&AsyncDocumentParser::Parse, this)
, mutex_()
, notEmpty_()
, finish_()
, completed_()
{}
~AsyncDocumentParser() {
if (!parseThread_.joinable())
return;
{
std::unique_lock<std::mutex> lock(mutex_);
// Wait until the buffer is read up (or parsing is completed)
while (!stream_.Empty() && !completed_)
finish_.wait(lock);
// Automatically append '\0' as the terminator in the stream.
static const char terminator[] = "";
stream_.src_ = terminator;
stream_.end_ = terminator + 1;
notEmpty_.notify_one(); // unblock the AsyncStringStream
}
parseThread_.join();
}
void ParsePart(const char* buffer, size_t length) {
std::unique_lock<std::mutex> lock(mutex_);
// Wait until the buffer is read up (or parsing is completed)
while (!stream_.Empty() && !completed_)
finish_.wait(lock);
// Stop further parsing if the parsing process is completed.
if (completed_)
return;
// Set the buffer to stream and unblock the AsyncStringStream
stream_.src_ = buffer;
stream_.end_ = buffer + length;
notEmpty_.notify_one();
}
private:
void Parse() {
d_.ParseStream<parseFlags>(stream_);
// The stream may not be fully read, notify finish anyway to unblock ParsePart()
std::unique_lock<std::mutex> lock(mutex_);
completed_ = true; // Parsing process is completed
finish_.notify_one(); // Unblock ParsePart() or destructor if they are waiting.
}
struct AsyncStringStream {
typedef char Ch;
AsyncStringStream(AsyncDocumentParser& parser) : parser_(parser), src_(), end_(), count_() {}
char Peek() const {
std::unique_lock<std::mutex> lock(parser_.mutex_);
// If nothing in stream, block to wait.
while (Empty())
parser_.notEmpty_.wait(lock);
return *src_;
}
char Take() {
std::unique_lock<std::mutex> lock(parser_.mutex_);
// If nothing in stream, block to wait.
while (Empty())
parser_.notEmpty_.wait(lock);
count_++;
char c = *src_++;
// If all stream is read up, notify that the stream is finish.
if (Empty())
parser_.finish_.notify_one();
return c;
}
size_t Tell() const { return count_; }
// Not implemented
char* PutBegin() { return 0; }
void Put(char) {}
void Flush() {}
size_t PutEnd(char*) { return 0; }
bool Empty() const { return src_ == end_; }
AsyncDocumentParser& parser_;
const char* src_; //!< Current read position.
const char* end_; //!< End of buffer
size_t count_; //!< Number of characters taken so far.
};
AsyncStringStream stream_;
Document& d_;
std::thread parseThread_;
std::mutex mutex_;
std::condition_variable notEmpty_;
std::condition_variable finish_;
bool completed_;
};
int main() {
Document d;
{
AsyncDocumentParser<> parser(d);
const char json1[] = " { \"hello\" : \"world\", \"t\" : tr";
//const char json1[] = " { \"hello\" : \"world\", \"t\" : trX"; // Fot test parsing error
const char json2[] = "ue, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.14";
const char json3[] = "16, \"a\":[1, 2, 3, 4] } ";
parser.ParsePart(json1, sizeof(json1) - 1);
parser.ParsePart(json2, sizeof(json2) - 1);
parser.ParsePart(json3, sizeof(json3) - 1);
}
if (d.HasParseError()) {
std::cout << "Error at offset " << d.GetErrorOffset() << ": " << GetParseError_En(d.GetParseError()) << std::endl;
return EXIT_FAILURE;
}
// Stringify the JSON to cout
OStreamWrapper os(std::cout);
Writer<OStreamWrapper> writer(os);
d.Accept(writer);
std::cout << std::endl;
return EXIT_SUCCESS;
}
#else // Not supporting C++11
#include <iostream>
int main() {
std::cout << "This example requires C++11 compiler" << std::endl;
}
#endif

View File

@ -0,0 +1,30 @@
// JSON pretty formatting example
// This example can only handle UTF-8. For handling other encodings, see prettyauto example.
#include "rapidjson/reader.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/error/en.h"
using namespace rapidjson;
int main(int, char*[]) {
// Prepare reader and input stream.
Reader reader;
char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare writer and output stream.
char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
PrettyWriter<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output.
if (!reader.Parse<kParseValidateEncodingFlag>(is, writer)) {
fprintf(stderr, "\nError(%u): %s\n", static_cast<unsigned>(reader.GetErrorOffset()), GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
return 0;
}

View File

@ -0,0 +1,56 @@
// JSON pretty formatting example
// This example can handle UTF-8/UTF-16LE/UTF-16BE/UTF-32LE/UTF-32BE.
// The input firstly convert to UTF8, and then write to the original encoding with pretty formatting.
#include "rapidjson/reader.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/encodedstream.h" // NEW
#include "rapidjson/error/en.h"
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#endif
using namespace rapidjson;
int main(int, char*[]) {
#ifdef _WIN32
// Prevent Windows converting between CR+LF and LF
_setmode(_fileno(stdin), _O_BINARY); // NEW
_setmode(_fileno(stdout), _O_BINARY); // NEW
#endif
// Prepare reader and input stream.
//Reader reader;
GenericReader<AutoUTF<unsigned>, UTF8<> > reader; // CHANGED
char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
AutoUTFInputStream<unsigned, FileReadStream> eis(is); // NEW
// Prepare writer and output stream.
char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
#if 1
// Use the same Encoding of the input. Also use BOM according to input.
typedef AutoUTFOutputStream<unsigned, FileWriteStream> OutputStream; // NEW
OutputStream eos(os, eis.GetType(), eis.HasBOM()); // NEW
PrettyWriter<OutputStream, UTF8<>, AutoUTF<unsigned> > writer(eos); // CHANGED
#else
// You may also use static bound encoding type, such as output to UTF-16LE with BOM
typedef EncodedOutputStream<UTF16LE<>,FileWriteStream> OutputStream; // NEW
OutputStream eos(os, true); // NEW
PrettyWriter<OutputStream, UTF8<>, UTF16LE<> > writer(eos); // CHANGED
#endif
// JSON reader parse from the input stream and let writer generate the output.
//if (!reader.Parse<kParseValidateEncodingFlag>(is, writer)) {
if (!reader.Parse<kParseValidateEncodingFlag>(eis, writer)) { // CHANGED
fprintf(stderr, "\nError(%u): %s\n", static_cast<unsigned>(reader.GetErrorOffset()), GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
return 0;
}

View File

@ -0,0 +1,72 @@
// Schema Validator example
// The example validates JSON text from stdin with a JSON schema specified in the argument.
#include "rapidjson/error/en.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/schema.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: schemavalidator schema.json < input.json\n");
return EXIT_FAILURE;
}
// Read a JSON schema from file into Document
Document d;
char buffer[4096];
{
FILE *fp = fopen(argv[1], "r");
if (!fp) {
printf("Schema file '%s' not found\n", argv[1]);
return -1;
}
FileReadStream fs(fp, buffer, sizeof(buffer));
d.ParseStream(fs);
if (d.HasParseError()) {
fprintf(stderr, "Schema file '%s' is not a valid JSON\n", argv[1]);
fprintf(stderr, "Error(offset %u): %s\n",
static_cast<unsigned>(d.GetErrorOffset()),
GetParseError_En(d.GetParseError()));
fclose(fp);
return EXIT_FAILURE;
}
fclose(fp);
}
// Then convert the Document into SchemaDocument
SchemaDocument sd(d);
// Use reader to parse the JSON in stdin, and forward SAX events to validator
SchemaValidator validator(sd);
Reader reader;
FileReadStream is(stdin, buffer, sizeof(buffer));
if (!reader.Parse(is, validator) && reader.GetParseErrorCode() != kParseErrorTermination) {
// Schema validator error would cause kParseErrorTermination, which will handle it in next step.
fprintf(stderr, "Input is not a valid JSON\n");
fprintf(stderr, "Error(offset %u): %s\n",
static_cast<unsigned>(reader.GetErrorOffset()),
GetParseError_En(reader.GetParseErrorCode()));
}
// Check the validation result
if (validator.IsValid()) {
printf("Input JSON is valid.\n");
return EXIT_SUCCESS;
}
else {
printf("Input JSON is invalid.\n");
StringBuffer sb;
validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
fprintf(stderr, "Invalid schema: %s\n", sb.GetString());
fprintf(stderr, "Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());
sb.Clear();
validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
fprintf(stderr, "Invalid document: %s\n", sb.GetString());
return EXIT_FAILURE;
}
}

View File

@ -0,0 +1,173 @@
// Serialize example
// This example shows writing JSON string with writer directly.
#include "rapidjson/prettywriter.h" // for stringify JSON
#include <cstdio>
#include <string>
#include <vector>
using namespace rapidjson;
class Person {
public:
Person(const std::string& name, unsigned age) : name_(name), age_(age) {}
Person(const Person& rhs) : name_(rhs.name_), age_(rhs.age_) {}
virtual ~Person();
Person& operator=(const Person& rhs) {
name_ = rhs.name_;
age_ = rhs.age_;
return *this;
}
protected:
template <typename Writer>
void Serialize(Writer& writer) const {
// This base class just write out name-value pairs, without wrapping within an object.
writer.String("name");
#if RAPIDJSON_HAS_STDSTRING
writer.String(name_);
#else
writer.String(name_.c_str(), static_cast<SizeType>(name_.length())); // Supplying length of string is faster.
#endif
writer.String("age");
writer.Uint(age_);
}
private:
std::string name_;
unsigned age_;
};
Person::~Person() {
}
class Education {
public:
Education(const std::string& school, double GPA) : school_(school), GPA_(GPA) {}
Education(const Education& rhs) : school_(rhs.school_), GPA_(rhs.GPA_) {}
template <typename Writer>
void Serialize(Writer& writer) const {
writer.StartObject();
writer.String("school");
#if RAPIDJSON_HAS_STDSTRING
writer.String(school_);
#else
writer.String(school_.c_str(), static_cast<SizeType>(school_.length()));
#endif
writer.String("GPA");
writer.Double(GPA_);
writer.EndObject();
}
private:
std::string school_;
double GPA_;
};
class Dependent : public Person {
public:
Dependent(const std::string& name, unsigned age, Education* education = 0) : Person(name, age), education_(education) {}
Dependent(const Dependent& rhs) : Person(rhs), education_(0) { education_ = (rhs.education_ == 0) ? 0 : new Education(*rhs.education_); }
virtual ~Dependent();
Dependent& operator=(const Dependent& rhs) {
if (this == &rhs)
return *this;
delete education_;
education_ = (rhs.education_ == 0) ? 0 : new Education(*rhs.education_);
return *this;
}
template <typename Writer>
void Serialize(Writer& writer) const {
writer.StartObject();
Person::Serialize(writer);
writer.String("education");
if (education_)
education_->Serialize(writer);
else
writer.Null();
writer.EndObject();
}
private:
Education *education_;
};
Dependent::~Dependent() {
delete education_;
}
class Employee : public Person {
public:
Employee(const std::string& name, unsigned age, bool married) : Person(name, age), dependents_(), married_(married) {}
Employee(const Employee& rhs) : Person(rhs), dependents_(rhs.dependents_), married_(rhs.married_) {}
virtual ~Employee();
Employee& operator=(const Employee& rhs) {
static_cast<Person&>(*this) = rhs;
dependents_ = rhs.dependents_;
married_ = rhs.married_;
return *this;
}
void AddDependent(const Dependent& dependent) {
dependents_.push_back(dependent);
}
template <typename Writer>
void Serialize(Writer& writer) const {
writer.StartObject();
Person::Serialize(writer);
writer.String("married");
writer.Bool(married_);
writer.String(("dependents"));
writer.StartArray();
for (std::vector<Dependent>::const_iterator dependentItr = dependents_.begin(); dependentItr != dependents_.end(); ++dependentItr)
dependentItr->Serialize(writer);
writer.EndArray();
writer.EndObject();
}
private:
std::vector<Dependent> dependents_;
bool married_;
};
Employee::~Employee() {
}
int main(int, char*[]) {
std::vector<Employee> employees;
employees.push_back(Employee("Milo YIP", 34, true));
employees.back().AddDependent(Dependent("Lua YIP", 3, new Education("Happy Kindergarten", 3.5)));
employees.back().AddDependent(Dependent("Mio YIP", 1));
employees.push_back(Employee("Percy TSE", 30, false));
StringBuffer sb;
PrettyWriter<StringBuffer> writer(sb);
writer.StartArray();
for (std::vector<Employee>::const_iterator employeeItr = employees.begin(); employeeItr != employees.end(); ++employeeItr)
employeeItr->Serialize(writer);
writer.EndArray();
puts(sb.GetString());
return 0;
}

View File

@ -0,0 +1,29 @@
// JSON simple example
// This example does not handle errors.
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
int main() {
// 1. Parse a JSON string into DOM.
const char* json = "{\"project\":\"rapidjson\",\"stars\":10}";
Document d;
d.Parse(json);
// 2. Modify it by DOM.
Value& s = d["stars"];
s.SetInt(s.GetInt() + 1);
// 3. Stringify the DOM
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
// Output {"project":"rapidjson","stars":11}
std::cout << buffer.GetString() << std::endl;
return 0;
}

View File

@ -0,0 +1,42 @@
#include "rapidjson/reader.h"
#include <iostream>
using namespace rapidjson;
using namespace std;
struct MyHandler {
bool Null() { cout << "Null()" << endl; return true; }
bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; }
bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; }
bool Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; return true; }
bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; }
bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; }
bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; }
bool RawNumber(const char* str, SizeType length, bool copy) {
cout << "Number(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool String(const char* str, SizeType length, bool copy) {
cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool StartObject() { cout << "StartObject()" << endl; return true; }
bool Key(const char* str, SizeType length, bool copy) {
cout << "Key(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
bool StartArray() { cout << "StartArray()" << endl; return true; }
bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
};
int main() {
const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
MyHandler handler;
Reader reader;
StringStream ss(json);
reader.Parse(ss, handler);
return 0;
}

View File

@ -0,0 +1,36 @@
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
using namespace std;
int main() {
StringBuffer s;
Writer<StringBuffer> writer(s);
writer.StartObject(); // Between StartObject()/EndObject(),
writer.Key("hello"); // output a key,
writer.String("world"); // follow by a value.
writer.Key("t");
writer.Bool(true);
writer.Key("f");
writer.Bool(false);
writer.Key("n");
writer.Null();
writer.Key("i");
writer.Uint(123);
writer.Key("pi");
writer.Double(3.1416);
writer.Key("a");
writer.StartArray(); // Between StartArray()/EndArray(),
for (unsigned i = 0; i < 4; i++)
writer.Uint(i); // all values are elements of the array.
writer.EndArray();
writer.EndObject();
// {"hello":"world","t":true,"f":false,"n":null,"i":123,"pi":3.1416,"a":[0,1,2,3]}
cout << s.GetString() << endl;
return 0;
}

View File

@ -0,0 +1,151 @@
// Hello World example
// This example shows basic usage of DOM-style API.
#include "rapidjson/document.h" // rapidjson's DOM-style API
#include "rapidjson/prettywriter.h" // for stringify JSON
#include <cstdio>
using namespace rapidjson;
using namespace std;
int main(int, char*[]) {
////////////////////////////////////////////////////////////////////////////
// 1. Parse a JSON text string to a document.
const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
printf("Original JSON:\n %s\n", json);
Document document; // Default template parameter uses UTF8 and MemoryPoolAllocator.
#if 0
// "normal" parsing, decode strings to new buffers. Can use other input stream via ParseStream().
if (document.Parse(json).HasParseError())
return 1;
#else
// In-situ parsing, decode strings directly in the source string. Source must be string.
char buffer[sizeof(json)];
memcpy(buffer, json, sizeof(json));
if (document.ParseInsitu(buffer).HasParseError())
return 1;
#endif
printf("\nParsing to document succeeded.\n");
////////////////////////////////////////////////////////////////////////////
// 2. Access values in document.
printf("\nAccess values in document:\n");
assert(document.IsObject()); // Document is a JSON value represents the root of DOM. Root can be either an object or array.
assert(document.HasMember("hello"));
assert(document["hello"].IsString());
printf("hello = %s\n", document["hello"].GetString());
// Since version 0.2, you can use single lookup to check the existing of member and its value:
Value::MemberIterator hello = document.FindMember("hello");
assert(hello != document.MemberEnd());
assert(hello->value.IsString());
assert(strcmp("world", hello->value.GetString()) == 0);
(void)hello;
assert(document["t"].IsBool()); // JSON true/false are bool. Can also uses more specific function IsTrue().
printf("t = %s\n", document["t"].GetBool() ? "true" : "false");
assert(document["f"].IsBool());
printf("f = %s\n", document["f"].GetBool() ? "true" : "false");
printf("n = %s\n", document["n"].IsNull() ? "null" : "?");
assert(document["i"].IsNumber()); // Number is a JSON type, but C++ needs more specific type.
assert(document["i"].IsInt()); // In this case, IsUint()/IsInt64()/IsUInt64() also return true.
printf("i = %d\n", document["i"].GetInt()); // Alternative (int)document["i"]
assert(document["pi"].IsNumber());
assert(document["pi"].IsDouble());
printf("pi = %g\n", document["pi"].GetDouble());
{
const Value& a = document["a"]; // Using a reference for consecutive access is handy and faster.
assert(a.IsArray());
for (SizeType i = 0; i < a.Size(); i++) // rapidjson uses SizeType instead of size_t.
printf("a[%d] = %d\n", i, a[i].GetInt());
int y = a[0].GetInt();
(void)y;
// Iterating array with iterators
printf("a = ");
for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
printf("%d ", itr->GetInt());
printf("\n");
}
// Iterating object members
static const char* kTypeNames[] = { "Null", "False", "True", "Object", "Array", "String", "Number" };
for (Value::ConstMemberIterator itr = document.MemberBegin(); itr != document.MemberEnd(); ++itr)
printf("Type of member %s is %s\n", itr->name.GetString(), kTypeNames[itr->value.GetType()]);
////////////////////////////////////////////////////////////////////////////
// 3. Modify values in document.
// Change i to a bigger number
{
uint64_t f20 = 1; // compute factorial of 20
for (uint64_t j = 1; j <= 20; j++)
f20 *= j;
document["i"] = f20; // Alternate form: document["i"].SetUint64(f20)
assert(!document["i"].IsInt()); // No longer can be cast as int or uint.
}
// Adding values to array.
{
Value& a = document["a"]; // This time we uses non-const reference.
Document::AllocatorType& allocator = document.GetAllocator();
for (int i = 5; i <= 10; i++)
a.PushBack(i, allocator); // May look a bit strange, allocator is needed for potentially realloc. We normally uses the document's.
// Fluent API
a.PushBack("Lua", allocator).PushBack("Mio", allocator);
}
// Making string values.
// This version of SetString() just store the pointer to the string.
// So it is for literal and string that exists within value's life-cycle.
{
document["hello"] = "rapidjson"; // This will invoke strlen()
// Faster version:
// document["hello"].SetString("rapidjson", 9);
}
// This version of SetString() needs an allocator, which means it will allocate a new buffer and copy the the string into the buffer.
Value author;
{
char buffer2[10];
int len = sprintf(buffer2, "%s %s", "Milo", "Yip"); // synthetic example of dynamically created string.
author.SetString(buffer2, static_cast<SizeType>(len), document.GetAllocator());
// Shorter but slower version:
// document["hello"].SetString(buffer, document.GetAllocator());
// Constructor version:
// Value author(buffer, len, document.GetAllocator());
// Value author(buffer, document.GetAllocator());
memset(buffer2, 0, sizeof(buffer2)); // For demonstration purpose.
}
// Variable 'buffer' is unusable now but 'author' has already made a copy.
document.AddMember("author", author, document.GetAllocator());
assert(author.IsNull()); // Move semantic for assignment. After this variable is assigned as a member, the variable becomes null.
////////////////////////////////////////////////////////////////////////////
// 4. Stringify JSON
printf("\nModified JSON with reformatting:\n");
StringBuffer sb;
PrettyWriter<StringBuffer> writer(sb);
document.Accept(writer); // Accept() traverses the DOM and generates Handler events.
puts(sb.GetString());
return 0;
}

View File

@ -0,0 +1,271 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ALLOCATORS_H_
#define RAPIDJSON_ALLOCATORS_H_
#include "rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Allocator
/*! \class rapidjson::Allocator
\brief Concept for allocating, resizing and freeing memory block.
Note that Malloc() and Realloc() are non-static but Free() is static.
So if an allocator need to support Free(), it needs to put its pointer in
the header of memory block.
\code
concept Allocator {
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
// Allocate a memory block.
// \param size of the memory block in bytes.
// \returns pointer to the memory block.
void* Malloc(size_t size);
// Resize a memory block.
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
// \param newSize the new size in bytes.
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
// Free a memory block.
// \param pointer to the memory block. Null pointer is permitted.
static void Free(void *ptr);
};
\endcode
*/
///////////////////////////////////////////////////////////////////////////////
// CrtAllocator
//! C-runtime library allocator.
/*! This class is just wrapper for standard C library memory routines.
\note implements Allocator concept
*/
class CrtAllocator {
public:
static const bool kNeedFree = true;
void* Malloc(size_t size) {
if (size) // behavior of malloc(0) is implementation defined.
return std::malloc(size);
else
return NULL; // standardize to returning NULL.
}
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
(void)originalSize;
if (newSize == 0) {
std::free(originalPtr);
return NULL;
}
return std::realloc(originalPtr, newSize);
}
static void Free(void *ptr) { std::free(ptr); }
};
///////////////////////////////////////////////////////////////////////////////
// MemoryPoolAllocator
//! Default memory allocator used by the parser and DOM.
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
It does not free memory blocks. And Realloc() only allocate new memory.
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
User may also supply a buffer as the first chunk.
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
The user-buffer is not deallocated by this allocator.
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
\note implements Allocator concept
*/
template <typename BaseAllocator = CrtAllocator>
class MemoryPoolAllocator {
public:
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
//! Constructor with chunkSize.
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks.
*/
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{
}
//! Constructor with user-supplied buffer.
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
The user buffer will not be deallocated when this allocator is destructed.
\param buffer User supplied buffer.
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks.
*/
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{
RAPIDJSON_ASSERT(buffer != 0);
RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
chunkHead_->capacity = size - sizeof(ChunkHeader);
chunkHead_->size = 0;
chunkHead_->next = 0;
}
//! Destructor.
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
*/
~MemoryPoolAllocator() {
Clear();
RAPIDJSON_DELETE(ownBaseAllocator_);
}
//! Deallocates all memory chunks, excluding the user-supplied buffer.
void Clear() {
while (chunkHead_ && chunkHead_ != userBuffer_) {
ChunkHeader* next = chunkHead_->next;
baseAllocator_->Free(chunkHead_);
chunkHead_ = next;
}
if (chunkHead_ && chunkHead_ == userBuffer_)
chunkHead_->size = 0; // Clear user buffer
}
//! Computes the total capacity of allocated memory chunks.
/*! \return total capacity in bytes.
*/
size_t Capacity() const {
size_t capacity = 0;
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
capacity += c->capacity;
return capacity;
}
//! Computes the memory blocks allocated.
/*! \return total used bytes.
*/
size_t Size() const {
size_t size = 0;
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
size += c->size;
return size;
}
//! Allocates a memory block. (concept Allocator)
void* Malloc(size_t size) {
if (!size)
return NULL;
size = RAPIDJSON_ALIGN(size);
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
return NULL;
void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
chunkHead_->size += size;
return buffer;
}
//! Resizes a memory block (concept Allocator)
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
if (originalPtr == 0)
return Malloc(newSize);
if (newSize == 0)
return NULL;
originalSize = RAPIDJSON_ALIGN(originalSize);
newSize = RAPIDJSON_ALIGN(newSize);
// Do not shrink if new size is smaller than original
if (originalSize >= newSize)
return originalPtr;
// Simply expand it if it is the last allocation and there is sufficient space
if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
size_t increment = static_cast<size_t>(newSize - originalSize);
if (chunkHead_->size + increment <= chunkHead_->capacity) {
chunkHead_->size += increment;
return originalPtr;
}
}
// Realloc process: allocate and copy memory, do not free original buffer.
if (void* newBuffer = Malloc(newSize)) {
if (originalSize)
std::memcpy(newBuffer, originalPtr, originalSize);
return newBuffer;
}
else
return NULL;
}
//! Frees a memory block (concept Allocator)
static void Free(void *ptr) { (void)ptr; } // Do nothing
private:
//! Copy constructor is not permitted.
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
//! Copy assignment operator is not permitted.
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
//! Creates a new chunk.
/*! \param capacity Capacity of the chunk in bytes.
\return true if success.
*/
bool AddChunk(size_t capacity) {
if (!baseAllocator_)
ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator());
if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) {
chunk->capacity = capacity;
chunk->size = 0;
chunk->next = chunkHead_;
chunkHead_ = chunk;
return true;
}
else
return false;
}
static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
//! Chunk header for perpending to each chunk.
/*! Chunks are stored as a singly linked list.
*/
struct ChunkHeader {
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
size_t size; //!< Current size of allocated memory in bytes.
ChunkHeader *next; //!< Next chunk in the linked list.
};
ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
void *userBuffer_; //!< User supplied buffer.
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
};
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ENCODINGS_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,299 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
#define RAPIDJSON_ENCODEDSTREAM_H_
#include "stream.h"
#include "memorystream.h"
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Input byte stream wrapper with a statically bound encoding.
/*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
\tparam InputByteStream Type of input byte stream. For example, FileReadStream.
*/
template <typename Encoding, typename InputByteStream>
class EncodedInputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
public:
typedef typename Encoding::Ch Ch;
EncodedInputStream(InputByteStream& is) : is_(is) {
current_ = Encoding::TakeBOM(is_);
}
Ch Peek() const { return current_; }
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
size_t Tell() const { return is_.Tell(); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
EncodedInputStream(const EncodedInputStream&);
EncodedInputStream& operator=(const EncodedInputStream&);
InputByteStream& is_;
Ch current_;
};
//! Specialized for UTF8 MemoryStream.
template <>
class EncodedInputStream<UTF8<>, MemoryStream> {
public:
typedef UTF8<>::Ch Ch;
EncodedInputStream(MemoryStream& is) : is_(is) {
if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
}
Ch Peek() const { return is_.Peek(); }
Ch Take() { return is_.Take(); }
size_t Tell() const { return is_.Tell(); }
// Not implemented
void Put(Ch) {}
void Flush() {}
Ch* PutBegin() { return 0; }
size_t PutEnd(Ch*) { return 0; }
MemoryStream& is_;
private:
EncodedInputStream(const EncodedInputStream&);
EncodedInputStream& operator=(const EncodedInputStream&);
};
//! Output byte stream wrapper with statically bound encoding.
/*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
\tparam OutputByteStream Type of input byte stream. For example, FileWriteStream.
*/
template <typename Encoding, typename OutputByteStream>
class EncodedOutputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
public:
typedef typename Encoding::Ch Ch;
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
if (putBOM)
Encoding::PutBOM(os_);
}
void Put(Ch c) { Encoding::Put(os_, c); }
void Flush() { os_.Flush(); }
// Not implemented
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
EncodedOutputStream(const EncodedOutputStream&);
EncodedOutputStream& operator=(const EncodedOutputStream&);
OutputByteStream& os_;
};
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
/*!
\tparam CharType Type of character for reading.
\tparam InputByteStream type of input byte stream to be wrapped.
*/
template <typename CharType, typename InputByteStream>
class AutoUTFInputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
public:
typedef CharType Ch;
//! Constructor.
/*!
\param is input stream to be wrapped.
\param type UTF encoding type if it is not detected from the stream.
*/
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
DetectType();
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
takeFunc_ = f[type_];
current_ = takeFunc_(*is_);
}
UTFType GetType() const { return type_; }
bool HasBOM() const { return hasBOM_; }
Ch Peek() const { return current_; }
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
size_t Tell() const { return is_->Tell(); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
AutoUTFInputStream(const AutoUTFInputStream&);
AutoUTFInputStream& operator=(const AutoUTFInputStream&);
// Detect encoding type with BOM or RFC 4627
void DetectType() {
// BOM (Byte Order Mark):
// 00 00 FE FF UTF-32BE
// FF FE 00 00 UTF-32LE
// FE FF UTF-16BE
// FF FE UTF-16LE
// EF BB BF UTF-8
const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4());
if (!c)
return;
unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
hasBOM_ = false;
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
// RFC 4627: Section 3
// "Since the first two characters of a JSON text will always be ASCII
// characters [RFC0020], it is possible to determine whether an octet
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
// at the pattern of nulls in the first four octets."
// 00 00 00 xx UTF-32BE
// 00 xx 00 xx UTF-16BE
// xx 00 00 00 UTF-32LE
// xx 00 xx 00 UTF-16LE
// xx xx xx xx UTF-8
if (!hasBOM_) {
unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
switch (pattern) {
case 0x08: type_ = kUTF32BE; break;
case 0x0A: type_ = kUTF16BE; break;
case 0x01: type_ = kUTF32LE; break;
case 0x05: type_ = kUTF16LE; break;
case 0x0F: type_ = kUTF8; break;
default: break; // Use type defined by user.
}
}
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
}
typedef Ch (*TakeFunc)(InputByteStream& is);
InputByteStream* is_;
UTFType type_;
Ch current_;
TakeFunc takeFunc_;
bool hasBOM_;
};
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
/*!
\tparam CharType Type of character for writing.
\tparam OutputByteStream type of output byte stream to be wrapped.
*/
template <typename CharType, typename OutputByteStream>
class AutoUTFOutputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
public:
typedef CharType Ch;
//! Constructor.
/*!
\param os output stream to be wrapped.
\param type UTF encoding type.
\param putBOM Whether to write BOM at the beginning of the stream.
*/
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
putFunc_ = f[type_];
if (putBOM)
PutBOM();
}
UTFType GetType() const { return type_; }
void Put(Ch c) { putFunc_(*os_, c); }
void Flush() { os_->Flush(); }
// Not implemented
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
AutoUTFOutputStream(const AutoUTFOutputStream&);
AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
void PutBOM() {
typedef void (*PutBOMFunc)(OutputByteStream&);
static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
f[type_](*os_);
}
typedef void (*PutFunc)(OutputByteStream&, Ch);
OutputByteStream* os_;
UTFType type_;
PutFunc putFunc_;
};
#undef RAPIDJSON_ENCODINGS_FUNC
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_FILESTREAM_H_

Some files were not shown because too many files have changed in this diff Show More