Merge remote-tracking branch 'upstream/master' into TransitionTable

This commit is contained in:
thebusytypist 2014-07-11 12:52:07 +08:00
commit 36434b66c8
38 changed files with 3555 additions and 1436 deletions

1
.gitignore vendored
View File

@ -5,5 +5,6 @@
/build/*.exe
/build/gmake
/build/vs*/
/doc/html
/thirdparty/lib
/intermediate

View File

@ -5,8 +5,11 @@ compiler:
- gcc
env:
matrix:
- config=debug64 config_suffix=debug_x64_gmake
- config=release64 config_suffix=release_x64_gmake
global:
secure: "CR3yKliFhwQLX+Zs1PCRcGej6jr4DIZsCqs9x6J2NN+U9Aow0gd/uiPBho/utgm+/TmSBji5n8FO/J3ORo34q4gC6EebTEaN4gCHNXVlIBJFw9x+Gs/lML5i8F2AoweFJY334OVaOf9qC8ZVJ8Z1nEwxj77fq3gcSLzRU3pIaS8="
before_install:
- sudo add-apt-repository -y ppa:codegear/release
@ -16,14 +19,14 @@ before_install:
install: true
before_script:
- cd build
- premake4 'gmake'
- cd "${TRAVIS_BUILD_DIR}"
- pushd build && premake4 'gmake' && popd
script:
- make -C build/gmake -f test.make
- make -C build/gmake -f example.make
- cd bin
- pushd bin
- ./unittest_${config_suffix}
- valgrind --leak-check=full --error-exitcode=1 ./unittest_${config_suffix}
- ./perftest_${config_suffix}
- if [ "$config" = "release64" ]; then ./perftest_${config_suffix}; fi
- popd
- ./build/travis-doxygen.sh;

File diff suppressed because it is too large Load Diff

View File

@ -64,7 +64,7 @@ solution "test"
defines { "_CRT_SECURE_NO_WARNINGS" }
configuration "gmake"
buildoptions "-msse4.2 -Werror -Wall -Wextra -Wswitch-default"
buildoptions "-msse4.2 -Werror -Wall -Wextra"
project "gtest"
kind "StaticLib"
@ -87,7 +87,7 @@ solution "test"
kind "ConsoleApp"
if _ACTION == "gmake" then
buildoptions "-Weffc++"
buildoptions "-Weffc++ -Wswitch-default"
end
files {

100
build/travis-doxygen.sh Executable file
View File

@ -0,0 +1,100 @@
#!/bin/bash
# Update Doxygen documentation after push to 'master'.
# Author: @pah
set -e
SUDO=sudo
DOXYGEN_VER=doxygen-1.8.7
DOXYGEN_TAR=${DOXYGEN_VER}.linux.bin.tar.gz
DOXYGEN_URL="http://ftp.stack.nl/pub/users/dimitri/${DOXYGEN_TAR}"
DOXYGEN_BIN="/usr/local/bin/doxygen"
GHPAGES_REPO="miloyip/rapidjson"
GHPAGES_URL="https://github.com/${GHPAGES_REPO}"
skip() {
echo "$@" 1>&2
echo "Exiting..." 1>&2
exit 0
}
abort() {
echo "Error: $@" 1>&2
echo "Exiting..." 1>&2
exit 1
}
# TRAVIS_BUILD_DIR not set, exiting
[ -d "${TRAVIS_BUILD_DIR-/nonexistent}" ] || \
abort '${TRAVIS_BUILD_DIR} not set or nonexistent.'
# check for pull-requests
[ "${TRAVIS_PULL_REQUEST}" = "false" ] || \
skip "Not running Doxygen for pull-requests."
# check for branch name
[ "${TRAVIS_BRANCH}" = "master" ] || \
skip "Running Doxygen only for updates on 'master' branch (current: ${TRAVIS_BRANCH})."
# check for job number
[ "${TRAVIS_JOB_NUMBER}" = "${TRAVIS_BUILD_NUMBER}.1" ] || \
skip "Running Doxygen only on first job of build ${TRAVIS_BUILD_NUMBER} (current: ${TRAVIS_JOB_NUMBER})."
# install doxygen binary distribution
doxygen_install()
{
wget -O - "${DOXYGEN_URL}" | \
tar xz -C ${TMPDIR-/tmp} ${DOXYGEN_VER}/bin/doxygen
$SUDO install -m 755 ${TMPDIR-/tmp}/${DOXYGEN_VER}/bin/doxygen \
${DOXYGEN_BIN};
}
doxygen_run()
{
cd "${TRAVIS_BUILD_DIR}";
doxygen build/Doxyfile;
}
gh_pages_prepare()
{
cd "${TRAVIS_BUILD_DIR}/doc";
[ ! -d "html" ] || \
abort "Doxygen target directory already exists."
git clone --single-branch -b gh-pages ${GHPAGES_URL} html
cd html
# setup git config (with defaults)
git config user.name "${GIT_NAME-travis}"
git config user.email "${GIT_EMAIL-"travis@localhost"}"
# clean working dir
rm -f .git/index
git clean -df
}
gh_pages_commit() {
cd "${TRAVIS_BUILD_DIR}/doc/html";
git add --all;
git diff-index --quiet HEAD || git commit -m "Automatic doxygen build";
}
gh_pages_push() {
# check for secure variables
[ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] || \
skip "Secure variables not available, not updating GitHub pages."
[ "${GH_TOKEN+set}" = set ] || \
skip "GitHub access token not available, not updating GitHub pages."
cd "${TRAVIS_BUILD_DIR}/doc/html";
# setup credentials (hide in "set -x" mode)
git config core.askpass /bin/true
( set +x ; git config credential.${GHPAGES_URL}.username "${GH_TOKEN}" )
# push to GitHub
git push origin gh-pages
}
doxygen_install
gh_pages_prepare
doxygen_run
gh_pages_commit
gh_pages_push

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: 24 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: 22 KiB

View File

@ -1,17 +1,269 @@
# RapidJSON DOM
# DOM
## Template
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.
### Encoding
[TOC]
### Allocator
# Template {#Template}
## Parsing
In the tutorial, `Value` and `Document` was used. Similarly to `std::string`, these are actually `typedef` of template classes:
### Parse Error
~~~~~~~~~~cpp
namespace rapidjson {
### In situ Parsing
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
class GenericValue {
// ...
};
## Techniques
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
class GenericDocument : public GenericValue<Encoding, Allocator> {
// ...
};
### DOM as SAX Generator
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](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 to generate 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, typename SourceEncoding>
GenericDocument& GenericDocument::ParseInsitu(Ch* str);
// (5) In situ parsing, using same Encoding for stream
template <unsigned parseFlags>
GenericDocument& GenericDocument::ParseInsitu(Ch* str);
// (6) In situ parsing, using default parse flags
GenericDocument& GenericDocument::ParseInsitu(Ch* str);
// (7) Normal parsing of a string
template <unsigned parseFlags, typename SourceEncoding>
GenericDocument& GenericDocument::Parse(const Ch* str);
// (8) Normal parsing of a string, using same Encoding for stream
template <unsigned parseFlags>
GenericDocument& GenericDocument::Parse(const Ch* str);
// (9) Normal parsing of a string, using default parse flags
GenericDocument& GenericDocument::Parse(const Ch* str);
~~~~~~~~~~
The examples of [tutorial](tutorial.md) uses (9) for normal parsing of string. The examples of [stream](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
------------------------------|-----------------------------------
`kParseDefaultFlags = 0` | Default parse flags.
`kParseInsituFlag` | In-situ(destructive) parsing.
`kParseValidateEncodingFlag` | Validate encoding of JSON strings.
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 GetParseOffet()`.
Parse Error Code | Description
--------------------------------------------|---------------------------------------------------
`kParseErrorNone` | No error.
`kParseErrorDocumentEmpty` | The document is empty.
`kParseErrorDocumentRootNotObjectOrArray` | The document root must be either object or array.
`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
// TODO: example
~~~~~~~~~~
## 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 decoded string is always shorter than 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 constant-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#EncodedInputStream).
When writing a JSON from DOM to output stream, transcoding can also be used. An example is in [EncodedOutputStream](stream.md##EncodedOutputStream).
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 customer handlers for transforming the DOM into other formats. For example, a handler which converts the DOM into XML.
~~~~~~~~~~cpp
// TODO: example
~~~~~~~~~~
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.
~~~~~~~~~~cpp
char buffer[1024];
MemoryPoolAllocator allocator(buffer, sizeof(buffer));
Document d(&allocator);
d.Parse(json);
~~~~~~~~~~
If the total size of allocation is less than 1024 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

@ -1,4 +1,4 @@
# RapidJSON Encoding
# Encoding
## Unicode

View File

@ -1,4 +1,4 @@
# RapidJSON FAQ
# FAQ
## General

View File

@ -1,4 +1,4 @@
# RapidJSON Features
# Features
## General
@ -39,10 +39,10 @@
## 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 (`GenericReader`). It also provides a generator API (`GenericWriter`) which consumes the same set of events.
* 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 (`GenericDocument`), for easy manipulation, and finally stringify back to JSON if needed.
* The DOM style API (`GenericDocument`) is actually implemented with SAX style API (`GenericReader`). SAX is faster but sometimes DOM is easier. Users can pick their choices according to scenarios.
* 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.
## DOM (Document)
@ -59,13 +59,13 @@
## SAX (Writer)
* Support PrettyWriter for adding newlines and indentations.
* Support `rapidjson::PrettyWriter` for adding newlines and indentations.
* Support custom precision for floating point values.
## Stream
* Support `GenericStringBuffer` for storing the output JSON as string.
* Support `FileReadStream`/`FileWriteStream` for input/output `FILE` object.
* 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

View File

@ -1,4 +1,4 @@
# RapidJSON Internals
# Internals
This section records some design and implementation details.

194
doc/misc/DoxygenLayout.xml Normal file
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>

260
doc/misc/doxygenextra.css Normal file
View File

@ -0,0 +1,260 @@
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;
}
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;
}
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;
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=);
}

