Merge upstream/master.
This commit is contained in:
commit
d4da07c5fb
19
.travis.yml
19
.travis.yml
@ -6,10 +6,13 @@ compiler:
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- config=debug64 config_suffix=debug_x64_gmake
|
||||
- config=release64 config_suffix=release_x64_gmake
|
||||
- CONF=debug
|
||||
- CONF=release
|
||||
global:
|
||||
secure: "CR3yKliFhwQLX+Zs1PCRcGej6jr4DIZsCqs9x6J2NN+U9Aow0gd/uiPBho/utgm+/TmSBji5n8FO/J3ORo34q4gC6EebTEaN4gCHNXVlIBJFw9x+Gs/lML5i8F2AoweFJY334OVaOf9qC8ZVJ8Z1nEwxj77fq3gcSLzRU3pIaS8="
|
||||
- GITHUB_REPO='miloyip/rapidjson'
|
||||
# prepare for 32/64 bit multi-arch support
|
||||
- BITS=64
|
||||
- secure: "HrsaCb+N66EG1HR+LWH1u51SjaJyRwJEDzqJGYMB7LJ/bfqb9mWKF1fLvZGk46W5t7TVaXRDD5KHFx9DPWvKn4gRUVkwTHEy262ah5ORh8M6n/6VVVajeV/AYt2C0sswdkDBDO4Xq+xy5gdw3G8s1A4Inbm73pUh+6vx+7ltBbk="
|
||||
|
||||
before_install:
|
||||
- sudo add-apt-repository -y ppa:codegear/release
|
||||
@ -22,11 +25,11 @@ before_script:
|
||||
- pushd build && premake4 'gmake' && popd
|
||||
|
||||
script:
|
||||
- make -C build/gmake -f test.make
|
||||
- make -C build/gmake -f example.make
|
||||
- make -C build/gmake -f test.make config=${CONF}${BITS}
|
||||
- make -C build/gmake -f example.make config=${CONF}${BITS}
|
||||
- pushd bin
|
||||
- ./unittest_${config_suffix}
|
||||
- valgrind --leak-check=full --error-exitcode=1 ./unittest_${config_suffix}
|
||||
- if [ "$config" = "release64" ]; then ./perftest_${config_suffix}; fi
|
||||
- ./unittest_${CONF}_x${BITS}_gmake
|
||||
- valgrind --leak-check=full --error-exitcode=1 ./unittest_${CONF}_x${BITS}_gmake
|
||||
- if [ "$CONF" = "release" ]; then ./perftest_${CONF}_x${BITS}_gmake; fi
|
||||
- popd
|
||||
- ./build/travis-doxygen.sh;
|
||||
|
@ -156,32 +156,10 @@ solution "example"
|
||||
configuration "gmake"
|
||||
buildoptions "-Werror -Wall -Wextra -Weffc++ -Wswitch-default"
|
||||
|
||||
project "condense"
|
||||
local examplepaths = os.matchdirs("../example/*")
|
||||
for _, examplepath in ipairs(examplepaths) do
|
||||
project(path.getname(examplepath))
|
||||
kind "ConsoleApp"
|
||||
files "../example/condense/*"
|
||||
setTargetObjDir("../bin")
|
||||
|
||||
project "pretty"
|
||||
kind "ConsoleApp"
|
||||
files "../example/pretty/*"
|
||||
setTargetObjDir("../bin")
|
||||
|
||||
project "prettyauto"
|
||||
kind "ConsoleApp"
|
||||
files "../example/prettyauto/*"
|
||||
setTargetObjDir("../bin")
|
||||
|
||||
project "tutorial"
|
||||
kind "ConsoleApp"
|
||||
files "../example/tutorial/*"
|
||||
setTargetObjDir("../bin")
|
||||
|
||||
project "serialize"
|
||||
kind "ConsoleApp"
|
||||
files "../example/serialize/*"
|
||||
setTargetObjDir("../bin")
|
||||
|
||||
project "simpledom"
|
||||
kind "ConsoleApp"
|
||||
files "../example/simpledom/*"
|
||||
files(examplepath .. "/*")
|
||||
setTargetObjDir("../bin")
|
||||
end
|
||||
|
@ -10,8 +10,13 @@ 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}"
|
||||
: ${GITHUB_REPO:="miloyip/rapidjson"}
|
||||
GITHUB_HOST="github.com"
|
||||
GITHUB_CLONE="git://${GITHUB_HOST}/${GITHUB_REPO}"
|
||||
GITHUB_URL="https://${GITHUB_HOST}/${GITHUB_PUSH-${GITHUB_REPO}}"
|
||||
|
||||
# if not set, ignore password
|
||||
#GIT_ASKPASS="${TRAVIS_BUILD_DIR}/gh_ignore_askpass.sh"
|
||||
|
||||
skip() {
|
||||
echo "$@" 1>&2
|
||||
@ -61,7 +66,8 @@ 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
|
||||
git --version
|
||||
git clone --single-branch -b gh-pages "${GITHUB_CLONE}" html
|
||||
cd html
|
||||
# setup git config (with defaults)
|
||||
git config user.name "${GIT_NAME-travis}"
|
||||
@ -77,17 +83,33 @@ gh_pages_commit() {
|
||||
git diff-index --quiet HEAD || git commit -m "Automatic doxygen build";
|
||||
}
|
||||
|
||||
gh_setup_askpass() {
|
||||
cat > ${GIT_ASKPASS} <<EOF
|
||||
#!/bin/bash
|
||||
echo
|
||||
exit 0
|
||||
EOF
|
||||
chmod a+x "$GIT_ASKPASS"
|
||||
}
|
||||
|
||||
gh_pages_push() {
|
||||
# check for secure variables
|
||||
[ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] || \
|
||||
skip "Secure variables not available, not updating GitHub pages."
|
||||
# check for GitHub access token
|
||||
[ "${GH_TOKEN+set}" = set ] || \
|
||||
skip "GitHub access token not available, not updating GitHub pages."
|
||||
[ "${#GH_TOKEN}" -eq 40 ] || \
|
||||
abort "GitHub token invalid: found ${#GH_TOKEN} characters, expected 40."
|
||||
|
||||
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}" )
|
||||
git remote set-url --push origin "${GITHUB_URL}"
|
||||
git config credential.helper 'store'
|
||||
# ( set +x ; git config credential.username "${GH_TOKEN}" )
|
||||
( set +x ; \
|
||||
echo "https://${GH_TOKEN}:@${GITHUB_HOST}" > ${HOME}/.git-credentials ; \
|
||||
chmod go-rw ${HOME}/.git-credentials )
|
||||
# push to GitHub
|
||||
git push origin gh-pages
|
||||
}
|
||||
|
481
doc/sax.md
481
doc/sax.md
@ -1,11 +1,482 @@
|
||||
# SAX
|
||||
|
||||
## Reader
|
||||
The term "SAX" originated from [Simple API for XML](http://en.wikipedia.org/wiki/Simple_API_for_XML). We borrowed this term for JSON parsing and generation.
|
||||
|
||||
### Handler
|
||||
In RapidJSON, `Reader` (typedef of `GenericReader<...>`) is the SAX-style parser for JSON, and `Writer` (typedef of `GenericWriter<...>`) is the SAX-style generator for JSON.
|
||||
|
||||
### Parse Error
|
||||
[TOC]
|
||||
|
||||
## Writer
|
||||
# Reader {#Reader}
|
||||
|
||||
### PrettyWriter
|
||||
`Reader` parses a JSON from a stream. While it reads characters from the stream, it analyze the characters according to the syntax of JSON, and publish events to a handler.
|
||||
|
||||
For example, here is a JSON.
|
||||
|
||||
~~~~~~~~~~js
|
||||
{
|
||||
"hello": "world",
|
||||
"t": true ,
|
||||
"f": false,
|
||||
"n": null,
|
||||
"i": 123,
|
||||
"pi": 3.1416,
|
||||
"a": [1, 2, 3, 4]
|
||||
}
|
||||
~~~~~~~~~~
|
||||
|
||||
While a `Reader` parses this JSON, it publishes the following events to the handler sequentially:
|
||||
|
||||
~~~~~~~~~~
|
||||
BeginObject()
|
||||
String("hello", 5, true)
|
||||
String("world", 5, true)
|
||||
String("t", 1, true)
|
||||
Bool(true)
|
||||
String("f", 1, true)
|
||||
Bool(false)
|
||||
String("n", 1, true)
|
||||
Null()
|
||||
String("i")
|
||||
UInt(123)
|
||||
String("pi")
|
||||
Double(3.1416)
|
||||
String("a")
|
||||
BeginArray()
|
||||
Uint(1)
|
||||
Uint(2)
|
||||
Uint(3)
|
||||
Uint(4)
|
||||
EndArray(4)
|
||||
EndObject(7)
|
||||
~~~~~~~~~~
|
||||
|
||||
These events can be easily matched with the JSON, except some event parameters need further explanation. Let's see the `simplereader` example which produces exactly the same output as above:
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
#include "rapidjson/reader.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace rapidjson;
|
||||
using namespace std;
|
||||
|
||||
struct MyHandler {
|
||||
bool Null() { cout << "Null()" << endl; return true; }
|
||||
bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; }
|
||||
bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; }
|
||||
bool Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; return true; }
|
||||
bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; }
|
||||
bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; }
|
||||
bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; }
|
||||
bool String(const char* str, SizeType length, bool copy) {
|
||||
cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
|
||||
return true;
|
||||
}
|
||||
bool StartObject() { cout << "StartObject()" << endl; return true; }
|
||||
bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
|
||||
bool StartArray() { cout << "StartArray()" << endl; return true; }
|
||||
bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
|
||||
};
|
||||
|
||||
void main() {
|
||||
const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
|
||||
|
||||
MyHandler handler;
|
||||
Reader reader;
|
||||
StringStream ss(json);
|
||||
reader.Parse(ss, handler);
|
||||
}
|
||||
~~~~~~~~~~
|
||||
|
||||
Note that, RapidJSON uses template to statically bind the `Reader` type and the handler type, instead of using class with virtual functions. This paradigm can improve the performance by inlining functions.
|
||||
|
||||
## Handler {#Handler}
|
||||
|
||||
As the previous example showed, user needs to implement a handler, which consumes the events (function calls) from `Reader`. The handler must contain the following member functions.
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
class Handler {
|
||||
bool Null();
|
||||
bool Bool(bool b);
|
||||
bool Int(int i);
|
||||
bool Uint(unsigned i);
|
||||
bool Int64(int64_t i);
|
||||
bool Uint64(uint64_t i);
|
||||
bool Double(double d);
|
||||
bool String(const Ch* str, SizeType length, bool copy);
|
||||
bool StartObject();
|
||||
bool EndObject(SizeType memberCount);
|
||||
bool StartArray();
|
||||
bool EndArray(SizeType elementCount);
|
||||
};
|
||||
~~~~~~~~~~
|
||||
|
||||
`Null()` is called when the `Reader` encounters a JSON null value.
|
||||
|
||||
`Bool(bool)` is called when the `Reader` encounters a JSON true or false value.
|
||||
|
||||
When the `Reader` encounters a JSON number, it chooses a suitable C++ type mapping. And then it calls *one* function out of `Int(int)`, `Uint(unsigned)`, `Int64(int64_t)`, `Uint64(uint64_t)` and `Double(double)`.
|
||||
|
||||
`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `'\0'` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And beware that, the character type depends on the target encoding, which will be explained later.
|
||||
|
||||
When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `String()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeats until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler, user may not need this parameter.
|
||||
|
||||
Array is similar to object but simpler. At the beginning of an array, the `Reader` calls `BeginArary()`. If there is elements, it calls functions according to the types of element. Similarly, in the last call `EndArray(SizeType elementCount)`, the parameter `elementCount` is just an aid for the handler.
|
||||
|
||||
Every handler functions returns a `bool`. Normally it should returns `true`. If the handler encounters an error, it can return `false` to notify event publisher to stop further processing.
|
||||
|
||||
For example, when we parse a JSON with `Reader` and the handler detected that the JSON does not conform to the required schema, then the handler can return `false` and let the `Reader` stop further parsing. And the `Reader` will be in error state with error code `kParseErrorTermination`.
|
||||
|
||||
## GenericReader {#GenericReader}
|
||||
|
||||
As mentioned before, `Reader` is a typedef of a template class `GenericReader`:
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
namespace rapidjson {
|
||||
|
||||
template <typename SourceEncoding, typename TargetEncoding, typename Allocator = MemoryPoolAllocator<> >
|
||||
class GenericReader {
|
||||
// ...
|
||||
};
|
||||
|
||||
typedef GenericReader<UTF8<>, UTF8<> > Reader;
|
||||
|
||||
} // namespace rapidjson
|
||||
~~~~~~~~~~
|
||||
|
||||
The `Reader` uses UTF-8 as both source and target encoding. The source encoding means the encoding in the JSON stream. The target encoding means the encoding of the `str` parameter in `String()` calls. For example, to parse a UTF-8 stream and outputs UTF-16 string events, you can define a reader by:
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
GenericReader<UTF8<>, UTF16<> > reader;
|
||||
~~~~~~~~~~
|
||||
|
||||
Note that, the default character type of `UTF16` is `wchar_t`. So this `reader`needs to call `String(const wchar_t*, SizeType, bool)` of the handler.
|
||||
|
||||
The third template parameter `Allocator` is the allocator type for internal data structure (actually a stack).
|
||||
|
||||
## Parsing {#Parsing}
|
||||
|
||||
The one and only one function of `Reader` is to parse JSON.
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
template <unsigned parseFlags, typename InputStream, typename Handler>
|
||||
bool Parse(InputStream& is, Handler& handler);
|
||||
|
||||
// with parseFlags = kDefaultParseFlags
|
||||
template <typename InputStream, typename Handler>
|
||||
bool Parse(InputStream& is, Handler& handler);
|
||||
~~~~~~~~~~
|
||||
|
||||
If an error occurs during parsing, it will return `false`. User can also calls `bool HasParseEror()`, `ParseErrorCode GetParseErrorCode()` and `size_t GetErrorOffset()` to obtain the error states. Actually `Document` uses these `Reader` functions to obtain parse errors. Please refer to [DOM](doc/dom.md) for details about parse error.
|
||||
|
||||
# Writer {#Writer}
|
||||
|
||||
`Reader` converts (parses) JSON into events. `Writer` does exactly the opposite. It converts events into JSON.
|
||||
|
||||
`Writer` is very easy to use. If your application only need to converts some data into JSON, it may be a good choice to use `Writer` directly, instead of building a `Document` and then stringifying it with a `Writer`.
|
||||
|
||||
In `simplewriter` example, we do exactly the reverse of `simplereader`.
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace rapidjson;
|
||||
using namespace std;
|
||||
|
||||
void main() {
|
||||
StringBuffer s;
|
||||
Writer<StringBuffer> writer(s);
|
||||
|
||||
writer.StartObject();
|
||||
writer.String("hello");
|
||||
writer.String("world");
|
||||
writer.String("t");
|
||||
writer.Bool(true);
|
||||
writer.String("f");
|
||||
writer.Bool(false);
|
||||
writer.String("n");
|
||||
writer.Null();
|
||||
writer.String("i");
|
||||
writer.Uint(123);
|
||||
writer.String("pi");
|
||||
writer.Double(3.1416);
|
||||
writer.String("a");
|
||||
writer.StartArray();
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
writer.Uint(i);
|
||||
writer.EndArray();
|
||||
writer.EndObject();
|
||||
|
||||
cout << s.GetString() << endl;
|
||||
}
|
||||
~~~~~~~~~~
|
||||
|
||||
~~~~~~~~~~
|
||||
{"hello":"world","t":true,"f":false,"n":null,"i":123,"pi":3.1416,"a":[0,1,2,3]}
|
||||
~~~~~~~~~~
|
||||
|
||||
There is two `String()` overloads. One is the same as defined in handler concept with 3 parameters. It can handle string with null characters. Another one is the simpler version used in the above example.
|
||||
|
||||
Note that, the example code does not pass any parameters in `EndArray()` and `EndObject()`. An `SizeType` can be passed but it will be simply ignored by `Writer`.
|
||||
|
||||
You may doubt that, why not just using `sprintf()` or `std::stringstream` to build a JSON?
|
||||
|
||||
There are various reasons:
|
||||
1. `Writer` must output a well-formed JSON. If there is incorrect event sequence (e.g. `Int()` just after `StartObject()`), it generates assertion fail in debug mode.
|
||||
2. `Writer::String()` can handle string escaping (e.g. converting code point `U+000A` to `\n`) and Unicode transcoding.
|
||||
3. `Writer` handles number output consistently. For example, user can set precision for `Double()`.
|
||||
4. `Writer` implements the event handler concept. It can be used to handle events from `Reader`, `Document` or other event publisher.
|
||||
5. `Writer` can be optimized for different platforms.
|
||||
|
||||
Anyway, using `Writer` API is even simpler than generating a JSON by ad hoc methods.
|
||||
|
||||
## Template {#WriterTemplate}
|
||||
|
||||
`Writer` has a minor design difference to `Reader`. `Writer` is a template class, not a typedef. There is no `GenericWriter`. The following is the declaration.
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
namespace rapidjson {
|
||||
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = CrtAllocator<> >
|
||||
class Writer {
|
||||
public:
|
||||
Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth)
|
||||
// ...
|
||||
};
|
||||
|
||||
} // namespace rapidjson
|
||||
~~~~~~~~~~
|
||||
|
||||
The `OutputStream` template parameter is the type of output stream. It cannot be deduced and must be specified by user.
|
||||
|
||||
The `SourceEncoding` template parameter specifies the encoding to be used in `String(const Ch*, ...)`.
|
||||
|
||||
The `TargetEncoding` template parameter specifies the encoding in the output stream.
|
||||
|
||||
The last one, `Allocator` is the type of allocator, which is used for allocating internal data structure (a stack).
|
||||
|
||||
Besides, the constructor of `Writer` has a `levelDepth` parameter. This parameter affects the initial memory allocated for storing information per hierarchy level.
|
||||
|
||||
## Precision (#WriterPrecision)
|
||||
|
||||
When using `Double()`, the precision of output can be specified, for example:
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
writer.SetDoublePrecision(4);
|
||||
writer.StartArary();
|
||||
writer.Double(3.14159265359);
|
||||
writer.EndArray();
|
||||
~~~~~~~~~~
|
||||
~~~~~~~~~~
|
||||
[3.1416]
|
||||
~~~~~~~~~~
|
||||
|
||||
## PrettyWriter {#PrettyWriter}
|
||||
|
||||
While the output of `Writer` is the most condensed JSON without white-spaces, suitable for network transfer or storage, it is not easily readable by human.
|
||||
|
||||
Therefore, RapidJSON provides a `PrettyWriter`, which adds indentation and line feeds in the output.
|
||||
|
||||
The usage of `PrettyWriter` is exactly the same as `Writer`, expect that `PrettyWriter` provides a `SetIndent(Ch indentChar, unsigned indentCharCount)` function. The default is 4 spaces.
|
||||
|
||||
## Completeness and Reset {#CompletenessReset}
|
||||
|
||||
A `Writer` can only output a single JSON, which can be either an object or array at the root. Once the last matching `EndObject()` or `EndArray()` event is handled, the output JSON is well-formed and complete. User can detect this state by calling `Writer::IsComplete()`.
|
||||
|
||||
When a JSON is complete, the `Writer` cannot accept any new events. Otherwise the output will be invalid (i.e. having more than one root). To reuse the `Writer` object, user can call `Writer::Reset(OutputStream& os)` to reset all internal states of the `Writer` with a new output stream.
|
||||
|
||||
# Techniques {#Techniques}
|
||||
|
||||
## Parsing JSON to Custom Data Structure {#CustomDataStructure}
|
||||
|
||||
`Document`'s parsing capability is completely based on `Reader`. Actually `Document` is a handler which receives events from a reader to build a DOM during parsing.
|
||||
|
||||
User may uses `Reader` to build other data structures directly. This eliminates building of DOM, thus reducing memory and improving performance.
|
||||
|
||||
In the following `messagereader` example, `ParseMessages()` parses a JSON which should be an object with key-string pairs.
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
#include "rapidjson/reader.h"
|
||||
#include "rapidjson/error/en.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
|
||||
typedef map<string, string> MessageMap;
|
||||
|
||||
struct MessageHandler : public BaseReaderHandler<> {
|
||||
MessageHandler() : state_(kExpectObjectStart) {
|
||||
}
|
||||
|
||||
bool StartObject() {
|
||||
switch (state_) {
|
||||
case kExpectObjectStart:
|
||||
state_ = kExpectNameOrObjectEnd;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool String(const char* str, SizeType length, bool) {
|
||||
switch (state_) {
|
||||
case kExpectNameOrObjectEnd:
|
||||
name_ = string(str, length);
|
||||
state_ = kExpectValue;
|
||||
return true;
|
||||
case kExpectValue:
|
||||
messages_.insert(MessageMap::value_type(name_, string(str, length)));
|
||||
state_ = kExpectNameOrObjectEnd;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EndObject(SizeType) { return state_ == kExpectNameOrObjectEnd; }
|
||||
|
||||
bool Default() { return false; } // All other events are invalid.
|
||||
|
||||
MessageMap messages_;
|
||||
enum State {
|
||||
kExpectObjectStart,
|
||||
kExpectNameOrObjectEnd,
|
||||
kExpectValue,
|
||||
}state_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
void ParseMessages(const char* json, MessageMap& messages) {
|
||||
Reader reader;
|
||||
MessageHandler handler;
|
||||
StringStream ss(json);
|
||||
if (reader.Parse(ss, handler))
|
||||
messages.swap(handler.messages_); // Only change it if success.
|
||||
else {
|
||||
ParseErrorCode e = reader.GetParseErrorCode();
|
||||
size_t o = reader.GetErrorOffset();
|
||||
cout << "Error: " << GetParseError_En(e) << endl;;
|
||||
cout << " at offset " << o << " near '" << string(json).substr(o, 10) << "...'" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
MessageMap messages;
|
||||
|
||||
const char* json1 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }";
|
||||
cout << json1 << endl;
|
||||
ParseMessages(json1, messages);
|
||||
|
||||
for (MessageMap::const_iterator itr = messages.begin(); itr != messages.end(); ++itr)
|
||||
cout << itr->first << ": " << itr->second << endl;
|
||||
|
||||
cout << endl << "Parse a JSON with invalid schema." << endl;
|
||||
const char* json2 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\", \"foo\" : {} }";
|
||||
cout << json2 << endl;
|
||||
ParseMessages(json2, messages);
|
||||
|
||||
return 0;
|
||||
}
|
||||
~~~~~~~~~~
|
||||
|
||||
~~~~~~~~~~
|
||||
{ "greeting" : "Hello!", "farewell" : "bye-bye!" }
|
||||
farewell: bye-bye!
|
||||
greeting: Hello!
|
||||
|
||||
Parse a JSON with invalid schema.
|
||||
{ "greeting" : "Hello!", "farewell" : "bye-bye!", "foo" : {} }
|
||||
Error: Terminate parsing due to Handler error.
|
||||
at offset 59 near '} }...'
|
||||
~~~~~~~~~~
|
||||
|
||||
The first JSON (`json1`) was successfully parsed into `MessageMap`. Since `MessageMap` is a `std::map`, the printing order are sorted by the key. This order is different from the JSON's order.
|
||||
|
||||
In the second JSON (`json2`), `foo`'s value is an empty object. As it is an object, `MessageHandler::StartObject()` will be called. However, at that moment `state_ = kExpectValue`, so that function returns `false` and cause the parsing process be terminated. The error code is `kParseErrorTermination`.
|
||||
|
||||
## Filtering of JSON {#Filtering}
|
||||
|
||||
As mentioned earlier, `Writer` can handle the events published by `Reader`. `condense` example simply set a `Writer` as handler of a `Reader`, so it can remove all white-spaces in JSON. `pretty` example uses the same relationship, but replacing `Writer` by `PrettyWriter`. So `pretty` can be used to reformat a JSON with indentation and line feed.
|
||||
|
||||
Actually, we can add intermediate layer(s) to filter the contents of JSON via these SAX-style API. For example, `capitalize` example capitalize all strings in a JSON.
|
||||
|
||||
~~~~~~~~~~cpp
|
||||
#include "rapidjson/reader.h"
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/filereadstream.h"
|
||||
#include "rapidjson/filewritestream.h"
|
||||
#include "rapidjson/error/en.h"
|
||||
#include <vector>
|
||||
#include <cctype>
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
template<typename OutputHandler>
|
||||
struct CapitalizeFilter {
|
||||
CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() {
|
||||
}
|
||||
|
||||
bool Null() { return out_.Null(); }
|
||||
bool Bool(bool b) { return out_.Bool(b); }
|
||||
bool Int(int i) { return out_.Int(i); }
|
||||
bool Uint(unsigned u) { return out_.Uint(u); }
|
||||
bool Int64(int64_t i) { return out_.Int64(i); }
|
||||
bool Uint64(uint64_t u) { return out_.Uint64(u); }
|
||||
bool Double(double d) { return out_.Double(d); }
|
||||
bool String(const char* str, SizeType length, bool) {
|
||||
buffer_.clear();
|
||||
for (SizeType i = 0; i < length; i++)
|
||||
buffer_.push_back(std::toupper(str[i]));
|
||||
return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string
|
||||
}
|
||||
bool StartObject() { return out_.StartObject(); }
|
||||
bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); }
|
||||
bool StartArray() { return out_.StartArray(); }
|
||||
bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); }
|
||||
|
||||
OutputHandler& out_;
|
||||
std::vector<char> buffer_;
|
||||
};
|
||||
|
||||
int main(int, char*[]) {
|
||||
// Prepare JSON reader and input stream.
|
||||
Reader reader;
|
||||
char readBuffer[65536];
|
||||
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
|
||||
|
||||
// Prepare JSON writer and output stream.
|
||||
char writeBuffer[65536];
|
||||
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
|
||||
Writer<FileWriteStream> writer(os);
|
||||
|
||||
// JSON reader parse from the input stream and let writer generate the output.
|
||||
CapitalizeFilter<Writer<FileWriteStream> > filter(writer);
|
||||
if (!reader.Parse(is, filter)) {
|
||||
fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
~~~~~~~~~~
|
||||
|
||||
Note that, it is incorrect to simply capitalize the JSON as a string. For example:
|
||||
~~~~~~~~~~
|
||||
["Hello\nWorld"]
|
||||
~~~~~~~~~~
|
||||
|
||||
Simply capitalizing the whole JSON would contain incorrect escape character:
|
||||
~~~~~~~~~~
|
||||
["HELLO\NWORLD"]
|
||||
~~~~~~~~~~
|
||||
|
||||
The correct result by `capitalize`:
|
||||
~~~~~~~~~~
|
||||
["HELLO\nWORLD"]
|
||||
~~~~~~~~~~
|
||||
|
||||
More complicated filters can be developed. However, since SAX-style API can only provide information about a single event at a time, user may need to book-keeping the contextual information (e.g. the path from root value, storage of other related values). Some processing may be easier to be implemented in DOM than SAX.
|
||||
|
65
example/capitalize/capitalize.cpp
Normal file
65
example/capitalize/capitalize.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
// JSON condenser exmaple
|
||||
|
||||
// This example parses JSON from stdin with validation,
|
||||
// and re-output the JSON content to stdout with all string capitalized, and without whitespace.
|
||||
|
||||
#include "rapidjson/reader.h"
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/filereadstream.h"
|
||||
#include "rapidjson/filewritestream.h"
|
||||
#include "rapidjson/error/en.h"
|
||||
#include <vector>
|
||||
#include <cctype>
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
template<typename OutputHandler>
|
||||
struct CapitalizeFilter {
|
||||
CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() {}
|
||||
|
||||
bool Null() { return out_.Null(); }
|
||||
bool Bool(bool b) { return out_.Bool(b); }
|
||||
bool Int(int i) { return out_.Int(i); }
|
||||
bool Uint(unsigned u) { return out_.Uint(u); }
|
||||
bool Int64(int64_t i) { return out_.Int64(i); }
|
||||
bool Uint64(uint64_t u) { return out_.Uint64(u); }
|
||||
bool Double(double d) { return out_.Double(d); }
|
||||
bool String(const char* str, SizeType length, bool) {
|
||||
buffer_.clear();
|
||||
for (SizeType i = 0; i < length; i++)
|
||||
buffer_.push_back(std::toupper(str[i]));
|
||||
return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string
|
||||
}
|
||||
bool StartObject() { return out_.StartObject(); }
|
||||
bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); }
|
||||
bool StartArray() { return out_.StartArray(); }
|
||||
bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); }
|
||||
|
||||
OutputHandler& out_;
|
||||
std::vector<char> buffer_;
|
||||
|
||||
private:
|
||||
CapitalizeFilter(const CapitalizeFilter&);
|
||||
CapitalizeFilter& operator=(const CapitalizeFilter&);
|
||||
};
|
||||
|
||||
int main(int, char*[]) {
|
||||
// Prepare JSON reader and input stream.
|
||||
Reader reader;
|
||||
char readBuffer[65536];
|
||||
FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
|
||||
|
||||
// Prepare JSON writer and output stream.
|
||||
char writeBuffer[65536];
|
||||
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
|
||||
Writer<FileWriteStream> writer(os);
|
||||
|
||||
// JSON reader parse from the input stream and let writer generate the output.
|
||||
CapitalizeFilter<Writer<FileWriteStream> > filter(writer);
|
||||
if (!reader.Parse(is, filter)) {
|
||||
fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
95
example/messagereader/messagereader.cpp
Normal file
95
example/messagereader/messagereader.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
// Reading a message JSON with Reader (SAX-style API).
|
||||
// The JSON should be an object with key-string pairs.
|
||||
|
||||
#include "rapidjson/reader.h"
|
||||
#include "rapidjson/error/en.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
|
||||
typedef map<string, string> MessageMap;
|
||||
|
||||
#if defined(__GNUC__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
struct MessageHandler : public BaseReaderHandler<> {
|
||||
MessageHandler() : messages_(), state_(kExpectObjectStart), name_() {}
|
||||
|
||||
bool StartObject() {
|
||||
switch (state_) {
|
||||
case kExpectObjectStart:
|
||||
state_ = kExpectNameOrObjectEnd;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool String(const char* str, SizeType length, bool) {
|
||||
switch (state_) {
|
||||
case kExpectNameOrObjectEnd:
|
||||
name_ = string(str, length);
|
||||
state_ = kExpectValue;
|
||||
return true;
|
||||
case kExpectValue:
|
||||
messages_.insert(MessageMap::value_type(name_, string(str, length)));
|
||||
state_ = kExpectNameOrObjectEnd;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EndObject(SizeType) { return state_ == kExpectNameOrObjectEnd; }
|
||||
|
||||
bool Default() { return false; } // All other events are invalid.
|
||||
|
||||
MessageMap messages_;
|
||||
enum State {
|
||||
kExpectObjectStart,
|
||||
kExpectNameOrObjectEnd,
|
||||
kExpectValue,
|
||||
}state_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
#if defined(__GNUC__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
void ParseMessages(const char* json, MessageMap& messages) {
|
||||
Reader reader;
|
||||
MessageHandler handler;
|
||||
StringStream ss(json);
|
||||
if (reader.Parse(ss, handler))
|
||||
messages.swap(handler.messages_); // Only change it if success.
|
||||
else {
|
||||
ParseErrorCode e = reader.GetParseErrorCode();
|
||||
size_t o = reader.GetErrorOffset();
|
||||
cout << "Error: " << GetParseError_En(e) << endl;;
|
||||
cout << " at offset " << o << " near '" << string(json).substr(o, 10) << "...'" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
MessageMap messages;
|
||||
|
||||
const char* json1 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }";
|
||||
cout << json1 << endl;
|
||||
ParseMessages(json1, messages);
|
||||
|
||||
for (MessageMap::const_iterator itr = messages.begin(); itr != messages.end(); ++itr)
|
||||
cout << itr->first << ": " << itr->second << endl;
|
||||
|
||||
cout << endl << "Parse a JSON with invalid schema." << endl;
|
||||
const char* json2 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\", \"foo\" : {} }";
|
||||
cout << json2 << endl;
|
||||
ParseMessages(json2, messages);
|
||||
|
||||
return 0;
|
||||
}
|
34
example/simplereader/simplereader.cpp
Normal file
34
example/simplereader/simplereader.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "rapidjson/reader.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace rapidjson;
|
||||
using namespace std;
|
||||
|
||||
struct MyHandler {
|
||||
bool Null() { cout << "Null()" << endl; return true; }
|
||||
bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; }
|
||||
bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; }
|
||||
bool Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; return true; }
|
||||
bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; }
|
||||
bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; }
|
||||
bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; }
|
||||
bool String(const char* str, SizeType length, bool copy) {
|
||||
cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
|
||||
return true;
|
||||
}
|
||||
bool StartObject() { cout << "StartObject()" << endl; return true; }
|
||||
bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
|
||||
bool StartArray() { cout << "StartArray()" << endl; return true; }
|
||||
bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
|
||||
};
|
||||
|
||||
int main() {
|
||||
const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
|
||||
|
||||
MyHandler handler;
|
||||
Reader reader;
|
||||
StringStream ss(json);
|
||||
reader.Parse(ss, handler);
|
||||
|
||||
return 0;
|
||||
}
|
35
example/simplewriter/simplewriter.cpp
Normal file
35
example/simplewriter/simplewriter.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace rapidjson;
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
StringBuffer s;
|
||||
Writer<StringBuffer> writer(s);
|
||||
|
||||
writer.StartObject();
|
||||
writer.String("hello");
|
||||
writer.String("world");
|
||||
writer.String("t");
|
||||
writer.Bool(true);
|
||||
writer.String("f");
|
||||
writer.Bool(false);
|
||||
writer.String("n");
|
||||
writer.Null();
|
||||
writer.String("i");
|
||||
writer.Uint(123);
|
||||
writer.String("pi");
|
||||
writer.Double(3.1416);
|
||||
writer.String("a");
|
||||
writer.StartArray();
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
writer.Uint(i);
|
||||
writer.EndArray();
|
||||
writer.EndObject();
|
||||
|
||||
cout << s.GetString() << endl;
|
||||
|
||||
return 0;
|
||||
}
|
@ -501,7 +501,8 @@ public:
|
||||
\see GenericStringRef, operator=(T)
|
||||
*/
|
||||
GenericValue& operator=(StringRefType str) {
|
||||
return (*this).operator=<StringRefType>(str);
|
||||
GenericValue s(str);
|
||||
return *this = s;
|
||||
}
|
||||
|
||||
//! Assignment with primitive types.
|
||||
@ -519,9 +520,8 @@ public:
|
||||
template <typename T>
|
||||
RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer<T>,GenericValue&)
|
||||
operator=(T value) {
|
||||
this->~GenericValue();
|
||||
new (this) GenericValue(value);
|
||||
return *this;
|
||||
GenericValue v(value);
|
||||
return *this = v;
|
||||
}
|
||||
|
||||
//! Deep-copy assignment from Value
|
||||
@ -1015,44 +1015,45 @@ int z = a[0u].GetInt(); // This works too.
|
||||
\param handler An object implementing concept Handler.
|
||||
*/
|
||||
template <typename Handler>
|
||||
const GenericValue& Accept(Handler& handler) const {
|
||||
bool Accept(Handler& handler) const {
|
||||
switch(GetType()) {
|
||||
case kNullType: handler.Null(); break;
|
||||
case kFalseType: handler.Bool(false); break;
|
||||
case kTrueType: handler.Bool(true); break;
|
||||
case kNullType: return handler.Null();
|
||||
case kFalseType: return handler.Bool(false);
|
||||
case kTrueType: return handler.Bool(true);
|
||||
|
||||
case kObjectType:
|
||||
handler.StartObject();
|
||||
if (!handler.StartObject())
|
||||
return false;
|
||||
for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) {
|
||||
handler.String(m->name.data_.s.str, m->name.data_.s.length, (m->name.flags_ & kCopyFlag) != 0);
|
||||
m->value.Accept(handler);
|
||||
if (!handler.String(m->name.data_.s.str, m->name.data_.s.length, (m->name.flags_ & kCopyFlag) != 0))
|
||||
return false;
|
||||
if (!m->value.Accept(handler))
|
||||
return false;
|
||||
}
|
||||
handler.EndObject(data_.o.size);
|
||||
break;
|
||||
return handler.EndObject(data_.o.size);
|
||||
|
||||
case kArrayType:
|
||||
handler.StartArray();
|
||||
if (!handler.StartArray())
|
||||
return false;
|
||||
for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v)
|
||||
v->Accept(handler);
|
||||
handler.EndArray(data_.a.size);
|
||||
break;
|
||||
if (!v->Accept(handler))
|
||||
return false;
|
||||
return handler.EndArray(data_.a.size);
|
||||
|
||||
case kStringType:
|
||||
handler.String(data_.s.str, data_.s.length, (flags_ & kCopyFlag) != 0);
|
||||
break;
|
||||
return handler.String(data_.s.str, data_.s.length, (flags_ & kCopyFlag) != 0);
|
||||
|
||||
case kNumberType:
|
||||
if (IsInt()) handler.Int(data_.n.i.i);
|
||||
else if (IsUint()) handler.Uint(data_.n.u.u);
|
||||
else if (IsInt64()) handler.Int64(data_.n.i64);
|
||||
else if (IsUint64()) handler.Uint64(data_.n.u64);
|
||||
else handler.Double(data_.n.d);
|
||||
break;
|
||||
if (IsInt()) return handler.Int(data_.n.i.i);
|
||||
else if (IsUint()) return handler.Uint(data_.n.u.u);
|
||||
else if (IsInt64()) return handler.Int64(data_.n.i64);
|
||||
else if (IsUint64()) return handler.Uint64(data_.n.u64);
|
||||
else return handler.Double(data_.n.d);
|
||||
|
||||
default:
|
||||
RAPIDJSON_ASSERT(false);
|
||||
}
|
||||
return *this;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1210,7 +1211,7 @@ public:
|
||||
/*! \param allocator Optional allocator for allocating stack memory.
|
||||
\param stackCapacity Initial capacity of stack in bytes.
|
||||
*/
|
||||
GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {}
|
||||
GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseResult_() {}
|
||||
|
||||
//!@name Parse from stream
|
||||
//!@{
|
||||
@ -1226,16 +1227,11 @@ public:
|
||||
GenericDocument& ParseStream(InputStream& is) {
|
||||
ValueType::SetNull(); // Remove existing root if exist
|
||||
GenericReader<SourceEncoding, Encoding, Allocator> reader(&GetAllocator());
|
||||
if (reader.template Parse<parseFlags>(is, *this)) {
|
||||
ClearStackOnExit scope(*this);
|
||||
parseResult_ = reader.template Parse<parseFlags>(is, *this);
|
||||
if (parseResult_) {
|
||||
RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
|
||||
this->RawAssign(*stack_.template Pop<ValueType>(1)); // Add this-> to prevent issue 13.
|
||||
parseErrorCode_ = kParseErrorNone;
|
||||
errorOffset_ = 0;
|
||||
}
|
||||
else {
|
||||
parseErrorCode_ = reader.GetParseErrorCode();
|
||||
errorOffset_ = reader.GetErrorOffset();
|
||||
ClearStack();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -1331,14 +1327,14 @@ public:
|
||||
//!@name Handling parse errors
|
||||
//!@{
|
||||
|
||||
//! Whether a parse error was occured in the last parsing.
|
||||
bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; }
|
||||
//! Whether a parse error has occured in the last parsing.
|
||||
bool HasParseError() const { return parseResult_.IsError(); }
|
||||
|
||||
//! Get the message of parsing error.
|
||||
ParseErrorCode GetParseError() const { return parseErrorCode_; }
|
||||
//! Get the \ref ParseErrorCode of last parsing.
|
||||
ParseErrorCode GetParseError() const { return parseResult_.Code(); }
|
||||
|
||||
//! Get the offset in character of the parsing error.
|
||||
size_t GetErrorOffset() const { return errorOffset_; }
|
||||
//! Get the position of last parsing error in input, 0 otherwise.
|
||||
size_t GetErrorOffset() const { return parseResult_.Offset(); }
|
||||
|
||||
//!@}
|
||||
|
||||
@ -1349,38 +1345,51 @@ public:
|
||||
size_t GetStackCapacity() const { return stack_.GetCapacity(); }
|
||||
|
||||
private:
|
||||
// clear stack on any exit from ParseStream, e.g. due to exception
|
||||
struct ClearStackOnExit {
|
||||
explicit ClearStackOnExit(GenericDocument& d) : d_(d) {}
|
||||
~ClearStackOnExit() { d_.ClearStack(); }
|
||||
private:
|
||||
ClearStackOnExit(const ClearStackOnExit&);
|
||||
ClearStackOnExit& operator=(const ClearStackOnExit&);
|
||||
GenericDocument& d_;
|
||||
};
|
||||
|
||||
// callers of the following private Handler functions
|
||||
template <typename,typename,typename> friend class GenericReader; // for parsing
|
||||
friend class GenericValue<Encoding,Allocator>; // for deep copying
|
||||
|
||||
// Implementation of Handler
|
||||
void Null() { new (stack_.template Push<ValueType>()) ValueType(); }
|
||||
void Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); }
|
||||
void Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); }
|
||||
void Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueType(i); }
|
||||
void Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); }
|
||||
void Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); }
|
||||
void Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); }
|
||||
bool Null() { new (stack_.template Push<ValueType>()) ValueType(); return true; }
|
||||
bool Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); return true; }
|
||||
bool Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
|
||||
bool Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
|
||||
bool Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
|
||||
bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
|
||||
bool Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); return true; }
|
||||
|
||||
void String(const Ch* str, SizeType length, bool copy) {
|
||||
bool String(const Ch* str, SizeType length, bool copy) {
|
||||
if (copy)
|
||||
new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
|
||||
else
|
||||
new (stack_.template Push<ValueType>()) ValueType(str, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); }
|
||||
bool StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); return true; }
|
||||
|
||||
void EndObject(SizeType memberCount) {
|
||||
bool EndObject(SizeType memberCount) {
|
||||
typename ValueType::Member* members = stack_.template Pop<typename ValueType::Member>(memberCount);
|
||||
stack_.template Top<ValueType>()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator());
|
||||
return true;
|
||||
}
|
||||
|
||||
void StartArray() { new (stack_.template Push<ValueType>()) ValueType(kArrayType); }
|
||||
bool StartArray() { new (stack_.template Push<ValueType>()) ValueType(kArrayType); return true; }
|
||||
|
||||
void EndArray(SizeType elementCount) {
|
||||
bool EndArray(SizeType elementCount) {
|
||||
ValueType* elements = stack_.template Pop<ValueType>(elementCount);
|
||||
stack_.template Top<ValueType>()->SetArrayRaw(elements, elementCount, GetAllocator());
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1397,8 +1406,7 @@ private:
|
||||
|
||||
static const size_t kDefaultStackCapacity = 1024;
|
||||
internal::Stack<Allocator> stack_;
|
||||
ParseErrorCode parseErrorCode_;
|
||||
size_t errorOffset_;
|
||||
ParseResult parseResult_;
|
||||
};
|
||||
|
||||
//! GenericDocument with UTF8 encoding
|
||||
|
@ -32,12 +32,13 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro
|
||||
case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
|
||||
case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
|
||||
case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
|
||||
case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoidng in string.");
|
||||
case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
|
||||
|
||||
case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
|
||||
case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
|
||||
case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
|
||||
|
||||
case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
|
||||
case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
|
||||
|
||||
default:
|
||||
|
@ -1,8 +1,6 @@
|
||||
#ifndef RAPIDJSON_ERROR_ERROR_H__
|
||||
#define RAPIDJSON_ERROR_ERROR_H__
|
||||
|
||||
#include "../reader.h" // ParseErrorCode
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ERROR_CHARTYPE
|
||||
|
||||
@ -29,6 +27,85 @@
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ParseErrorCode
|
||||
|
||||
//! Error code of parsing.
|
||||
/*! \see GenericReader::Parse, GenericReader::GetParseErrorCode
|
||||
*/
|
||||
enum ParseErrorCode {
|
||||
kParseErrorNone = 0, //!< 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.
|
||||
|
||||
kParseErrorTermination, //!< Parsing was terminated.
|
||||
kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
|
||||
};
|
||||
|
||||
//! Result of parsing (wraps ParseErrorCode)
|
||||
/*!
|
||||
\code
|
||||
Document doc;
|
||||
ParseResult ok = doc.Parse("[42]");
|
||||
if (!ok) {
|
||||
fprintf(stderr, "JSON parse error: %s (%u)",
|
||||
GetParseError_En(ok.Code()), ok.Offset());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
\endcode
|
||||
\see GenericReader::Parse, GenericDocument::Parse
|
||||
*/
|
||||
struct ParseResult {
|
||||
|
||||
//! Default constructor, no error.
|
||||
ParseResult() : code_(kParseErrorNone), offset_(0) {}
|
||||
//! Constructor to set an error.
|
||||
ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
|
||||
|
||||
//! Get the error code.
|
||||
ParseErrorCode Code() const { return code_; }
|
||||
//! Get the error offset, if \ref IsError(), 0 otherwise.
|
||||
size_t Offset() const { return offset_; }
|
||||
|
||||
//! Conversion to \c bool, returns \c true, iff !\ref IsError().
|
||||
operator bool() const { return !IsError(); }
|
||||
//! Whether the result is an error.
|
||||
bool IsError() const { return code_ != kParseErrorNone; }
|
||||
|
||||
bool operator==(const ParseResult& that) const { return code_ == that.code_; }
|
||||
bool operator==(ParseErrorCode code) const { return code_ == code; }
|
||||
friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
|
||||
|
||||
//! Reset error code.
|
||||
void Clear() { Set(kParseErrorNone); }
|
||||
//! Update error code and offset.
|
||||
void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
|
||||
|
||||
private:
|
||||
ParseErrorCode code_;
|
||||
size_t offset_;
|
||||
};
|
||||
|
||||
//! Function pointer type of GetParseError().
|
||||
/*! This is the prototype for GetParseError_X(), where X is a locale.
|
||||
User can dynamically change locale in runtime, e.g.:
|
||||
|
@ -51,29 +51,27 @@ public:
|
||||
*/
|
||||
//@{
|
||||
|
||||
PrettyWriter& Null() { PrettyPrefix(kNullType); Base::WriteNull(); return *this; }
|
||||
PrettyWriter& Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); Base::WriteBool(b); return *this; }
|
||||
PrettyWriter& Int(int i) { PrettyPrefix(kNumberType); Base::WriteInt(i); return *this; }
|
||||
PrettyWriter& Uint(unsigned u) { PrettyPrefix(kNumberType); Base::WriteUint(u); return *this; }
|
||||
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; }
|
||||
bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); }
|
||||
bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
|
||||
bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
|
||||
bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
|
||||
bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
|
||||
bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
|
||||
bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
|
||||
|
||||
PrettyWriter& String(const Ch* str, SizeType length, bool copy = false) {
|
||||
bool String(const Ch* str, SizeType length, bool copy = false) {
|
||||
(void)copy;
|
||||
PrettyPrefix(kStringType);
|
||||
Base::WriteString(str, length);
|
||||
return *this;
|
||||
return Base::WriteString(str, length);
|
||||
}
|
||||
|
||||
PrettyWriter& StartObject() {
|
||||
bool StartObject() {
|
||||
PrettyPrefix(kObjectType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
|
||||
Base::WriteStartObject();
|
||||
return *this;
|
||||
return Base::WriteStartObject();
|
||||
}
|
||||
|
||||
PrettyWriter& EndObject(SizeType memberCount = 0) {
|
||||
bool EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||
RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||
@ -83,20 +81,20 @@ public:
|
||||
Base::os_.Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
Base::WriteEndObject();
|
||||
if (!Base::WriteEndObject())
|
||||
return false;
|
||||
if (Base::level_stack_.Empty()) // end of json text
|
||||
Base::os_.Flush();
|
||||
return *this;
|
||||
return true;
|
||||
}
|
||||
|
||||
PrettyWriter& StartArray() {
|
||||
bool StartArray() {
|
||||
PrettyPrefix(kArrayType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
|
||||
Base::WriteStartArray();
|
||||
return *this;
|
||||
return Base::WriteStartArray();
|
||||
}
|
||||
|
||||
PrettyWriter& EndArray(SizeType memberCount = 0) {
|
||||
bool EndArray(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||
@ -106,10 +104,11 @@ public:
|
||||
Base::os_.Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
Base::WriteEndArray();
|
||||
if (!Base::WriteEndArray())
|
||||
return false;
|
||||
if (Base::level_stack_.Empty()) // end of json text
|
||||
Base::os_.Flush();
|
||||
return *this;
|
||||
return true;
|
||||
}
|
||||
|
||||
//@}
|
||||
@ -118,12 +117,15 @@ public:
|
||||
//@{
|
||||
|
||||
//! Simpler but slower overload.
|
||||
PrettyWriter& String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
|
||||
//! Overridden for fluent API, see \ref Writer::Double()
|
||||
PrettyWriter& Double(double d, int precision) {
|
||||
bool Double(double d, int precision) {
|
||||
int oldPrecision = Base::GetDoublePrecision();
|
||||
return SetDoublePrecision(precision).Double(d).SetDoublePrecision(oldPrecision);
|
||||
SetDoublePrecision(precision);
|
||||
bool ret = Double(d);
|
||||
SetDoublePrecision(oldPrecision);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
@ -24,12 +24,21 @@ RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
#define RAPIDJSON_NOTHING /* deliberately empty */
|
||||
#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN
|
||||
#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \
|
||||
RAPIDJSON_MULTILINEMACRO_BEGIN \
|
||||
if (HasParseError()) { return value; } \
|
||||
RAPIDJSON_MULTILINEMACRO_END
|
||||
#endif
|
||||
#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING)
|
||||
|
||||
#ifndef RAPIDJSON_PARSE_ERROR_NORETURN
|
||||
#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \
|
||||
RAPIDJSON_MULTILINEMACRO_BEGIN \
|
||||
RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \
|
||||
parseErrorCode_ = parseErrorCode; \
|
||||
errorOffset_ = offset; \
|
||||
parseResult_.Set(parseErrorCode,offset); \
|
||||
RAPIDJSON_MULTILINEMACRO_END
|
||||
#endif
|
||||
|
||||
@ -37,10 +46,12 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
||||
#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \
|
||||
RAPIDJSON_MULTILINEMACRO_BEGIN \
|
||||
RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \
|
||||
return; \
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \
|
||||
RAPIDJSON_MULTILINEMACRO_END
|
||||
#endif
|
||||
|
||||
#include "error/error.h" // ParseErrorCode, ParseResult
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -56,56 +67,29 @@ enum ParseFlag {
|
||||
kParseIterativeFlag = 4 //!< Iterative(constant complexity in terms of function call stack size) parsing.
|
||||
};
|
||||
|
||||
//! Error code of parsing.
|
||||
enum ParseErrorCode {
|
||||
kParseErrorNone = 0, //!< 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.
|
||||
|
||||
kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Handler
|
||||
|
||||
/*! \class rapidjson::Handler
|
||||
\brief Concept for receiving events from GenericReader upon parsing.
|
||||
The functions return true if no error occurs. If they return false,
|
||||
the event publisher should terminate the process.
|
||||
\code
|
||||
concept Handler {
|
||||
typename Ch;
|
||||
|
||||
void Null();
|
||||
void Bool(bool b);
|
||||
void Int(int i);
|
||||
void Uint(unsigned i);
|
||||
void Int64(int64_t i);
|
||||
void Uint64(uint64_t i);
|
||||
void Double(double d);
|
||||
void String(const Ch* str, SizeType length, bool copy);
|
||||
void StartObject();
|
||||
void EndObject(SizeType memberCount);
|
||||
void StartArray();
|
||||
void EndArray(SizeType elementCount);
|
||||
bool Null();
|
||||
bool Bool(bool b);
|
||||
bool Int(int i);
|
||||
bool Uint(unsigned i);
|
||||
bool Int64(int64_t i);
|
||||
bool Uint64(uint64_t i);
|
||||
bool Double(double d);
|
||||
bool String(const Ch* str, SizeType length, bool copy);
|
||||
bool StartObject();
|
||||
bool EndObject(SizeType memberCount);
|
||||
bool StartArray();
|
||||
bool EndArray(SizeType elementCount);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
@ -120,19 +104,19 @@ template<typename Encoding = UTF8<> >
|
||||
struct BaseReaderHandler {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
void Default() {}
|
||||
void Null() { Default(); }
|
||||
void Bool(bool) { Default(); }
|
||||
void Int(int) { Default(); }
|
||||
void Uint(unsigned) { Default(); }
|
||||
void Int64(int64_t) { Default(); }
|
||||
void Uint64(uint64_t) { Default(); }
|
||||
void Double(double) { Default(); }
|
||||
void String(const Ch*, SizeType, bool) { Default(); }
|
||||
void StartObject() { Default(); }
|
||||
void EndObject(SizeType) { Default(); }
|
||||
void StartArray() { Default(); }
|
||||
void EndArray(SizeType) { Default(); }
|
||||
bool Default() { return true; }
|
||||
bool Null() { return Default(); }
|
||||
bool Bool(bool) { return Default(); }
|
||||
bool Int(int) { return Default(); }
|
||||
bool Uint(unsigned) { return Default(); }
|
||||
bool Int64(int64_t) { return Default(); }
|
||||
bool Uint64(uint64_t) { return Default(); }
|
||||
bool Double(double) { return Default(); }
|
||||
bool String(const Ch*, SizeType, bool) { return Default(); }
|
||||
bool StartObject() { return Default(); }
|
||||
bool EndObject(SizeType) { return Default(); }
|
||||
bool StartArray() { return Default(); }
|
||||
bool EndArray(SizeType) { return Default(); }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -291,7 +275,7 @@ public:
|
||||
/*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
|
||||
\param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing)
|
||||
*/
|
||||
GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {}
|
||||
GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseResult_() {}
|
||||
|
||||
//! Parse JSON text.
|
||||
/*! \tparam parseFlags Combination of \ref ParseFlag.
|
||||
@ -302,35 +286,37 @@ public:
|
||||
\return Whether the parsing is successful.
|
||||
*/
|
||||
template <unsigned parseFlags, typename InputStream, typename Handler>
|
||||
bool Parse(InputStream& is, Handler& handler) {
|
||||
parseErrorCode_ = kParseErrorNone;
|
||||
errorOffset_ = 0;
|
||||
|
||||
ParseResult Parse(InputStream& is, Handler& handler) {
|
||||
if (parseFlags & kParseIterativeFlag)
|
||||
return IterativeParse<parseFlags>(is, handler);
|
||||
|
||||
parseResult_.Clear();
|
||||
|
||||
ClearStackOnExit scope(*this);
|
||||
|
||||
SkipWhitespace(is);
|
||||
|
||||
if (is.Peek() == '\0')
|
||||
if (is.Peek() == '\0') {
|
||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell());
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
|
||||
}
|
||||
else {
|
||||
switch (is.Peek()) {
|
||||
case '{': ParseObject<parseFlags>(is, handler); break;
|
||||
case '[': ParseArray<parseFlags>(is, handler); break;
|
||||
default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell());
|
||||
}
|
||||
if (HasParseError())
|
||||
goto out;
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
|
||||
|
||||
SkipWhitespace(is);
|
||||
|
||||
if (is.Peek() != '\0')
|
||||
if (is.Peek() != '\0') {
|
||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell());
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
stack_.Clear();
|
||||
return !HasParseError();
|
||||
return parseResult_;
|
||||
}
|
||||
|
||||
//! Parse JSON text (with \ref kParseDefaultFlags)
|
||||
@ -341,32 +327,51 @@ public:
|
||||
\return Whether the parsing is successful.
|
||||
*/
|
||||
template <typename InputStream, typename Handler>
|
||||
bool Parse(InputStream& is, Handler& handler) {
|
||||
ParseResult Parse(InputStream& is, Handler& handler) {
|
||||
return Parse<kParseDefaultFlags>(is, handler);
|
||||
}
|
||||
|
||||
bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; }
|
||||
//! Whether a parse error has occured in the last parsing.
|
||||
bool HasParseError() const { return parseResult_.IsError(); }
|
||||
|
||||
ParseErrorCode GetParseErrorCode() const { return parseErrorCode_; }
|
||||
//! Get the \ref ParseErrorCode of last parsing.
|
||||
ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); }
|
||||
|
||||
size_t GetErrorOffset() const { return errorOffset_; }
|
||||
//! Get the position of last parsing error in input, 0 otherwise.
|
||||
size_t GetErrorOffset() const { return parseResult_.Offset(); }
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
GenericReader(const GenericReader&);
|
||||
GenericReader& operator=(const GenericReader&);
|
||||
|
||||
void ClearStack() { stack_.Clear(); }
|
||||
|
||||
// clear stack on any exit from ParseStream, e.g. due to exception
|
||||
struct ClearStackOnExit {
|
||||
explicit ClearStackOnExit(GenericReader& r) : r_(r) {}
|
||||
~ClearStackOnExit() { r_.ClearStack(); }
|
||||
private:
|
||||
GenericReader& r_;
|
||||
ClearStackOnExit(const ClearStackOnExit&);
|
||||
ClearStackOnExit& operator=(const ClearStackOnExit&);
|
||||
};
|
||||
|
||||
// Parse object: { string : value, ... }
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseObject(InputStream& is, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(is.Peek() == '{');
|
||||
is.Take(); // Skip '{'
|
||||
handler.StartObject();
|
||||
|
||||
if (!handler.StartObject())
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
|
||||
|
||||
SkipWhitespace(is);
|
||||
|
||||
if (is.Peek() == '}') {
|
||||
is.Take();
|
||||
handler.EndObject(0); // empty object
|
||||
if (!handler.EndObject(0)) // empty object
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -375,8 +380,7 @@ private:
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
|
||||
|
||||
ParseString<parseFlags>(is, handler);
|
||||
if (HasParseError())
|
||||
return;
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
||||
|
||||
SkipWhitespace(is);
|
||||
|
||||
@ -386,16 +390,19 @@ private:
|
||||
SkipWhitespace(is);
|
||||
|
||||
ParseValue<parseFlags>(is, handler);
|
||||
if (HasParseError())
|
||||
return;
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
||||
|
||||
SkipWhitespace(is);
|
||||
|
||||
++memberCount;
|
||||
|
||||
switch(is.Take()) {
|
||||
switch (is.Take()) {
|
||||
case ',': SkipWhitespace(is); break;
|
||||
case '}': handler.EndObject(memberCount); return;
|
||||
case '}':
|
||||
if (!handler.EndObject(memberCount))
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
|
||||
else
|
||||
return;
|
||||
default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell());
|
||||
}
|
||||
}
|
||||
@ -406,26 +413,33 @@ private:
|
||||
void ParseArray(InputStream& is, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(is.Peek() == '[');
|
||||
is.Take(); // Skip '['
|
||||
handler.StartArray();
|
||||
|
||||
if (!handler.StartArray())
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
|
||||
|
||||
SkipWhitespace(is);
|
||||
|
||||
if (is.Peek() == ']') {
|
||||
is.Take();
|
||||
handler.EndArray(0); // empty array
|
||||
if (!handler.EndArray(0)) // empty array
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
|
||||
return;
|
||||
}
|
||||
|
||||
for (SizeType elementCount = 0;;) {
|
||||
ParseValue<parseFlags>(is, handler);
|
||||
if (HasParseError())
|
||||
return;
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
||||
|
||||
++elementCount;
|
||||
SkipWhitespace(is);
|
||||
|
||||
switch (is.Take()) {
|
||||
case ',': SkipWhitespace(is); break;
|
||||
case ']': handler.EndArray(elementCount); return;
|
||||
case ']':
|
||||
if (!handler.EndArray(elementCount))
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
|
||||
else
|
||||
return;
|
||||
default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());
|
||||
}
|
||||
}
|
||||
@ -436,8 +450,10 @@ private:
|
||||
RAPIDJSON_ASSERT(is.Peek() == 'n');
|
||||
is.Take();
|
||||
|
||||
if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l')
|
||||
handler.Null();
|
||||
if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') {
|
||||
if (!handler.Null())
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
|
||||
}
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1);
|
||||
}
|
||||
@ -447,10 +463,12 @@ private:
|
||||
RAPIDJSON_ASSERT(is.Peek() == 't');
|
||||
is.Take();
|
||||
|
||||
if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e')
|
||||
handler.Bool(true);
|
||||
if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') {
|
||||
if (!handler.Bool(true))
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
|
||||
}
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1);
|
||||
}
|
||||
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
@ -458,8 +476,10 @@ private:
|
||||
RAPIDJSON_ASSERT(is.Peek() == 'f');
|
||||
is.Take();
|
||||
|
||||
if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e')
|
||||
handler.Bool(false);
|
||||
if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') {
|
||||
if (!handler.Bool(false))
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
|
||||
}
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1);
|
||||
}
|
||||
@ -480,7 +500,7 @@ private:
|
||||
codepoint -= 'a' - 10;
|
||||
else {
|
||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1);
|
||||
return 0;
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0);
|
||||
}
|
||||
}
|
||||
return codepoint;
|
||||
@ -512,18 +532,18 @@ private:
|
||||
if (parseFlags & kParseInsituFlag) {
|
||||
typename InputStream::Ch *head = s.PutBegin();
|
||||
ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s);
|
||||
if (HasParseError())
|
||||
return;
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
||||
size_t length = s.PutEnd(head) - 1;
|
||||
RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
|
||||
handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false);
|
||||
if (!handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false))
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
|
||||
}
|
||||
else {
|
||||
StackStream stackStream(stack_);
|
||||
ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream);
|
||||
if (HasParseError())
|
||||
return;
|
||||
handler.String(stack_.template Pop<typename TargetEncoding::Ch>(stackStream.length_), stackStream.length_ - 1, true);
|
||||
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
||||
if (!handler.String(stack_.template Pop<typename TargetEncoding::Ch>(stackStream.length_), stackStream.length_ - 1, true))
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
|
||||
}
|
||||
}
|
||||
|
||||
@ -598,7 +618,7 @@ private:
|
||||
}
|
||||
|
||||
// Parse int: zero / ( digit1-9 *DIGIT )
|
||||
unsigned i;
|
||||
unsigned i = 0;
|
||||
bool try64bit = false;
|
||||
if (s.Peek() == '0') {
|
||||
i = 0;
|
||||
@ -725,6 +745,7 @@ private:
|
||||
}
|
||||
|
||||
// Finish parsing, call event according to the type of number.
|
||||
bool cont = true;
|
||||
if (useDouble) {
|
||||
int expSum = exp + expFrac;
|
||||
if (expSum < -308) {
|
||||
@ -735,22 +756,24 @@ private:
|
||||
else
|
||||
d *= internal::Pow10(expSum);
|
||||
|
||||
handler.Double(minus ? -d : d);
|
||||
cont = handler.Double(minus ? -d : d);
|
||||
}
|
||||
else {
|
||||
if (try64bit) {
|
||||
if (minus)
|
||||
handler.Int64(-(int64_t)i64);
|
||||
cont = handler.Int64(-(int64_t)i64);
|
||||
else
|
||||
handler.Uint64(i64);
|
||||
cont = handler.Uint64(i64);
|
||||
}
|
||||
else {
|
||||
if (minus)
|
||||
handler.Int(-(int)i);
|
||||
cont = handler.Int(-(int)i);
|
||||
else
|
||||
handler.Uint(i);
|
||||
cont = handler.Uint(i);
|
||||
}
|
||||
}
|
||||
if (!cont)
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
|
||||
}
|
||||
|
||||
// Parse any JSON value
|
||||
@ -992,6 +1015,7 @@ private:
|
||||
IterativeParsingState Transit(IterativeParsingState src, IterativeParsingToken token, IterativeParsingState dst, InputStream& is, Handler& handler) {
|
||||
int c = 0;
|
||||
IterativeParsingState n;
|
||||
bool hr;
|
||||
|
||||
switch (dst) {
|
||||
case IterativeParsingStartState:
|
||||
@ -1021,9 +1045,15 @@ private:
|
||||
*stack_.template Push<int>(1) = 0;
|
||||
// Call handler
|
||||
if (dst == IterativeParsingObjectInitialState)
|
||||
handler.StartObject();
|
||||
hr = handler.StartObject();
|
||||
else
|
||||
hr = handler.StartArray();
|
||||
// On handler short circuits the parsing.
|
||||
if (!hr) {
|
||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
|
||||
return IterativeParsingErrorState;
|
||||
}
|
||||
else
|
||||
handler.StartArray();
|
||||
return dst;
|
||||
|
||||
case IterativeParsingMemberKeyState:
|
||||
@ -1077,7 +1107,13 @@ private:
|
||||
if (n == IterativeParsingStartState)
|
||||
n = IterativeParsingFinishState;
|
||||
// Call handler
|
||||
handler.EndObject(c);
|
||||
hr = handler.EndObject(c);
|
||||
// On handler short circuits the parsing.
|
||||
if (!hr) {
|
||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
|
||||
return IterativeParsingErrorState;
|
||||
}
|
||||
else
|
||||
return n;
|
||||
|
||||
case IterativeParsingArrayFinishState:
|
||||
@ -1093,7 +1129,13 @@ private:
|
||||
if (n == IterativeParsingStartState)
|
||||
n = IterativeParsingFinishState;
|
||||
// Call handler
|
||||
handler.EndArray(c);
|
||||
hr = handler.EndArray(c);
|
||||
// On handler short circuits the parsing.
|
||||
if (!hr) {
|
||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
|
||||
return IterativeParsingErrorState;
|
||||
}
|
||||
else
|
||||
return n;
|
||||
|
||||
default:
|
||||
@ -1135,7 +1177,9 @@ private:
|
||||
}
|
||||
|
||||
template <unsigned parseFlags, typename InputStream, typename Handler>
|
||||
bool IterativeParse(InputStream& is, Handler& handler) {
|
||||
ParseResult IterativeParse(InputStream& is, Handler& handler) {
|
||||
parseResult_.Clear();
|
||||
ClearStackOnExit scope(*this);
|
||||
IterativeParsingState state = IterativeParsingStartState;
|
||||
|
||||
SkipWhitespace(is);
|
||||
@ -1157,14 +1201,12 @@ private:
|
||||
if (state != IterativeParsingFinishState)
|
||||
HandleError(state, is);
|
||||
|
||||
stack_.Clear();
|
||||
return state == IterativeParsingFinishState;
|
||||
return parseResult_;
|
||||
}
|
||||
|
||||
static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string.
|
||||
internal::Stack<Allocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing.
|
||||
ParseErrorCode parseErrorCode_;
|
||||
size_t errorOffset_;
|
||||
ParseResult parseResult_;
|
||||
}; // class GenericReader
|
||||
|
||||
//! Reader with UTF8 encoding and default allocator.
|
||||
|
@ -64,12 +64,12 @@ public:
|
||||
*/
|
||||
//@{
|
||||
|
||||
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; }
|
||||
Writer& Uint(unsigned u) { Prefix(kNumberType); WriteUint(u); return *this; }
|
||||
Writer& Int64(int64_t i64) { Prefix(kNumberType); WriteInt64(i64); return *this; }
|
||||
Writer& Uint64(uint64_t u64) { Prefix(kNumberType); WriteUint64(u64); return *this; }
|
||||
bool Null() { Prefix(kNullType); return WriteNull(); }
|
||||
bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); }
|
||||
bool Int(int i) { Prefix(kNumberType); return WriteInt(i); }
|
||||
bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); }
|
||||
bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); }
|
||||
bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); }
|
||||
|
||||
//! Writes the given \c double value to the stream
|
||||
/*!
|
||||
@ -80,51 +80,48 @@ public:
|
||||
writer.SetDoublePrecision(12).Double(M_PI);
|
||||
\endcode
|
||||
\param d The value to be written.
|
||||
\return The Writer itself for fluent API.
|
||||
\return Whether it is succeed.
|
||||
*/
|
||||
Writer& Double(double d) { Prefix(kNumberType); WriteDouble(d); return *this; }
|
||||
bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); }
|
||||
|
||||
Writer& String(const Ch* str, SizeType length, bool copy = false) {
|
||||
bool String(const Ch* str, SizeType length, bool copy = false) {
|
||||
(void)copy;
|
||||
Prefix(kStringType);
|
||||
WriteString(str, length);
|
||||
return *this;
|
||||
return WriteString(str, length);
|
||||
}
|
||||
|
||||
Writer& StartObject() {
|
||||
bool StartObject() {
|
||||
Prefix(kObjectType);
|
||||
new (level_stack_.template Push<Level>()) Level(false);
|
||||
WriteStartObject();
|
||||
return *this;
|
||||
return WriteStartObject();
|
||||
}
|
||||
|
||||
Writer& EndObject(SizeType memberCount = 0) {
|
||||
bool EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
WriteEndObject();
|
||||
bool ret = WriteEndObject();
|
||||
if (level_stack_.Empty()) // end of json text
|
||||
os_.Flush();
|
||||
return *this;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Writer& StartArray() {
|
||||
bool StartArray() {
|
||||
Prefix(kArrayType);
|
||||
new (level_stack_.template Push<Level>()) Level(true);
|
||||
WriteStartArray();
|
||||
return *this;
|
||||
return WriteStartArray();
|
||||
}
|
||||
|
||||
Writer& EndArray(SizeType elementCount = 0) {
|
||||
bool EndArray(SizeType elementCount = 0) {
|
||||
(void)elementCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
WriteEndArray();
|
||||
bool ret = WriteEndArray();
|
||||
if (level_stack_.Empty()) // end of json text
|
||||
os_.Flush();
|
||||
return *this;
|
||||
return ret;
|
||||
}
|
||||
//@}
|
||||
|
||||
@ -138,15 +135,18 @@ public:
|
||||
\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.
|
||||
\return Whether it is succeeded.
|
||||
*/
|
||||
Writer& Double(double d, int precision) {
|
||||
bool Double(double d, int precision) {
|
||||
int oldPrecision = GetDoublePrecision();
|
||||
return SetDoublePrecision(precision).Double(d).SetDoublePrecision(oldPrecision);
|
||||
SetDoublePrecision(precision);
|
||||
bool ret = Double(d);
|
||||
SetDoublePrecision(oldPrecision);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! Simpler but slower overload.
|
||||
Writer& String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
|
||||
//@}
|
||||
|
||||
@ -160,28 +160,29 @@ protected:
|
||||
|
||||
static const size_t kDefaultLevelDepth = 32;
|
||||
|
||||
void WriteNull() {
|
||||
os_.Put('n'); os_.Put('u'); os_.Put('l'); os_.Put('l');
|
||||
bool WriteNull() {
|
||||
os_.Put('n'); os_.Put('u'); os_.Put('l'); os_.Put('l'); return true;
|
||||
}
|
||||
|
||||
void WriteBool(bool b) {
|
||||
bool WriteBool(bool b) {
|
||||
if (b) {
|
||||
os_.Put('t'); os_.Put('r'); os_.Put('u'); os_.Put('e');
|
||||
}
|
||||
else {
|
||||
os_.Put('f'); os_.Put('a'); os_.Put('l'); os_.Put('s'); os_.Put('e');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteInt(int i) {
|
||||
bool WriteInt(int i) {
|
||||
if (i < 0) {
|
||||
os_.Put('-');
|
||||
i = -i;
|
||||
}
|
||||
WriteUint((unsigned)i);
|
||||
return WriteUint((unsigned)i);
|
||||
}
|
||||
|
||||
void WriteUint(unsigned u) {
|
||||
bool WriteUint(unsigned u) {
|
||||
char buffer[10];
|
||||
char *p = buffer;
|
||||
do {
|
||||
@ -193,17 +194,19 @@ protected:
|
||||
--p;
|
||||
os_.Put(*p);
|
||||
} while (p != buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteInt64(int64_t i64) {
|
||||
bool WriteInt64(int64_t i64) {
|
||||
if (i64 < 0) {
|
||||
os_.Put('-');
|
||||
i64 = -i64;
|
||||
}
|
||||
WriteUint64((uint64_t)i64);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteUint64(uint64_t u64) {
|
||||
bool WriteUint64(uint64_t u64) {
|
||||
char buffer[20];
|
||||
char *p = buffer;
|
||||
do {
|
||||
@ -215,6 +218,7 @@ protected:
|
||||
--p;
|
||||
os_.Put(*p);
|
||||
} while (p != buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@ -224,16 +228,17 @@ protected:
|
||||
#endif
|
||||
|
||||
//! \todo Optimization with custom double-to-string converter.
|
||||
void WriteDouble(double d) {
|
||||
bool WriteDouble(double d) {
|
||||
char buffer[100];
|
||||
int ret = RAPIDJSON_SNPRINTF(buffer, sizeof(buffer), "%.*g", doublePrecision_, d);
|
||||
RAPIDJSON_ASSERT(ret >= 1);
|
||||
for (int i = 0; i < ret; i++)
|
||||
os_.Put(buffer[i]);
|
||||
return true;
|
||||
}
|
||||
#undef RAPIDJSON_SNPRINTF
|
||||
|
||||
void WriteString(const Ch* str, SizeType length) {
|
||||
bool WriteString(const Ch* str, SizeType length) {
|
||||
static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
static const char escape[256] = {
|
||||
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
@ -266,12 +271,13 @@ protected:
|
||||
Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, os_);
|
||||
}
|
||||
os_.Put('\"');
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteStartObject() { os_.Put('{'); }
|
||||
void WriteEndObject() { os_.Put('}'); }
|
||||
void WriteStartArray() { os_.Put('['); }
|
||||
void WriteEndArray() { os_.Put(']'); }
|
||||
bool WriteStartObject() { os_.Put('{'); return true; }
|
||||
bool WriteEndObject() { os_.Put('}'); return true; }
|
||||
bool WriteStartArray() { os_.Put('['); return true; }
|
||||
bool WriteEndArray() { os_.Put(']'); return true; }
|
||||
|
||||
void Prefix(Type type) {
|
||||
(void)type;
|
||||
|
@ -8,7 +8,11 @@
|
||||
#define TEST_PLATFORM 0
|
||||
#define TEST_MISC 0
|
||||
|
||||
#if TEST_RAPIDJSON
|
||||
#define TEST_VERSION_CODE(x,y,z) \
|
||||
(((x)*100000) + ((y)*100) + (z))
|
||||
|
||||
// Only gcc >4.3 supports SSE4.2
|
||||
#if TEST_RAPIDJSON && !(defined(__GNUC__) && TEST_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) < TEST_VERSION_CODE(4,3,0))
|
||||
//#define RAPIDJSON_SSE2
|
||||
#define RAPIDJSON_SSE42
|
||||
#endif
|
||||
|
@ -197,8 +197,8 @@ RAPIDJSON_DIAG_OFF(effc++)
|
||||
struct ValueCounter : public BaseReaderHandler<> {
|
||||
ValueCounter() : count_(1) {} // root
|
||||
|
||||
void EndObject(SizeType memberCount) { count_ += memberCount * 2; }
|
||||
void EndArray(SizeType elementCount) { count_ += elementCount; }
|
||||
bool EndObject(SizeType memberCount) { count_ += memberCount * 2; return true; }
|
||||
bool EndArray(SizeType elementCount) { count_ += elementCount; return true; }
|
||||
|
||||
SizeType count_;
|
||||
};
|
||||
|
@ -13,10 +13,10 @@ RAPIDJSON_DIAG_OFF(effc++)
|
||||
template<bool expect>
|
||||
struct ParseBoolHandler : BaseReaderHandler<> {
|
||||
ParseBoolHandler() : step_(0) {}
|
||||
void Default() { FAIL(); }
|
||||
bool Default() { ADD_FAILURE(); return false; }
|
||||
// gcc 4.8.x generates warning in EXPECT_EQ(bool, bool) on this gtest version.
|
||||
// Workaround with EXPECT_TRUE().
|
||||
void Bool(bool b) { /*EXPECT_EQ(expect, b); */EXPECT_TRUE(expect == b); ++step_; }
|
||||
bool Bool(bool b) { /*EXPECT_EQ(expect, b); */EXPECT_TRUE(expect == b); ++step_; return true; }
|
||||
|
||||
unsigned step_;
|
||||
};
|
||||
@ -39,8 +39,8 @@ TEST(Reader, ParseFalse) {
|
||||
|
||||
struct ParseIntHandler : BaseReaderHandler<> {
|
||||
ParseIntHandler() : step_(0), actual_() {}
|
||||
void Default() { FAIL(); }
|
||||
void Int(int i) { actual_ = i; step_++; }
|
||||
bool Default() { ADD_FAILURE(); return false; }
|
||||
bool Int(int i) { actual_ = i; step_++; return true; }
|
||||
|
||||
unsigned step_;
|
||||
int actual_;
|
||||
@ -48,8 +48,8 @@ struct ParseIntHandler : BaseReaderHandler<> {
|
||||
|
||||
struct ParseUintHandler : BaseReaderHandler<> {
|
||||
ParseUintHandler() : step_(0), actual_() {}
|
||||
void Default() { FAIL(); }
|
||||
void Uint(unsigned i) { actual_ = i; step_++; }
|
||||
bool Default() { ADD_FAILURE(); return false; }
|
||||
bool Uint(unsigned i) { actual_ = i; step_++; return true; }
|
||||
|
||||
unsigned step_;
|
||||
unsigned actual_;
|
||||
@ -57,8 +57,8 @@ struct ParseUintHandler : BaseReaderHandler<> {
|
||||
|
||||
struct ParseInt64Handler : BaseReaderHandler<> {
|
||||
ParseInt64Handler() : step_(0), actual_() {}
|
||||
void Default() { FAIL(); }
|
||||
void Int64(int64_t i) { actual_ = i; step_++; }
|
||||
bool Default() { ADD_FAILURE(); return false; }
|
||||
bool Int64(int64_t i) { actual_ = i; step_++; return true; }
|
||||
|
||||
unsigned step_;
|
||||
int64_t actual_;
|
||||
@ -66,8 +66,8 @@ struct ParseInt64Handler : BaseReaderHandler<> {
|
||||
|
||||
struct ParseUint64Handler : BaseReaderHandler<> {
|
||||
ParseUint64Handler() : step_(0), actual_() {}
|
||||
void Default() { FAIL(); }
|
||||
void Uint64(uint64_t i) { actual_ = i; step_++; }
|
||||
bool Default() { ADD_FAILURE(); return false; }
|
||||
bool Uint64(uint64_t i) { actual_ = i; step_++; return true; }
|
||||
|
||||
unsigned step_;
|
||||
uint64_t actual_;
|
||||
@ -75,8 +75,8 @@ struct ParseUint64Handler : BaseReaderHandler<> {
|
||||
|
||||
struct ParseDoubleHandler : BaseReaderHandler<> {
|
||||
ParseDoubleHandler() : step_(0), actual_() {}
|
||||
void Default() { FAIL(); }
|
||||
void Double(double d) { actual_ = d; step_++; }
|
||||
bool Default() { ADD_FAILURE(); return false; }
|
||||
bool Double(double d) { actual_ = d; step_++; return true; }
|
||||
|
||||
unsigned step_;
|
||||
double actual_;
|
||||
@ -194,8 +194,8 @@ struct ParseStringHandler : BaseReaderHandler<Encoding> {
|
||||
ParseStringHandler(const ParseStringHandler&);
|
||||
ParseStringHandler& operator=(const ParseStringHandler&);
|
||||
|
||||
void Default() { FAIL(); }
|
||||
void String(const typename Encoding::Ch* str, size_t length, bool copy) {
|
||||
bool Default() { ADD_FAILURE(); return false; }
|
||||
bool String(const typename Encoding::Ch* str, size_t length, bool copy) {
|
||||
EXPECT_EQ(0, str_);
|
||||
if (copy) {
|
||||
str_ = (typename Encoding::Ch*)malloc((length + 1) * sizeof(typename Encoding::Ch));
|
||||
@ -205,6 +205,7 @@ struct ParseStringHandler : BaseReaderHandler<Encoding> {
|
||||
str_ = str;
|
||||
length_ = length;
|
||||
copy_ = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
const typename Encoding::Ch* str_;
|
||||
@ -411,10 +412,10 @@ template <unsigned count>
|
||||
struct ParseArrayHandler : BaseReaderHandler<> {
|
||||
ParseArrayHandler() : step_(0) {}
|
||||
|
||||
void Default() { FAIL(); }
|
||||
void Uint(unsigned i) { EXPECT_EQ(step_, i); step_++; }
|
||||
void StartArray() { EXPECT_EQ(0u, step_); step_++; }
|
||||
void EndArray(SizeType) { step_++; }
|
||||
bool Default() { ADD_FAILURE(); return false; }
|
||||
bool Uint(unsigned i) { EXPECT_EQ(step_, i); step_++; return true; }
|
||||
bool StartArray() { EXPECT_EQ(0u, step_); step_++; return true; }
|
||||
bool EndArray(SizeType) { step_++; return true; }
|
||||
|
||||
unsigned step_;
|
||||
};
|
||||
@ -462,42 +463,42 @@ TEST(Reader, ParseArray_Error) {
|
||||
struct ParseObjectHandler : BaseReaderHandler<> {
|
||||
ParseObjectHandler() : step_(0) {}
|
||||
|
||||
void Null() { EXPECT_EQ(8u, step_); step_++; }
|
||||
void Bool(bool b) {
|
||||
bool Null() { EXPECT_EQ(8u, step_); step_++; return true; }
|
||||
bool Bool(bool b) {
|
||||
switch(step_) {
|
||||
case 4: EXPECT_TRUE(b); step_++; break;
|
||||
case 6: EXPECT_FALSE(b); step_++; break;
|
||||
default: FAIL();
|
||||
case 4: EXPECT_TRUE(b); step_++; return true;
|
||||
case 6: EXPECT_FALSE(b); step_++; return true;
|
||||
default: ADD_FAILURE(); return false;
|
||||
}
|
||||
}
|
||||
void Int(int i) {
|
||||
bool Int(int i) {
|
||||
switch(step_) {
|
||||
case 10: EXPECT_EQ(123, i); step_++; break;
|
||||
case 15: EXPECT_EQ(1, i); step_++; break;
|
||||
case 16: EXPECT_EQ(2, i); step_++; break;
|
||||
case 17: EXPECT_EQ(3, i); step_++; break;
|
||||
default: FAIL();
|
||||
case 10: EXPECT_EQ(123, i); step_++; return true;
|
||||
case 15: EXPECT_EQ(1, i); step_++; return true;
|
||||
case 16: EXPECT_EQ(2, i); step_++; return true;
|
||||
case 17: EXPECT_EQ(3, i); step_++; return true;
|
||||
default: ADD_FAILURE(); return false;
|
||||
}
|
||||
}
|
||||
void Uint(unsigned i) { Int(i); }
|
||||
void Double(double d) { EXPECT_EQ(12u, step_); EXPECT_EQ(3.1416, d); step_++; }
|
||||
void String(const char* str, size_t, bool) {
|
||||
bool Uint(unsigned i) { return Int(i); }
|
||||
bool Double(double d) { EXPECT_EQ(12u, step_); EXPECT_EQ(3.1416, d); step_++; return true; }
|
||||
bool String(const char* str, size_t, bool) {
|
||||
switch(step_) {
|
||||
case 1: EXPECT_STREQ("hello", str); step_++; break;
|
||||
case 2: EXPECT_STREQ("world", str); step_++; break;
|
||||
case 3: EXPECT_STREQ("t", str); step_++; break;
|
||||
case 5: EXPECT_STREQ("f", str); step_++; break;
|
||||
case 7: EXPECT_STREQ("n", str); step_++; break;
|
||||
case 9: EXPECT_STREQ("i", str); step_++; break;
|
||||
case 11: EXPECT_STREQ("pi", str); step_++; break;
|
||||
case 13: EXPECT_STREQ("a", str); step_++; break;
|
||||
default: FAIL();
|
||||
case 1: EXPECT_STREQ("hello", str); step_++; return true;
|
||||
case 2: EXPECT_STREQ("world", str); step_++; return true;
|
||||
case 3: EXPECT_STREQ("t", str); step_++; return true;
|
||||
case 5: EXPECT_STREQ("f", str); step_++; return true;
|
||||
case 7: EXPECT_STREQ("n", str); step_++; return true;
|
||||
case 9: EXPECT_STREQ("i", str); step_++; return true;
|
||||
case 11: EXPECT_STREQ("pi", str); step_++; return true;
|
||||
case 13: EXPECT_STREQ("a", str); step_++; return true;
|
||||
default: ADD_FAILURE(); return false;
|
||||
}
|
||||
}
|
||||
void StartObject() { EXPECT_EQ(0u, step_); step_++; }
|
||||
void EndObject(SizeType memberCount) { EXPECT_EQ(19u, step_); EXPECT_EQ(7u, memberCount); step_++;}
|
||||
void StartArray() { EXPECT_EQ(14u, step_); step_++; }
|
||||
void EndArray(SizeType elementCount) { EXPECT_EQ(18u, step_); EXPECT_EQ(3u, elementCount); step_++;}
|
||||
bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
|
||||
bool EndObject(SizeType memberCount) { EXPECT_EQ(19u, step_); EXPECT_EQ(7u, memberCount); step_++; return true; }
|
||||
bool StartArray() { EXPECT_EQ(14u, step_); step_++; return true; }
|
||||
bool EndArray(SizeType elementCount) { EXPECT_EQ(18u, step_); EXPECT_EQ(3u, elementCount); step_++; return true; }
|
||||
|
||||
unsigned step_;
|
||||
};
|
||||
@ -529,9 +530,9 @@ TEST(Reader, ParseObject) {
|
||||
struct ParseEmptyObjectHandler : BaseReaderHandler<> {
|
||||
ParseEmptyObjectHandler() : step_(0) {}
|
||||
|
||||
void Default() { FAIL(); }
|
||||
void StartObject() { EXPECT_EQ(0u, step_); step_++; }
|
||||
void EndObject(SizeType) { EXPECT_EQ(1u, step_); step_++; }
|
||||
bool Default() { ADD_FAILURE(); return false; }
|
||||
bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
|
||||
bool EndObject(SizeType) { EXPECT_EQ(1u, step_); step_++; return true; }
|
||||
|
||||
unsigned step_;
|
||||
};
|
||||
@ -756,42 +757,42 @@ struct IterativeParsingReaderHandler {
|
||||
}
|
||||
|
||||
bool IsNullTriggered;
|
||||
void Null() { IsNullTriggered = true; }
|
||||
bool Null() { IsNullTriggered = true; return true; }
|
||||
|
||||
bool IsBoolTriggered;
|
||||
void Bool(bool) { IsBoolTriggered = true; }
|
||||
bool Bool(bool) { IsBoolTriggered = true; return true; }
|
||||
|
||||
bool IsIntTriggered;
|
||||
void Int(int) { IsIntTriggered = true; }
|
||||
bool Int(int) { IsIntTriggered = true; return true; }
|
||||
|
||||
bool IsUintTriggered;
|
||||
void Uint(unsigned) { IsUintTriggered = true; }
|
||||
bool Uint(unsigned) { IsUintTriggered = true; return true; }
|
||||
|
||||
bool IsInt64Triggered;
|
||||
void Int64(int64_t) { IsInt64Triggered = true; }
|
||||
bool Int64(int64_t) { IsInt64Triggered = true; return true; }
|
||||
|
||||
bool IsUint64Triggered;
|
||||
void Uint64(uint64_t) { IsUint64Triggered = true; }
|
||||
bool Uint64(uint64_t) { IsUint64Triggered = true; return true; }
|
||||
|
||||
bool IsDoubleTriggered;
|
||||
void Double(double) { IsDoubleTriggered = true; }
|
||||
bool Double(double) { IsDoubleTriggered = true; return true; }
|
||||
|
||||
bool IsStringTriggered;
|
||||
void String(const Ch*, SizeType, bool) { IsStringTriggered = true; }
|
||||
bool String(const Ch*, SizeType, bool) { IsStringTriggered = true; return true; }
|
||||
|
||||
bool IsStartObjectTriggered;
|
||||
void StartObject() { IsStartObjectTriggered = true; }
|
||||
bool StartObject() { IsStartObjectTriggered = true; return true; }
|
||||
|
||||
bool IsEndObjectTriggered;
|
||||
SizeType MemberCount;
|
||||
void EndObject(SizeType c) { IsEndObjectTriggered = true; MemberCount = c; }
|
||||
bool EndObject(SizeType c) { IsEndObjectTriggered = true; MemberCount = c; return true; }
|
||||
|
||||
bool IsStartArrayTriggered;
|
||||
void StartArray() { IsStartArrayTriggered = true; }
|
||||
bool StartArray() { IsStartArrayTriggered = true; return true; }
|
||||
|
||||
bool IsEndArrayTriggered;
|
||||
SizeType ElementCount;
|
||||
void EndArray(SizeType c) { IsEndArrayTriggered = true; ElementCount = c; }
|
||||
bool EndArray(SizeType c) { IsEndArrayTriggered = true; ElementCount = c; return true; }
|
||||
};
|
||||
|
||||
#define ITERATIVE_PARSING_PREPARE_STATE_UNTIL(text, pos)\
|
||||
|
@ -83,13 +83,13 @@ TEST(Writer,DoublePrecision) {
|
||||
buffer.Clear();
|
||||
}
|
||||
{ // explicit individual double precisions
|
||||
writer.SetDoublePrecision(2)
|
||||
.StartArray()
|
||||
.Double(1.2345,5)
|
||||
.Double(1.2345678,9)
|
||||
.Double(0.123456789012,12)
|
||||
.Double(1234567.8,8)
|
||||
.EndArray();
|
||||
writer.SetDoublePrecision(2);
|
||||
writer.StartArray();
|
||||
writer.Double(1.2345, 5);
|
||||
writer.Double(1.2345678, 9);
|
||||
writer.Double(0.123456789012, 12);
|
||||
writer.Double(1234567.8, 8);
|
||||
writer.EndArray();
|
||||
|
||||
EXPECT_EQ(writer.GetDoublePrecision(), 2);
|
||||
EXPECT_STREQ(json, buffer.GetString());
|
||||
|
Loading…
x
Reference in New Issue
Block a user