9
doc/misc/footer.html Normal file
View File

@ -0,0 +1,9 @@
<!-- 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-->

24
doc/misc/header.html Normal file
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 -->

View File

@ -1,4 +1,4 @@
# RapidJSON Performance
# Performance
The old performance article for RapidJSON 0.1 is provided [here](https://code.google.com/p/rapidjson/wiki/Performance).

View File

@ -1,4 +1,4 @@
# RapidJSON SAX
# SAX
## Reader

View File

@ -1,16 +1,18 @@
# RapidJSON Stream
# Stream
In 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 streams.
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 streams.
## Memory Streams
[TOC]
# Memory Streams {#MemoryStreams}
Memory streams store JSON in memory.
### StringStream (Input)
## 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
~~~~~~~~~~cpp
#include "rapidjson/document.h" // will include "rapidjson/rapidjson.h"
using namespace rapidjson;
@ -21,24 +23,24 @@ 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
~~~~~~~~~~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 (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
~~~~~~~~~~cpp
#include "rapidjson/stringbuffer.h"
StringBuffer buffer;
@ -46,33 +48,35 @@ 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
~~~~~~~~~~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
# 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 (Input) {#FileReadStream}
`FileReadStream` reads the file via a `FILE` pointer. And user need to provide a buffer.
```cpp
~~~~~~~~~~cpp
#include "rapidjson/filereadstream.h"
#include <cstdio>
using namespace rapidjson;
FILE* fp = fopen("big.json", "rb"); // non-Windows use "r"
char readBuffer[65536];
@ -82,20 +86,22 @@ 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 (Output) {#FileWriteStream}
`FileWriteStream` is buffered output stream. Its usage is very similar to `FileReadStream`.
```cpp
~~~~~~~~~~cpp
#include "rapidjson/filewritestream.h"
#include <cstdio>
using namespace rapidjson;
Document d;
d.Parse(json);
// ...
@ -109,11 +115,11 @@ Writer<FileWriteStream> writer(os);
d.Accept(writer);
fclose(fp);
```
~~~~~~~~~~
It can also directs the output to `stdout`.
## Encoded Streams
# Encoded Streams {#EncodedStreams}
Encoded streams do not contain JSON itself, but they wrap byte streams to provide basic encoding/decoding function.
@ -125,11 +131,11 @@ If the encoding of stream is known in compile-time, you may use `EncodedInputStr
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}
`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
~~~~~~~~~~cpp
#include "rapidjson/document.h"
#include "rapidjson/filereadstream.h" // FileReadStream
#include "rapidjson/encodedstream.h" // EncodedInputStream
@ -148,13 +154,13 @@ 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}
`EncodedOutputStream` is similar but it has a `bool putBOM` parameter in the constructor, controlling whether to write BOM into output byte stream.
```cpp
~~~~~~~~~~cpp
#include "rapidjson/filewritestream.h" // FileWriteStream
#include "rapidjson/encodedstream.h" // EncodedOutputStream
#include <cstdio>
@ -174,15 +180,15 @@ Writer<OutputStream, UTF32LE<>, UTF8<>> writer(eos);
d.Accept(writer); // This generates UTF32-LE file from UTF-8 in memory
fclose(fp);
```
~~~~~~~~~~
### AutoUTFInputStream
## 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
~~~~~~~~~~cpp
#include "rapidjson/document.h"
#include "rapidjson/filereadstream.h" // FileReadStream
#include "rapidjson/encodedstream.h" // AutoUTFInputStream
@ -201,17 +207,19 @@ 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 {#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
~~~~~~~~~~cpp
using namespace rapidjson;
void WriteJSONFile(FILE* fp, UTFType type, bool putBOM, const Document& d) {
char writeBuffer[256];
FileWriteStream bos(fp, writeBuffer, sizeof(writeBuffer));
@ -222,17 +230,17 @@ void WriteJSONFile(FILE* fp, UTFType type, bool putBOM, const Document& d) {
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
# 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
~~~~~~~~~~cpp
concept Stream {
typename Ch; //!< Character type of the stream.
@ -261,17 +269,17 @@ concept Stream {
//! \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
## Example: istream wrapper {#ExampleIStreamWrapper}
The following example is a wrapper of `std::istream`, which only implements 3 functions.
```cpp
~~~~~~~~~~cpp
class IStreamWrapper {
public:
typedef char Ch;
@ -302,26 +310,26 @@ private:
std::istream& is_;
};
```
~~~~~~~~~~
User can use it to wrap instances of `std::stringstream`, `std::ifstream`.
```cpp
~~~~~~~~~~cpp
const char* json = "[1,2,3,4]";
std::stringstream ss(json);
IStreamWrapper is(ss);
Document d;
d.Parse(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
## Example: ostream wrapper {#ExampleOStreamWrapper}
The following example is a wrapper of `std::istream`, which only implements 2 functions.
```cpp
~~~~~~~~~~cpp
class OStreamWrapper {
public:
typedef char Ch;
@ -344,11 +352,11 @@ private:
std::ostream& os_;
};
```
~~~~~~~~~~
User can use it to wrap instances of `std::stringstream`, `std::ofstream`.
```cpp
~~~~~~~~~~cpp
Document d;
// ...
@ -357,10 +365,10 @@ OSStreamWrapper os(ss);
Writer<OStreamWrapper> 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 {#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

@ -1,19 +1,21 @@
# RapidJSON Tutorial
# Tutorial
This tutorial introduces the basics of the Document Object Model(DOM) API.
As shown in [Usage at a glance](../readme.md#usage-at-a-glance), a JSON text can be parsed into DOM, and then the DOM can be queried and modified easily, and finally be converted back to JSON text.
As shown in [Usage at a glance](readme.md), a JSON can be parsed into DOM, and then the DOM can be queried and modified easily, and finally be converted back to JSON.
## Value & Document
[TOC]
Each JSON value is stored in a type called `Value`. A `Document`, representing the DOM, contains the root of `Value`.
# Value & Document {#ValueDocument}
### Query Value
Each JSON value is stored in a type called `Value`. A `Document`, representing the DOM, contains the root of `Value`. All public types and functions of RapidJSON are defined in the `rapidjson` namespace.
In this section, we will use excerpt of [`example/tutorial/tutorial.cpp`](../example/tutorial/tutorial.cpp).
# Query Value {#QueryValue}
Assumes we have a JSON text stored in a C string (`const char* json`):
```js
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 ,
@ -23,10 +25,10 @@ Assumes we have a JSON text stored in a C string (`const char* json`):
"pi": 3.1416,
"a": [1, 2, 3, 4]
}
```
~~~~~~~~~~
Parse it into a `Document`
```cpp
~~~~~~~~~~cpp
#include "rapidjson/document.h"
using namespace rapidjson;
@ -34,50 +36,50 @@ using namespace rapidjson;
// ...
Document document;
document.Parse(json);
```
~~~~~~~~~~
The JSON text is now parsed into `document` as a *DOM tree*:
The JSON is now parsed into `document` as a *DOM tree*:
![tutorial](diagram/tutorial.png?raw=true)
![DOM in the tutorial](diagram/tutorial.png)
The root of a conforming JSON should be either an object or an array. In this case, the root is an object.
```cpp
~~~~~~~~~~cpp
assert(document.IsObject());
```
~~~~~~~~~~
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
~~~~~~~~~~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
~~~~~~~~~~cpp
assert(document["t"].IsBool());
printf("t = %s\n", document["t"].GetBool() ? "true" : "false");
```
~~~~~~~~~~
```
~~~~~~~~~~
true
```
~~~~~~~~~~
JSON null can be queryed by `IsNull()`.
```cpp
~~~~~~~~~~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
~~~~~~~~~~cpp
assert(document["i"].IsNumber());
// In this case, IsUint()/IsInt64()/IsUInt64() also return true.
@ -88,34 +90,34 @@ printf("i = %d\n", document["i"].GetInt());
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
~~~~~~~~~~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
## Query Array {#QueryArray}
By default, `SizeType` is typedef of `unsigned`. In most systems, array is limited to store up to 2^32-1 elements.
@ -124,20 +126,20 @@ You may access the elements in array by integer literal, for example, `a[1]`, `a
* `a[0u]`
Array is similar to `std::vector`, instead of using indices, you may also use iterator to access all the elements.
```cpp
~~~~~~~~~~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`
### Query Object
## Query Object {#QueryObject}
Similar to array, we can iterate object members by iterator:
```cpp
~~~~~~~~~~cpp
static const char* kTypeNames[] =
{ "Null", "False", "True", "Object", "Array", "String", "Number" };
@ -147,9 +149,9 @@ for (Value::ConstMemberIterator itr = document.MemberBegin();
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
@ -157,19 +159,19 @@ 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
~~~~~~~~~~cpp
Value::ConstMemberIterator itr = document.FindMember("hello");
if (itr != document.MemberEnd())
printf("%s %s\n", itr->value.GetString());
```
~~~~~~~~~~
### Querying Number
## 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.
@ -200,7 +202,7 @@ Note that, an integer value may be obtained in various ways without conversion.
When obtaining the numeric values, `GetDouble()` will convert internal integer representation to a `double`. Note that, `int` and `uint` 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
## Query String {#QueryString}
In addition to `GetString()`, the `Value` class also contains `GetStringLength()`. Here explains why.
@ -210,65 +212,65 @@ To conform RFC 4627, RapidJSON supports string containing `U+0000`. If you need
For example, after parsing a the following JSON string to `Document d`.
```js
~~~~~~~~~~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
~~~~~~~~~~cpp
string( const char* s, size_type count);
```
~~~~~~~~~~
which accepts the length of string as parameter. This constructor supports storing null character within the string, and should also provide better performance.
## Create/Modify Values
# 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`.
### Changing Value Type
## 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
~~~~~~~~~~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
~~~~~~~~~~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
~~~~~~~~~~cpp
Value o(kObjectType);
Value a(kArrayType);
```
~~~~~~~~~~
### Move Semantics
## 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
~~~~~~~~~~cpp
Value a(123);
Value b(456);
b = a; // a becomes a Null value, b becomes number 123.
```
~~~~~~~~~~
![move1](diagram/move1.png?raw=true)
![Assignment with move semantics.](diagram/move1.png)
Why? What is the advantage of this semantics?
@ -276,7 +278,7 @@ The simple answer is performance. For fixed size JSON types (Number, True, False
For example, if normal *copy* semantics was used:
```cpp
~~~~~~~~~~cpp
Value o(kObjectType);
{
Value contacts(kArrayType);
@ -285,9 +287,9 @@ Value o(kObjectType);
o.AddMember("contacts", contacts); // deep clone contacts (may be with lots of allocations)
// destruct contacts.
}
```
~~~~~~~~~~
![move2](diagram/move2.png?raw=true)
![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.
@ -297,7 +299,7 @@ To make RapidJSON simple and fast, we chose to use *move* semantics for assignme
So, with move semantics, the above example becomes:
```cpp
~~~~~~~~~~cpp
Value o(kObjectType);
{
Value contacts(kArrayType);
@ -305,13 +307,24 @@ Value o(kObjectType);
o.AddMember("contacts", contacts); // just memcpy() of contacts itself to the value of new member (16 bytes)
// contacts became Null here. Its destruction is trivial.
}
```
~~~~~~~~~~
![move3](diagram/move3.png?raw=true)
![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()`.
### Create String
### 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);
// a.PushBack(Value(42)); // will not compile
a.PushBack(Value().SetInt(42)); // fluent API
a.PushBack(Value(42).Move()); // 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.
@ -323,7 +336,7 @@ To make memory allocation customizable, RapidJSON requires user to pass an insta
Therefore, when we assign a copy-string, we call this overloaded `SetString()` with allocator:
```cpp
~~~~~~~~~~cpp
Document document;
Value author;
char buffer[10];
@ -331,22 +344,35 @@ 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 literal string or string with safe life-cycle can use const-string version of `SetString()`, which lacks allocator parameter:
Finally, for literal string 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
~~~~~~~~~~cpp
Value s;
s.SetString("rapidjson", 9); // faster, can contain null character
s.SetString("rapidjson"); // slower, assumes null-terminated
s.SetString("rapidjson"); // can contain null character, length derived at compile time
s = "rapidjson"; // shortcut, same as above
```
~~~~~~~~~~
### Modify Array
For plain string pointers, the RapidJSON requires to mark a string 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()`
@ -355,11 +381,11 @@ Value with array type provides similar APIs as `std::vector`.
* `template <typename T> GenericValue& PushBack(T, Allocator&)`
* `Value& PopBack()`
Note that, `Reserve(...)` and `PushBack(...)` may allocate memory, therefore requires an allocator.
Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore require an allocator.
Here is an example of `PushBack()`:
```cpp
~~~~~~~~~~cpp
Value a(kArrayType);
Document::AllocatorType& allocator = document.GetAllocator();
@ -368,30 +394,58 @@ for (int i = 5; i <= 10; i++)
// Fluent interface
a.PushBack("Lua", allocator).PushBack("Mio", allocator);
```
~~~~~~~~~~
Differs from STL, `PushBack()`/`PopBack()` returns the array reference itself. This is called fluent interface.
Differs from STL, `PushBack()`/`PopBack()` returns the array reference itself. This is called _fluent interface_.
### Modify Object
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. Each key must be a string value. The way to manipulating object is to add/remove members:
* `Value& AddMember(Value&, Value&, Allocator& allocator)`
* `Value& AddMember(const Ch*, Value&, Allocator&)`
* `template <typename T> Value& AddMember(const Ch*, T value, Allocator&)`
* `Value& AddMember(StringRefType, Value&, Allocator&)`
* `template <typename T> Value& AddMember(StringRefType, T value, Allocator&)`
* `bool RemoveMember(const Ch*)`
Here is an example.
```cpp
Value contact(kObejct);
~~~~~~~~~~cpp
Value contact(kObject);
contact.AddMember("name", "Milo", document.GetAllocator());
contact.AddMember("married", true, document.GetAllocator());
```
~~~~~~~~~~
### Deep Copy Value
The `StringRefType` used as name parameter assumes the same interface as the `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 name string
Value val(42); // some value
contact.AddMember(key, val, document.GetAllocator());
~~~~~~~~~~
## 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
~~~~~~~~~~cpp
Document d;
Document::AllocatorType& a = d.GetAllocator();
Value v1("foo");
@ -406,31 +460,31 @@ 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
## Swap Values {#SwapValues}
`Swap()` is also provided.
```cpp
~~~~~~~~~~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 tress.
## What's next
# What's next {#WhatsNext}
This tutorial shows the basics of DOM tree query and manipulation. There are several important concepts in RapidJSON:
1. [Streams](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](encoding.md) defines which character set is used in streams and memory. RapidJSON also provide Unicode conversion/validation internally.
3. [DOM](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](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](performance.md) shows some in-house and third-party benchmarks.
6. [Internals](internals.md) describes some internal designs and techniques of 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 set 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, API documentation, examples and unit tests.
You may also refer to the [FAQ](faq.md), API documentation, examples and unit tests.

View File

@ -6,13 +6,11 @@
#include <new> // placement new
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4127) // conditional expression is constant
#endif
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
#elif defined(__GNUC__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
@ -73,7 +71,7 @@ class GenericMemberIterator
public:
//! Iterator type itself
typedef GenericMemberIterator Type;
typedef GenericMemberIterator Iterator;
//! Constant iterator type
typedef GenericMemberIterator<true,Encoding,Allocator> ConstType;
//! Non-constant iterator type
@ -112,29 +110,29 @@ public:
//! @name stepping
//@{
Type& operator++(){ ++ptr_; return *this; }
Type& operator--(){ --ptr_; return *this; }
Type operator++(int){ Type old(*this); ++ptr_; return old; }
Type operator--(int){ Type old(*this); --ptr_; return old; }
Iterator& operator++(){ ++ptr_; return *this; }
Iterator& operator--(){ --ptr_; return *this; }
Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; }
Iterator operator--(int){ Iterator old(*this); --ptr_; return old; }
//@}
//! @name increment/decrement
//@{
Type operator+(DifferenceType n) const { return Type(ptr_+n); }
Type operator-(DifferenceType n) const { return Type(ptr_-n); }
Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); }
Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); }
Type& operator+=(DifferenceType n) { ptr_+=n; return *this; }
Type& operator-=(DifferenceType n) { ptr_-=n; return *this; }
Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; }
Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; }
//@}
//! @name relations
//@{
bool operator==(Type that) const { return ptr_ == that.ptr_; }
bool operator!=(Type that) const { return ptr_ != that.ptr_; }
bool operator<=(Type that) const { return ptr_ <= that.ptr_; }
bool operator>=(Type that) const { return ptr_ >= that.ptr_; }
bool operator< (Type that) const { return ptr_ < that.ptr_; }
bool operator> (Type that) const { return ptr_ > that.ptr_; }
bool operator==(Iterator that) const { return ptr_ == that.ptr_; }
bool operator!=(Iterator that) const { return ptr_ != that.ptr_; }
bool operator<=(Iterator that) const { return ptr_ <= that.ptr_; }
bool operator>=(Iterator that) const { return ptr_ >= that.ptr_; }
bool operator< (Iterator that) const { return ptr_ < that.ptr_; }
bool operator> (Iterator that) const { return ptr_ > that.ptr_; }
//@}
//! @name dereference
@ -145,7 +143,7 @@ public:
//@}
//! Distance
DifferenceType operator-(Type that) const { return ptr_-that.ptr_; }
DifferenceType operator-(Iterator that) const { return ptr_-that.ptr_; }
private:
//! Internal constructor from plain pointer
@ -165,17 +163,161 @@ struct GenericMemberIterator;
template <typename Encoding, typename Allocator>
struct GenericMemberIterator<false,Encoding,Allocator> {
//! use plain pointer as iterator type
typedef GenericMember<Encoding,Allocator>* Type;
typedef GenericMember<Encoding,Allocator>* Iterator;
};
//! const GenericMemberIterator
template <typename Encoding, typename Allocator>
struct GenericMemberIterator<true,Encoding,Allocator> {
//! use plain const pointer as iterator type
typedef const GenericMember<Encoding,Allocator>* Type;
typedef const GenericMember<Encoding,Allocator>* Iterator;
};
#endif // RAPIDJSON_NOMEMBERITERATORCLASS
///////////////////////////////////////////////////////////////////////////////
// GenericStringRef
//! Reference to a constant string (not taking a copy)
/*!
\tparam CharType character type of the string
This helper class is used to automatically infer constant string
references for string literals, especially from \c const \b (!)
character arrays.
The main use is for creating JSON string values without copying the
source string via an \ref Allocator. This requires that the referenced
string pointers have a sufficient lifetime, which exceeds the lifetime
of the associated GenericValue.
\b Example
\code
Value v("foo"); // ok, no need to copy & calculate length
const char foo[] = "foo";
v.SetString(foo); // ok
const char* bar = foo;
// Value x(bar); // not ok, can't rely on bar's lifetime
Value x(StringRef(bar)); // lifetime explicitly guaranteed by user
Value y(StringRef(bar, 3)); // ok, explicitly pass length
\endcode
\see StringRef, GenericValue::SetString
*/
template<typename CharType>
struct GenericStringRef {
typedef CharType Ch; //!< character type of the string
//! Create string reference from \c const character array
/*!
This constructor implicitly creates a constant string reference from
a \c const character array. It has better performance than
\ref StringRef(const CharType*) by inferring the string \ref length
from the array length, and also supports strings containing null
characters.
\tparam N length of the string, automatically inferred
\param str Constant character array, lifetime assumed to be longer
than the use of the string in e.g. a GenericValue
\post \ref s == str
\note Constant complexity.
\note There is a hidden, private overload to disallow references to
non-const character arrays to be created via this constructor.
By this, e.g. function-scope arrays used to be filled via
\c snprintf are excluded from consideration.
In such cases, the referenced string should be \b copied to the
GenericValue instead.
*/
template<SizeType N>
GenericStringRef(const CharType (&str)[N])
: s(str), length(N-1) {}
//! Explicitly create string reference from \c const character pointer
/*!
This constructor can be used to \b explicitly create a reference to
a constant string pointer.
\see StringRef(const CharType*)
\param str Constant character pointer, lifetime assumed to be longer
than the use of the string in e.g. a GenericValue
\post \ref s == str
\note There is a hidden, private overload to disallow references to
non-const character arrays to be created via this constructor.
By this, e.g. function-scope arrays used to be filled via
\c snprintf are excluded from consideration.
In such cases, the referenced string should be \b copied to the
GenericValue instead.
*/
explicit GenericStringRef(const CharType* str)
: s(str), length(internal::StrLen(str)){}
//! Create constant string reference from pointer and length
/*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
\param len length of the string, excluding the trailing NULL terminator
\post \ref s == str && \ref length == len
\note Constant complexity.
*/
GenericStringRef(const CharType* str, SizeType len)
: s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); }
//! implicit conversion to plain CharType pointer
operator const Ch *() const { return s; }
const Ch* const s; //!< plain CharType pointer
const SizeType length; //!< length of the string (excluding the trailing NULL terminator)
private:
//! Disallow copy-assignment
GenericStringRef operator=(const GenericStringRef&);
//! Disallow construction from non-const array
template<SizeType N>
GenericStringRef(CharType (&str)[N]) /* = delete */;
};
//! Mark a character pointer as constant string
/*! Mark a plain character pointer as a "string literal". This function
can be used to avoid copying a character string to be referenced as a
value in a JSON GenericValue object, if the string's lifetime is known
to be valid long enough.
\tparam CharType Character type of the string
\param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
\return GenericStringRef string reference object
\relatesalso GenericStringRef
\see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember
*/
template<typename CharType>
inline GenericStringRef<CharType> StringRef(const CharType* str) {
return GenericStringRef<CharType>(str, internal::StrLen(str));
}
//! Mark a character pointer as constant string
/*! Mark a plain character pointer as a "string literal". This function
can be used to avoid copying a character string to be referenced as a
value in a JSON GenericValue object, if the string's lifetime is known
to be valid long enough.
This version has better performance with supplied length, and also
supports string containing null characters.
\tparam CharType character type of the string
\param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
\param length The length of source string.
\return GenericStringRef string reference object
\relatesalso GenericStringRef
*/
template<typename CharType>
inline GenericStringRef<CharType> StringRef(const CharType* str, size_t length) {
return GenericStringRef<CharType>(str, SizeType(length));
}
///////////////////////////////////////////////////////////////////////////////
// GenericValue
@ -198,8 +340,9 @@ public:
typedef Encoding EncodingType; //!< Encoding type from template parameter.
typedef Allocator AllocatorType; //!< Allocator type from template parameter.
typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding.
typedef typename GenericMemberIterator<false,Encoding,Allocator>::Type MemberIterator; //!< Member iterator for iterating in object.
typedef typename GenericMemberIterator<true,Encoding,Allocator>::Type ConstMemberIterator; //!< Constant member iterator for iterating in object.
typedef GenericStringRef<Ch> StringRefType; //!< Reference to a constant string
typedef typename GenericMemberIterator<false,Encoding,Allocator>::Iterator MemberIterator; //!< Member iterator for iterating in object.
typedef typename GenericMemberIterator<true,Encoding,Allocator>::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object.
typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array.
typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array.
@ -209,8 +352,8 @@ public:
//! Default constructor creates a null value.
GenericValue() : data_(), flags_(kNullFlag) {}
//! Copy constructor is not permitted.
private:
//! Copy constructor is not permitted.
GenericValue(const GenericValue& rhs);
public:
@ -233,14 +376,25 @@ public:
/*! Creates a copy of a Value by using the given Allocator
\tparam SourceAllocator allocator of \c rhs
\param rhs Value to copy from (read-only)
\param allocator Allocator to use for copying
\param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator().
\see CopyFrom()
*/
template< typename SourceAllocator >
GenericValue(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator & allocator);
//! Constructor for boolean value.
explicit GenericValue(bool b) : data_(), flags_(b ? kTrueFlag : kFalseFlag) {}
/*! \param b Boolean value
\note This constructor is limited to \em real boolean values and rejects
implicitly converted types like arbitrary pointers. Use an explicit cast
to \c bool, if you want to construct a boolean JSON value in such cases.
*/
#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen
template <typename T>
explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<T,bool>)))
#else
explicit GenericValue(bool b)
#endif
: data_(), flags_(b ? kTrueFlag : kFalseFlag) {}
//! Constructor for int value.
explicit GenericValue(int i) : data_(), flags_(kNumberIntFlag) {
@ -285,16 +439,16 @@ public:
explicit GenericValue(double d) : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; }
//! Constructor for constant string (i.e. do not make a copy of string)
GenericValue(const Ch* s, SizeType length) : data_(), flags_() { SetStringRaw(s, length); }
GenericValue(const Ch* s, SizeType length) : data_(), flags_() { SetStringRaw(StringRef(s, length)); }
//! Constructor for constant string (i.e. do not make a copy of string)
explicit GenericValue(const Ch* s) : data_(), flags_() { SetStringRaw(s, internal::StrLen(s)); }
explicit GenericValue(StringRefType s) : data_(), flags_() { SetStringRaw(s); }
//! Constructor for copy-string (i.e. do make a copy of string)
GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(s, length, allocator); }
GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); }
//! Constructor for copy-string (i.e. do make a copy of string)
GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(s, internal::StrLen(s), allocator); }
GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); }
//! Destructor.
/*! Need to destruct elements of array, members of object, or copy-string.
@ -341,12 +495,30 @@ public:
return *this;
}
//! Assignment of constant string reference (no copy)
/*! \param str Constant string reference to be assigned
\note This overload is needed to avoid clashes with the generic primitive type assignment overload below.
\see GenericStringRef, operator=(T)
*/
GenericValue& operator=(StringRefType str) {
return (*this).operator=<StringRefType>(str);
}
//! Assignment with primitive types.
/*! \tparam T Either Type, int, unsigned, int64_t, uint64_t, const Ch*
/*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
\param value The value to be assigned.
\note The source type \c T explicitly disallows all pointer types,
especially (\c const) \ref Ch*. This helps avoiding implicitly
referencing character strings with insufficient lifetime, use
\ref SetString(const Ch*, Allocator&) (for copying) or
\ref StringRef() (to explicitly mark the pointer as constant) instead.
All other pointer types would implicitly convert to \c bool,
use \ref SetBool() instead.
*/
template <typename T>
GenericValue& operator=(T value) {
RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer<T>,GenericValue&)
operator=(T value) {
this->~GenericValue();
new (this) GenericValue(value);
return *this;
@ -379,6 +551,9 @@ public:
return *this;
}
//! Prepare Value for move semantics
/*! \return *this */
GenericValue& Move() { return *this; }
//@}
//!@name Type
@ -412,6 +587,8 @@ public:
//@{
bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; }
//!< Set boolean value
/*! \post IsBool() == true */
GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; }
//@}
@ -420,6 +597,7 @@ public:
//@{
//! Set this value as an empty object.
/*! \post IsObject() == true */
GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; }
//! Get the value associated with the name.
@ -430,7 +608,7 @@ public:
A better approach is to use the now public FindMember().
*/
GenericValue& operator[](const Ch* name) {
GenericValue n(name, internal::StrLen(name));
GenericValue n(StringRef(name));
return (*this)[n];
}
const GenericValue& operator[](const Ch* name) const { return const_cast<GenericValue&>(*this)[name]; }
@ -483,7 +661,7 @@ public:
\c std::map, this has been changed to MemberEnd() now.
*/
MemberIterator FindMember(const Ch* name) {
GenericValue n(name, internal::StrLen(name));
GenericValue n(StringRef(name));
return FindMember(n);
}
@ -506,9 +684,11 @@ public:
//! Add a member (name-value pair) to the object.
/*! \param name A string value as name of member.
\param value Value of any type.
\param allocator Allocator for reallocating memory.
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
\return The value itself for fluent API.
\note The ownership of name and value will be transfered to this object if success.
\note The ownership of \c name and \c value will be transferred to this object on success.
\pre IsObject() && name.IsString()
\post name.IsNull() && value.IsNull()
*/
GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
RAPIDJSON_ASSERT(IsObject());
@ -532,19 +712,53 @@ public:
return *this;
}
GenericValue& AddMember(const Ch* name, Allocator& nameAllocator, GenericValue& value, Allocator& allocator) {
GenericValue n(name, internal::StrLen(name), nameAllocator);
//! Add a member (name-value pair) to the object.
/*! \param name A constant string reference as name of member.
\param value Value of any type.
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
\return The value itself for fluent API.
\note The ownership of \c value will be transferred to this object on success.
\pre IsObject()
\post value.IsNull()
*/
GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) {
GenericValue n(name);
return AddMember(n, value, allocator);
}
GenericValue& AddMember(const Ch* name, GenericValue& value, Allocator& allocator) {
GenericValue n(name, internal::StrLen(name));
return AddMember(n, value, allocator);
//! Add a constant string value as member (name-value pair) to the object.
/*! \param name A constant string reference as name of member.
\param value constant string reference as value of member.
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
\return The value itself for fluent API.
\pre IsObject()
\note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below.
*/
GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) {
GenericValue v(value);
return AddMember(name, v, allocator);
}
//! Add any primitive value as member (name-value pair) to the object.
/*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
\param name A constant string reference as name of member.
\param value Value of primitive type \c T as value of member
\param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator().
\return The value itself for fluent API.
\pre IsObject()
\note The source type \c T explicitly disallows all pointer types,
especially (\c const) \ref Ch*. This helps avoiding implicitly
referencing character strings with insufficient lifetime, use
\ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref
AddMember(StringRefType, StringRefType, Allocator&).
All other pointer types would implicitly convert to \c bool,
use an explicit cast instead, if needed.
*/
template <typename T>
GenericValue& AddMember(const Ch* name, T value, Allocator& allocator) {
GenericValue n(name, internal::StrLen(name));
RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer<T>,GenericValue&)
AddMember(StringRefType name, T value, Allocator& allocator) {
GenericValue n(name);
GenericValue v(value);
return AddMember(n, v, allocator);
}
@ -555,7 +769,7 @@ public:
\note Removing member is implemented by moving the last member. So the ordering of members is changed.
*/
bool RemoveMember(const Ch* name) {
GenericValue n(name, internal::StrLen(name));
GenericValue n(StringRef(name));
return RemoveMember(n);
}
@ -601,6 +815,7 @@ public:
//@{
//! Set this value as an empty array.
/*! \post IsArray == true */
GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; }
//! Get the number of elements in array.
@ -647,7 +862,7 @@ int z = a[0u].GetInt(); // This works too.
//! Request the array to have enough capacity to store elements.
/*! \param newCapacity The capacity that the array at least need to have.
\param allocator The allocator for allocating memory. It must be the same one use previously.
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
\return The value itself for fluent API.
*/
GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) {
@ -659,11 +874,13 @@ int z = a[0u].GetInt(); // This works too.
return *this;
}
//! Append a value at the end of the array.
/*! \param value The value to be appended.
\param allocator The allocator for allocating memory. It must be the same one use previously.
//! Append a GenericValue at the end of the array.
/*! \param value Value to be appended.
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
\pre IsArray() == true
\post value.IsNull() == true
\return The value itself for fluent API.
\note The ownership of the value will be transfered to this object if success.
\note The ownership of \c value will be transferred to this array on success.
\note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
*/
GenericValue& PushBack(GenericValue& value, Allocator& allocator) {
@ -674,8 +891,37 @@ int z = a[0u].GetInt(); // This works too.
return *this;
}
//! Append a constant string reference at the end of the array.
/*! \param value Constant string reference to be appended.
\param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator().
\pre IsArray() == true
\return The value itself for fluent API.
\note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
\see GenericStringRef
*/
GenericValue& PushBack(StringRefType value, Allocator& allocator) {
return (*this).template PushBack<StringRefType>(value, allocator);
}
//! Append a primitive value at the end of the array(.)
/*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
\param value Value of primitive type T to be appended.
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
\pre IsArray() == true
\return The value itself for fluent API.
\note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
\note The source type \c T explicitly disallows all pointer types,
especially (\c const) \ref Ch*. This helps avoiding implicitly
referencing character strings with insufficient lifetime, use
\ref PushBack(GenericValue&, Allocator&) or \ref
PushBack(StringRefType, Allocator&).
All other pointer types would implicitly convert to \c bool,
use an explicit cast instead, if needed.
*/
template <typename T>
GenericValue& PushBack(T value, Allocator& allocator) {
RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer<T>,GenericValue&)
PushBack(T value, Allocator& allocator) {
GenericValue v(value);
return PushBack(v, allocator);
}
@ -729,30 +975,35 @@ int z = a[0u].GetInt(); // This works too.
\param s source string pointer.
\param length The length of source string, excluding the trailing null terminator.
\return The value itself for fluent API.
\post IsString() == true && GetString() == s && GetStringLength() == length
\see SetString(StringRefType)
*/
GenericValue& SetString(const Ch* s, SizeType length) { this->~GenericValue(); SetStringRaw(s, length); return *this; }
GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); }
//! Set this value as a string without copying source string.
/*! \param s source string pointer.
/*! \param s source string reference
\return The value itself for fluent API.
\post IsString() == true && GetString() == s && GetStringLength() == s.length
*/
GenericValue& SetString(const Ch* s) { return SetString(s, internal::StrLen(s)); }
GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; }
//! Set this value as a string by copying from source string.
/*! This version has better performance with supplied length, and also support string containing null character.
\param s source string.
\param length The length of source string, excluding the trailing null terminator.
\param allocator Allocator for allocating copied buffer. Commonly use document.GetAllocator().
\param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
\return The value itself for fluent API.
\post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length
*/
GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, length, allocator); return *this; }
GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; }
//! Set this value as a string by copying from source string.
/*! \param s source string.
\param allocator Allocator for allocating copied buffer. Commonly use document.GetAllocator().
\param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
\return The value itself for fluent API.
\post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length
*/
GenericValue& SetString(const Ch* s, Allocator& allocator) { SetString(s, internal::StrLen(s), allocator); return *this; }
GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); }
//@}
@ -908,21 +1159,19 @@ private:
}
//! Initialize this value as constant string, without calling destructor.
void SetStringRaw(const Ch* s, SizeType length) {
RAPIDJSON_ASSERT(s != NULL);
void SetStringRaw(StringRefType s) {
flags_ = kConstStringFlag;
data_.s.str = s;
data_.s.length = length;
data_.s.length = s.length;
}
//! Initialize this value as copy string with initial data, without calling destructor.
void SetStringRaw(const Ch* s, SizeType length, Allocator& allocator) {
RAPIDJSON_ASSERT(s != NULL);
void SetStringRaw(StringRefType s, Allocator& allocator) {
flags_ = kCopyStringFlag;
data_.s.str = (Ch *)allocator.Malloc((length + 1) * sizeof(Ch));
data_.s.length = length;
memcpy(const_cast<Ch*>(data_.s.str), s, length * sizeof(Ch));
const_cast<Ch*>(data_.s.str)[length] = '\0';
data_.s.str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch));
data_.s.length = s.length;
memcpy(const_cast<Ch*>(data_.s.str), s, s.length * sizeof(Ch));
const_cast<Ch*>(data_.s.str)[s.length] = '\0';
}
//! Assignment without calling destructor
@ -937,7 +1186,7 @@ private:
};
#pragma pack (pop)
//! Value with UTF8 encoding.
//! GenericValue with UTF8 encoding
typedef GenericValue<UTF8<> > Value;
///////////////////////////////////////////////////////////////////////////////
@ -948,6 +1197,7 @@ typedef GenericValue<UTF8<> > Value;
\note implements Handler concept
\tparam Encoding encoding for both parsing and string storage.
\tparam Allocator allocator for allocating memory for the DOM, and the stack during parsing.
\warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructors. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue.
*/
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
class GenericDocument : public GenericValue<Encoding, Allocator> {
@ -962,8 +1212,13 @@ public:
*/
GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {}
//! Parse JSON text from an input stream.
/*! \tparam parseFlags Combination of ParseFlag.
//!@name Parse from stream
//!@{
//! Parse JSON text from an input stream (with Encoding conversion)
/*! \tparam parseFlags Combination of \ref ParseFlag.
\tparam SourceEncoding Encoding of input stream
\tparam InputStream Type of input stream, implementing Stream concept
\param is Input stream to be parsed.
\return The document itself for fluent API.
*/
@ -985,18 +1240,34 @@ public:
return *this;
}
//! Parse JSON text from an input stream
/*! \tparam parseFlags Combination of \ref ParseFlag.
\tparam InputStream Type of input stream, implementing Stream concept
\param is Input stream to be parsed.
\return The document itself for fluent API.
*/
template <unsigned parseFlags, typename InputStream>
GenericDocument& ParseStream(InputStream& is) {
return ParseStream<parseFlags,Encoding,InputStream>(is);
}
//! Parse JSON text from an input stream (with \ref kParseDefaultFlags)
/*! \tparam InputStream Type of input stream, implementing Stream concept
\param is Input stream to be parsed.
\return The document itself for fluent API.
*/
template <typename InputStream>
GenericDocument& ParseStream(InputStream& is) {
return ParseStream<kParseDefaultFlags, Encoding, InputStream>(is);
}
//!@}
//! Parse JSON text from a mutable string.
/*! \tparam parseFlags Combination of ParseFlag.
//!@name Parse in-place from mutable string
//!@{
//! Parse JSON text from a mutable string (with Encoding conversion)
/*! \tparam parseFlags Combination of \ref ParseFlag.
\tparam SourceEncoding Transcoding from input Encoding
\param str Mutable zero-terminated string to be parsed.
\return The document itself for fluent API.
*/
@ -1006,17 +1277,31 @@ public:
return ParseStream<parseFlags | kParseInsituFlag, SourceEncoding>(s);
}
//! Parse JSON text from a mutable string
/*! \tparam parseFlags Combination of \ref ParseFlag.
\param str Mutable zero-terminated string to be parsed.
\return The document itself for fluent API.
*/
template <unsigned parseFlags>
GenericDocument& ParseInsitu(Ch* str) {
return ParseInsitu<parseFlags, Encoding>(str);
}
//! Parse JSON text from a mutable string (with \ref kParseDefaultFlags)
/*! \param str Mutable zero-terminated string to be parsed.
\return The document itself for fluent API.
*/
GenericDocument& ParseInsitu(Ch* str) {
return ParseInsitu<kParseDefaultFlags, Encoding>(str);
}
//!@}
//! Parse JSON text from a read-only string.
/*! \tparam parseFlags Combination of ParseFlag (must not contain kParseInsituFlag).
//!@name Parse from read-only string
//!@{
//! Parse JSON text from a read-only string (with Encoding conversion)
/*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag).
\tparam SourceEncoding Transcoding from input Encoding
\param str Read-only zero-terminated string to be parsed.
*/
template <unsigned parseFlags, typename SourceEncoding>
@ -1026,14 +1311,25 @@ public:
return ParseStream<parseFlags, SourceEncoding>(s);
}
//! Parse JSON text from a read-only string
/*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag).
\param str Read-only zero-terminated string to be parsed.
*/
template <unsigned parseFlags>
GenericDocument& Parse(const Ch* str) {
return Parse<parseFlags, Encoding>(str);
}
//! Parse JSON text from a read-only string (with \ref kParseDefaultFlags)
/*! \param str Read-only zero-terminated string to be parsed.
*/
GenericDocument& Parse(const Ch* str) {
return Parse<kParseDefaultFlags>(str);
}
//!@}
//!@name Handling parse errors
//!@{
//! Whether a parse error was occured in the last parsing.
bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; }
@ -1044,6 +1340,8 @@ public:
//! Get the offset in character of the parsing error.
size_t GetErrorOffset() const { return errorOffset_; }
//!@}
//! Get the allocator of this document.
Allocator& GetAllocator() { return stack_.GetAllocator(); }
@ -1086,7 +1384,7 @@ private:
}
private:
// Prohibit assignment
//! Prohibit assignment
GenericDocument& operator=(const GenericDocument&);
void ClearStack() {
@ -1103,6 +1401,7 @@ private:
size_t errorOffset_;
};
//! GenericDocument with UTF8 encoding
typedef GenericDocument<UTF8<> > Document;
// defined here due to the dependency on GenericDocument
@ -1118,12 +1417,8 @@ GenericValue<Encoding,Allocator>::GenericValue(const GenericValue<Encoding,Sourc
} // namespace rapidjson
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#ifdef __GNUC__
#pragma GCC diagnostic pop
#if defined(_MSC_VER) || defined(__GNUC__)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_DOCUMENT_H_

View File

@ -4,8 +4,8 @@
#include "rapidjson.h"
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
namespace rapidjson {
@ -264,7 +264,7 @@ private:
} // namespace rapidjson
#ifdef __GNUC__
#pragma GCC diagnostic pop
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_FILESTREAM_H_

View File

@ -3,9 +3,12 @@
#include "rapidjson.h"
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#ifdef _MSC_VER
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
#elif defined(__GNUC__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
namespace rapidjson {
@ -529,8 +532,8 @@ struct Transcoder<Encoding, Encoding> {
} // namespace rapidjson
#ifdef __GNUC__
#pragma GCC diagnostic pop
#if defined(__GNUC__) || defined(_MSV_VER)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_ENCODINGS_H_

View File

@ -24,11 +24,14 @@ struct SelectIf : SelectIfCond<Condition::Value,T1,T2> {};
template <bool Constify, typename T>
struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
template <typename T, typename U> struct IsSame { enum { Value = false }; };
template <typename T> struct IsSame<T,T> { enum { Value = true }; };
template <typename T, typename U> struct IsSame : FalseType {};
template <typename T> struct IsSame<T,T> : TrueType {};
template <typename T> struct IsConst { enum { Value = false }; };
template <typename T> struct IsConst<const T> { enum { Value = true }; };
template <typename T> struct IsConst : FalseType {};
template <typename T> struct IsConst<const T> : TrueType {};
template <typename T> struct IsPointer : FalseType {};
template <typename T> struct IsPointer<T*> : TrueType {};
template <typename CT, typename T>
struct IsMoreConst {
@ -64,6 +67,9 @@ template <typename T> struct RemoveSfinaeFptr<SfinaeResultTag&(*)(T)> { typedef
typename ::rapidjson::internal::EnableIf \
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
typename ::rapidjson::internal::DisableIf<cond,returntype>::Type
} // namespace internal
} // namespace rapidjson
//@endcond

View File

@ -13,8 +13,7 @@ namespace internal {
template <typename Ch>
inline SizeType StrLen(const Ch* s) {
const Ch* p = s;
while (*p != '\0')
++p;
while (*p) ++p;
return SizeType(p - s);
}

View File

@ -4,8 +4,8 @@
#include "writer.h"
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
namespace rapidjson {
@ -24,9 +24,9 @@ public:
typedef typename Base::Ch Ch;
//! Constructor
/*! \param os Output os.
/*! \param os Output stream.
\param allocator User supplied allocator. If it is null, it will create a private one.
\param levelDepth Initial capacity of
\param levelDepth Initial capacity of stack.
*/
PrettyWriter(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
@ -46,7 +46,9 @@ public:
return *this;
}
//@name Implementation of Handler.
/*! @name Implementation of Handler
\see Handler
*/
//@{
PrettyWriter& Null() { PrettyPrefix(kNullType); Base::WriteNull(); return *this; }
@ -56,11 +58,6 @@ public:
PrettyWriter& Int64(int64_t i64) { PrettyPrefix(kNumberType); Base::WriteInt64(i64); return *this; }
PrettyWriter& Uint64(uint64_t u64) { PrettyPrefix(kNumberType); Base::WriteUint64(u64); return *this; }
PrettyWriter& Double(double d) { PrettyPrefix(kNumberType); Base::WriteDouble(d); return *this; }
//! Overridden for fluent API, see \ref Writer::Double()
PrettyWriter& Double(double d, int precision) {
int oldPrecision = Base::GetDoublePrecision();
return SetDoublePrecision(precision).Double(d).SetDoublePrecision(oldPrecision);
}
PrettyWriter& String(const Ch* str, SizeType length, bool copy = false) {
(void)copy;
@ -117,9 +114,19 @@ public:
//@}
/*! @name Convenience extensions */
//@{
//! Simpler but slower overload.
PrettyWriter& String(const Ch* str) { return String(str, internal::StrLen(str)); }
//! Overridden for fluent API, see \ref Writer::Double()
PrettyWriter& Double(double d, int precision) {
int oldPrecision = Base::GetDoublePrecision();
return SetDoublePrecision(precision).Double(d).SetDoublePrecision(oldPrecision);
}
//@}
protected:
void PrettyPrefix(Type type) {
(void)type;
@ -177,7 +184,7 @@ private:
} // namespace rapidjson
#ifdef __GNUC__
#pragma GCC diagnostic pop
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_RAPIDJSON_H_

View File

@ -4,6 +4,12 @@
// Copyright (c) 2011 Milo Yip (miloyip@gmail.com)
// Version 0.1
/*!\file rapidjson.h
\brief common definitions and configuration
\todo Complete Doxygen documentation for configure macros.
*/
#include <cstdlib> // malloc(), realloc(), free()
#include <cstring> // memcpy()
@ -14,6 +20,7 @@
// (U)INT64_C constant macros.
// If user have their own definition, can define RAPIDJSON_NO_INT64DEFINE to disable this.
#ifndef RAPIDJSON_NO_INT64DEFINE
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
#ifndef __STDC_CONSTANT_MACROS
# define __STDC_CONSTANT_MACROS 1 // required by C++ standard
#endif
@ -25,6 +32,7 @@
#include <stdint.h>
#include <inttypes.h>
#endif
//!@endcond
#endif // RAPIDJSON_NO_INT64TYPEDEF
///////////////////////////////////////////////////////////////////////////////
@ -46,7 +54,7 @@
//! Endianness of the machine.
/*! GCC provided macro for detecting endianness of the target machine. But other
compilers may not have this. User can define RAPIDJSON_ENDIAN to either
RAPIDJSON_LITTLEENDIAN or RAPIDJSON_BIGENDIAN.
\ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN.
*/
#ifndef RAPIDJSON_ENDIAN
#ifdef __BYTE_ORDER__
@ -115,7 +123,9 @@ typedef unsigned SizeType;
// Adopt from boost
#ifndef RAPIDJSON_STATIC_ASSERT
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
namespace rapidjson {
template <bool x> struct STATIC_ASSERTION_FAILURE;
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
template<int x> struct StaticAssertTest {};
@ -130,7 +140,13 @@ template<int x> struct StaticAssertTest {};
#else
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
#endif
//!@endcond
/*! \def RAPIDJSON_STATIC_ASSERT
\brief (internal) macro to check for conditions at compile-time
\param x compile-time condition
\hideinitializer
*/
#define RAPIDJSON_STATIC_ASSERT(x) typedef ::rapidjson::StaticAssertTest<\
sizeof(::rapidjson::STATIC_ASSERTION_FAILURE<bool(x) >)>\
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
@ -139,16 +155,66 @@ template<int x> struct StaticAssertTest {};
///////////////////////////////////////////////////////////////////////////////
// Helpers
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
#define RAPIDJSON_MULTILINEMACRO_BEGIN do {
#define RAPIDJSON_MULTILINEMACRO_END \
} while((void)0, 0)
// adopted from Boost
#define RAPIDJSON_VERSION_CODE(x,y,z) \
(((x)*100000) + ((y)*100) + (z))
// token stringification
#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x)
#define RAPIDJSON_DO_STRINGIFY(x) #x
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
#if defined(__clang__) || (defined(__GNUC__) && RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= RAPIDJSON_VERSION_CODE(4,2,0))
#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x))
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x)
#define RAPIDJSON_DIAG_OFF(x) \
RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x)))
// push/pop support in Clang and GCC>=4.6
#if defined(__clang__) || (defined(__GNUC__) && RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= RAPIDJSON_VERSION_CODE(4,6,0))
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
#else // GCC >= 4.2, < 4.6
#define RAPIDJSON_DIAG_PUSH /* ignored */
#define RAPIDJSON_DIAG_POP /* ignored */
#endif
#elif defined(_MSC_VER)
// pragma (MSVC specific)
#define RAPIDJSON_PRAGMA(x) __pragma(x)
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x))
#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x)
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
#else
#define RAPIDJSON_DIAG_OFF(x) /* ignored */
#define RAPIDJSON_DIAG_PUSH /* ignored */
#define RAPIDJSON_DIAG_POP /* ignored */
#endif // RAPIDJSON_DIAG_*
//!@endcond
///////////////////////////////////////////////////////////////////////////////
// Allocators and Encodings
#include "allocators.h"
#include "encodings.h"
//! main RapidJSON namespace
namespace rapidjson {
///////////////////////////////////////////////////////////////////////////////
@ -246,6 +312,7 @@ struct StreamTraits<GenericStringStream<Encoding> > {
enum { copyOptimization = 1 };
};
//! String stream with UTF8 encoding.
typedef GenericStringStream<UTF8<> > StringStream;
///////////////////////////////////////////////////////////////////////////////
@ -282,6 +349,7 @@ struct StreamTraits<GenericInsituStringStream<Encoding> > {
enum { copyOptimization = 1 };
};
//! Insitu string stream with UTF8 encoding.
typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
///////////////////////////////////////////////////////////////////////////////

View File

@ -9,6 +9,10 @@
#include "internal/pow10.h"
#include "internal/stack.h"
#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
#include <intrin.h>
#pragma intrinsic(_BitScanForward)
#endif
#ifdef RAPIDJSON_SSE42
#include <nmmintrin.h>
#elif defined(RAPIDJSON_SSE2)
@ -16,8 +20,8 @@
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4127) // conditional expression is constant
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
#endif
#ifndef RAPIDJSON_PARSE_ERROR_NORETURN
@ -43,6 +47,8 @@ namespace rapidjson {
// ParseFlag
//! Combination of parseFlags
/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream
*/
enum ParseFlag {
kParseDefaultFlags = 0, //!< Default parse flags. Non-destructive parsing. Text strings are decoded into allocated buffer.
kParseInsituFlag = 1, //!< In-situ(destructive) parsing.
@ -260,7 +266,7 @@ template<> inline void SkipWhitespace(StringStream& is) {
///////////////////////////////////////////////////////////////////////////////
// GenericReader
//! SAX-style JSON parser. Use Reader for UTF8 encoding and default allocator.
//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator.
/*! GenericReader parses JSON text from a stream, and send events synchronously to an
object implementing Handler concept.
@ -279,7 +285,7 @@ template<> inline void SkipWhitespace(StringStream& is) {
template <typename SourceEncoding, typename TargetEncoding, typename Allocator = MemoryPoolAllocator<> >
class GenericReader {
public:
typedef typename SourceEncoding::Ch Ch;
typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type
//! Constructor.
/*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
@ -288,9 +294,9 @@ public:
GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {}
//! Parse JSON text.
/*! \tparam parseFlags Combination of ParseFlag.
\tparam InputStream Type of input stream.
\tparam Handler Type of handler which must implement Handler concept.
/*! \tparam parseFlags Combination of \ref ParseFlag.
\tparam InputStream Type of input stream, implementing Stream concept.
\tparam Handler Type of handler, implementing Handler concept.
\param is Input stream to be parsed.
\param handler The handler to receive events.
\return Whether the parsing is successful.
@ -327,6 +333,13 @@ public:
return !HasParseError();
}
//! Parse JSON text (with \ref kParseDefaultFlags)
/*! \tparam InputStream Type of input stream, implementing Stream concept
\tparam Handler Type of handler, implementing Handler concept.
\param is Input stream to be parsed.
\param handler The handler to receive events.
\return Whether the parsing is successful.
*/
template <typename InputStream, typename Handler>
bool Parse(InputStream& is, Handler& handler) {
return Parse<kParseDefaultFlags>(is, handler);
@ -1160,7 +1173,7 @@ typedef GenericReader<UTF8<>, UTF8<> > Reader;
} // namespace rapidjson
#ifdef _MSC_VER
#pragma warning(pop)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_READER_H_

View File

@ -37,6 +37,7 @@ struct GenericStringBuffer {
mutable internal::Stack<Allocator> stack_;
};
//! String buffer with UTF8 encoding
typedef GenericStringBuffer<UTF8<> > StringBuffer;
//! Implement specialized version of PutN() with memset() for better performance.

View File

@ -8,8 +8,8 @@
#include <new> // placement new
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4127) // conditional expression is constant
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
#endif
namespace rapidjson {
@ -25,8 +25,9 @@ namespace rapidjson {
for example Reader::Parse() and Document::Accept().
\tparam OutputStream Type of output stream.
\tparam SourceEncoding Encoding of both source strings.
\tparam TargetEncoding Encoding of and output stream.
\tparam SourceEncoding Encoding of source string.
\tparam TargetEncoding Encoding of output stream.
\tparam Allocator Type of allocator for allocating memory of stack.
\note implements Handler concept
*/
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> >
@ -34,6 +35,11 @@ class Writer {
public:
typedef typename SourceEncoding::Ch Ch;
//! Constructor
/*! \param os Output stream.
\param allocator User supplied allocator. If it is null, it will create a private one.
\param levelDepth Initial capacity of stack.
*/
Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
os_(os), level_stack_(allocator, levelDepth * sizeof(Level)),
doublePrecision_(kDefaultDoublePrecision) {}
@ -53,8 +59,11 @@ public:
//! \see SetDoublePrecision()
int GetDoublePrecision() const { return doublePrecision_; }
//@name Implementation of Handler
/*!@name Implementation of Handler
\see Handler
*/
//@{
Writer& Null() { Prefix(kNullType); WriteNull(); return *this; }
Writer& Bool(bool b) { Prefix(b ? kTrueType : kFalseType); WriteBool(b); return *this; }
Writer& Int(int i) { Prefix(kNumberType); WriteInt(i); return *this; }
@ -75,20 +84,6 @@ public:
*/
Writer& Double(double d) { Prefix(kNumberType); WriteDouble(d); return *this; }
//! Writes the given \c double value to the stream (explicit precision)
/*!
The currently set double precision is ignored in favor of the explicitly
given precision for this value.
\see Double(), SetDoublePrecision(), GetDoublePrecision()
\param d The value to be written
\param precision The number of significant digits for this value
\return The Writer itself for fluent API.
*/
Writer& Double(double d, int precision) {
int oldPrecision = GetDoublePrecision();
return SetDoublePrecision(precision).Double(d).SetDoublePrecision(oldPrecision);
}
Writer& String(const Ch* str, SizeType length, bool copy = false) {
(void)copy;
Prefix(kStringType);
@ -133,9 +128,28 @@ public:
}
//@}
/*! @name Convenience extensions */
//@{
//! Writes the given \c double value to the stream (explicit precision)
/*!
The currently set double precision is ignored in favor of the explicitly
given precision for this value.
\see Double(), SetDoublePrecision(), GetDoublePrecision()
\param d The value to be written
\param precision The number of significant digits for this value
\return The Writer itself for fluent API.
*/
Writer& Double(double d, int precision) {
int oldPrecision = GetDoublePrecision();
return SetDoublePrecision(precision).Double(d).SetDoublePrecision(oldPrecision);
}
//! Simpler but slower overload.
Writer& String(const Ch* str) { return String(str, internal::StrLen(str)); }
//@}
protected:
//! Information for each nested level
struct Level {
@ -171,7 +185,7 @@ protected:
char buffer[10];
char *p = buffer;
do {
*p++ = (u % 10) + '0';
*p++ = char(u % 10) + '0';
u /= 10;
} while (u > 0);
@ -292,7 +306,7 @@ private:
} // namespace rapidjson
#ifdef _MSC_VER
#pragma warning(pop)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_RAPIDJSON_H_

View File

@ -2,7 +2,9 @@
Copyright (c) 2011-2014 Milo Yip (miloyip@gmail.com)
https://github.com/miloyip/rapidjson/
[RapidJSON GitHub](https://github.com/miloyip/rapidjson/)
[RapidJSON Documentation](http://miloyip.github.io/rapidjson/)
## Introduction
@ -37,23 +39,29 @@ Users can build and run the unit tests on their platform/compiler.
## Installation
RapidJSON is a header-only C++ library. Just copy the `rapidjson/include/rapidjson` folder to system or project's include path.
RapidJSON is a header-only C++ library. Just copy the `include/rapidjson` folder to system or project's include path.
To build the tests and examples:
1. Obtain [premake4](http://industriousone.com/premake/download).
2. Copy premake4 executable to RapidJSON/build (or system path)
3. Run `rapidjson/build/premake.bat` on Windows, `RapidJSON/build/premake.sh` on Linux or other platforms
4. On Windows, build the solution at `rapidjson/build/vs2008/` or `/vs2010/`
5. On other platforms, run GNU make at `rapidjson/build/gmake/` (e.g., `make -f test.make config=release32`, `make -f example.make config=debug32`)
6. On success, the executable are generated at `rapidjson/bin`
2. Copy premake4 executable to RapidJSON/build (or system path).
3. Run `rapidjson/build/premake.bat` on Windows, `RapidJSON/build/premake.sh` on Linux or other platforms.
4. On Windows, build the solution at `rapidjson/build/vs2008/` or `/vs2010/`.
5. On other platforms, run GNU make at `rapidjson/build/gmake/` (e.g., `make -f test.make config=release32`; `make -f example.make config=debug32`).
6. On success, the executable are generated at `rapidjson/bin`.
To build the [Doxygen](http://doxygen.org) documentation:
1. Obtain and install [Doxygen](http://doxygen.org/download.html).
2. In the top-level directory, run `doxygen build/Doxyfile`.
3. Browse the generated documentation in `doc/html`.
## Usage at a glance
This simple example parses a JSON string into a document (DOM), make a simple modification of the DOM, and finally stringify the DOM to a JSON string.
[simpledom.cpp](example/simpledom/simpledom.cpp)
```cpp
~~~~~~~~~~cpp
// rapidjson/example/simpledom/simpledom.cpp`
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
@ -80,12 +88,12 @@ int main() {
std::cout << buffer.GetString() << std::endl;
return 0;
}
```
~~~~~~~~~~
Note that this example did not handle potential errors.
The following diagram shows the process.
![simpledom](doc/diagram/simpledom.png?raw=true)
![simpledom](doc/diagram/simpledom.png)
More [examples](example/) are available.

View File

@ -29,14 +29,16 @@
# define __STDC_CONSTANT_MACROS 1 // required by C++ standard
#endif
#ifdef __GNUC__
#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
#if defined(__clang__) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Weffc++"
#endif
#include "gtest/gtest.h"
#ifdef __GNUC__
#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic pop
#endif

View File

@ -190,8 +190,8 @@ TEST_F(RapidJson, DocumentTraverse) {
}
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
struct ValueCounter : public BaseReaderHandler<> {
@ -204,7 +204,7 @@ struct ValueCounter : public BaseReaderHandler<> {
};
#ifdef __GNUC__
#pragma GCC diagnostic pop
RAPIDJSON_DIAG_POP
#endif
TEST_F(RapidJson, DocumentAccept) {
@ -260,7 +260,7 @@ TEST_F(RapidJson, PrettyWriter_StringBuffer) {
TEST_F(RapidJson, internal_Pow10) {
double sum = 0;
for (size_t i = 0; i < kTrialCount * kTrialCount; i++)
sum += internal::Pow10(i & 255);
sum += internal::Pow10(int(i & 255));
EXPECT_GT(sum, 0.0);
}

View File

@ -6,8 +6,8 @@
using namespace rapidjson;
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
template<bool expect>
@ -1176,5 +1176,5 @@ TEST(Reader, IterativeParsing_StateTransition_ElementDelimiter) {
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
RAPIDJSON_DIAG_POP
#endif

View File

@ -13,22 +13,24 @@
#pragma warning(disable : 4996) // 'function': was declared deprecated
#endif
#ifdef __GNUC__
#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
#if defined(__clang__) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Weffc++"
#endif
#include "gtest/gtest.h"
#ifdef __GNUC__
#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic pop
#endif
template <typename Ch>
inline size_t StrLen(const Ch* s) {
inline unsigned StrLen(const Ch* s) {
const Ch* p = s;
while (*p) p++;
return p - s;
return unsigned(p - s);
}
template<typename Ch>

View File

@ -23,6 +23,25 @@ TEST(Value, assignment_operator) {
y = x;
EXPECT_TRUE(x.IsNull()); // move semantic
EXPECT_EQ(1234, y.GetInt());
y = 5678;
EXPECT_TRUE(y.IsInt());
EXPECT_EQ(5678, y.GetInt());
x = "Hello";
EXPECT_TRUE(x.IsString());
EXPECT_STREQ(x.GetString(),"Hello");
y = StringRef(x.GetString(),x.GetStringLength());
EXPECT_TRUE(y.IsString());
EXPECT_EQ(y.GetString(),x.GetString());
EXPECT_EQ(y.GetStringLength(),x.GetStringLength());
static char mstr[] = "mutable";
// y = mstr; // should not compile
y = StringRef(mstr);
EXPECT_TRUE(y.IsString());
EXPECT_EQ(y.GetString(),mstr);
}
template <typename Value>
@ -350,8 +369,8 @@ TEST(Value, Double) {
}
TEST(Value, String) {
// Constructor with const string
Value x("Hello", 5);
// Construction with const string
Value x("Hello", 5); // literal
EXPECT_EQ(kStringType, x.GetType());
EXPECT_TRUE(x.IsString());
EXPECT_STREQ("Hello", x.GetString());
@ -365,9 +384,41 @@ TEST(Value, String) {
EXPECT_FALSE(x.IsObject());
EXPECT_FALSE(x.IsArray());
static const char cstr[] = "World"; // const array
Value(cstr).Swap(x);
EXPECT_TRUE(x.IsString());
EXPECT_EQ(x.GetString(), cstr);
EXPECT_EQ(x.GetStringLength(), sizeof(cstr)-1);
static char mstr[] = "Howdy"; // non-const array
// Value(mstr).Swap(x); // should not compile
Value(StringRef(mstr)).Swap(x);
EXPECT_TRUE(x.IsString());
EXPECT_EQ(x.GetString(), mstr);
EXPECT_EQ(x.GetStringLength(), sizeof(mstr)-1);
strncpy(mstr,"Hello", sizeof(mstr));
EXPECT_STREQ(x.GetString(), "Hello");
const char* pstr = cstr;
//Value(pstr).Swap(x); // should not compile
Value(StringRef(pstr)).Swap(x);
EXPECT_TRUE(x.IsString());
EXPECT_EQ(x.GetString(), cstr);
EXPECT_EQ(x.GetStringLength(), sizeof(cstr)-1);
char* mpstr = mstr;
Value(StringRef(mpstr,sizeof(mstr)-1)).Swap(x);
EXPECT_TRUE(x.IsString());
EXPECT_EQ(x.GetString(), mstr);
EXPECT_EQ(x.GetStringLength(), 5u);
EXPECT_STREQ(x.GetString(), "Hello");
// Constructor with copy string
MemoryPoolAllocator<> allocator;
Value c(x.GetString(), x.GetStringLength(), allocator);
EXPECT_NE(x.GetString(), c.GetString());
EXPECT_EQ(x.GetStringLength(), c.GetStringLength());
EXPECT_STREQ(x.GetString(), c.GetString());
//x.SetString("World");
x.SetString("World", 5);
EXPECT_STREQ("Hello", c.GetString());
@ -381,11 +432,31 @@ TEST(Value, String) {
// SetConsttring()
Value z;
//z.SetString("Hello");
z.SetString("Hello");
EXPECT_TRUE(x.IsString());
z.SetString("Hello", 5);
EXPECT_STREQ("Hello", z.GetString());
EXPECT_STREQ("Hello", z.GetString());
EXPECT_EQ(5u, z.GetStringLength());
z.SetString("Hello");
EXPECT_TRUE(z.IsString());
EXPECT_STREQ("Hello", z.GetString());
//z.SetString(mstr); // should not compile
//z.SetString(pstr); // should not compile
z.SetString(StringRef(mstr));
EXPECT_TRUE(z.IsString());
EXPECT_STREQ(z.GetString(), mstr);
z.SetString(cstr);
EXPECT_TRUE(z.IsString());
EXPECT_EQ(cstr, z.GetString());
z = cstr;
EXPECT_TRUE(z.IsString());
EXPECT_EQ(cstr, z.GetString());
// SetString()
char s[] = "World";
Value w;
@ -424,11 +495,13 @@ TEST(Value, Array) {
x.PushBack(v, allocator);
v.SetInt(123);
x.PushBack(v, allocator);
//x.PushBack((const char*)"foo", allocator); // should not compile
x.PushBack("foo", allocator);
EXPECT_FALSE(x.Empty());
EXPECT_EQ(4u, x.Size());
EXPECT_EQ(5u, x.Size());
EXPECT_FALSE(y.Empty());
EXPECT_EQ(4u, y.Size());
EXPECT_EQ(5u, y.Size());
EXPECT_TRUE(x[SizeType(0)].IsNull());
EXPECT_TRUE(x[1u].IsTrue());
EXPECT_TRUE(x[2u].IsFalse());
@ -439,6 +512,8 @@ TEST(Value, Array) {
EXPECT_TRUE(y[2u].IsFalse());
EXPECT_TRUE(y[3u].IsInt());
EXPECT_EQ(123, y[3u].GetInt());
EXPECT_TRUE(y[4u].IsString());
EXPECT_STREQ("foo", y[4u].GetString());
// iterator
Value::ValueIterator itr = x.Begin();
@ -454,6 +529,10 @@ TEST(Value, Array) {
EXPECT_TRUE(itr != x.End());
EXPECT_TRUE(itr->IsInt());
EXPECT_EQ(123, itr->GetInt());
++itr;
EXPECT_TRUE(itr != x.End());
EXPECT_TRUE(itr->IsString());
EXPECT_STREQ("foo", itr->GetString());
// const iterator
Value::ConstValueIterator citr = y.Begin();
@ -469,13 +548,18 @@ TEST(Value, Array) {
EXPECT_TRUE(citr != y.End());
EXPECT_TRUE(citr->IsInt());
EXPECT_EQ(123, citr->GetInt());
++citr;
EXPECT_TRUE(citr != y.End());
EXPECT_TRUE(citr->IsString());
EXPECT_STREQ("foo", citr->GetString());
// PopBack()
x.PopBack();
EXPECT_EQ(3u, x.Size());
EXPECT_EQ(4u, x.Size());
EXPECT_TRUE(y[SizeType(0)].IsNull());
EXPECT_TRUE(y[1].IsTrue());
EXPECT_TRUE(y[2].IsFalse());
EXPECT_TRUE(y[1u].IsTrue());
EXPECT_TRUE(y[2u].IsFalse());
EXPECT_TRUE(y[3u].IsInt());
// Clear()
x.Clear();
@ -502,16 +586,34 @@ TEST(Value, Object) {
EXPECT_TRUE(y.IsObject());
// AddMember()
Value name("A", 1);
Value value("Apple", 5);
x.AddMember(name, value, allocator);
//name.SetString("B");
name.SetString("B", 1);
//value.SetString("Banana");
value.SetString("Banana", 6);
x.AddMember(name, value, allocator);
x.AddMember("A", "Apple", allocator);
Value value("Banana", 6);
x.AddMember("B", "Banana", allocator);
// AddMember<T>(StringRefType, T, Allocator)
{
Value o(kObjectType);
o.AddMember("true", true, allocator);
o.AddMember("false", false, allocator);
o.AddMember("int", -1, allocator);
o.AddMember("uint", 1u, allocator);
o.AddMember("int64", INT64_C(-4294967296), allocator);
o.AddMember("uint64", UINT64_C(4294967296), allocator);
o.AddMember("double", 3.14, allocator);
o.AddMember("string", "Jelly", allocator);
EXPECT_TRUE(o["true"].GetBool());
EXPECT_FALSE(o["false"].GetBool());
EXPECT_EQ(-1, o["int"].GetInt());
EXPECT_EQ(1u, o["uint"].GetUint());
EXPECT_EQ(INT64_C(-4294967296), o["int64"].GetInt64());
EXPECT_EQ(UINT64_C(4294967296), o["uint64"].GetUint64());
EXPECT_STREQ("Jelly",o["string"].GetString());
}
// Tests a member with null character
Value name;
const Value C0D("C\0D", 3);
name.SetString(C0D.GetString(), 3);
value.SetString("CherryD", 7);
@ -523,7 +625,7 @@ TEST(Value, Object) {
EXPECT_TRUE(y.HasMember("A"));
EXPECT_TRUE(y.HasMember("B"));
name.SetString("C\0D", 3);
name.SetString("C\0D");
EXPECT_TRUE(x.HasMember(name));
EXPECT_TRUE(y.HasMember(name));
@ -617,6 +719,7 @@ TEST(Value, BigNestedObject) {
char name1[10];
sprintf(name1, "%d", i);
// Value name(name1); // should not compile
Value name(name1, (SizeType)strlen(name1), allocator);
Value object(kObjectType);
@ -629,6 +732,7 @@ TEST(Value, BigNestedObject) {
object.AddMember(name, number, allocator);
}
// x.AddMember(name1, object, allocator); // should not compile
x.AddMember(name, object, allocator);
}