Unverified Commit b1e556d7 authored by Milo Yip's avatar Milo Yip Committed by GitHub

Merge branch 'master' into violationDetails

parents 8c182e51 03f5de9d
......@@ -23,3 +23,6 @@ Doxyfile
Doxyfile.zh-cn
DartConfiguration.tcl
*.nupkg
# Files created by OS
*.DS_Store
......@@ -140,7 +140,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
* Redo all documentation (English, Simplified Chinese)
### Changed
* Copyright ownership transfered to THL A29 Limited (a Tencent company).
* Copyright ownership transferred to THL A29 Limited (a Tencent company).
* Migrating from Premake to CMAKE (#192)
* Resolve all warning reports
......
......@@ -35,6 +35,8 @@ option(RAPIDJSON_BUILD_CXX11 "Build rapidjson with C++11 (gcc/clang)" ON)
option(RAPIDJSON_BUILD_ASAN "Build rapidjson with address sanitizer (gcc/clang)" OFF)
option(RAPIDJSON_BUILD_UBSAN "Build rapidjson with undefined behavior sanitizer (gcc/clang)" OFF)
option(RAPIDJSON_ENABLE_INSTRUMENTATION_OPT "Build rapidjson with -march or -mcpu options" ON)
option(RAPIDJSON_HAS_STDSTRING "" OFF)
if(RAPIDJSON_HAS_STDSTRING)
add_definitions(-DRAPIDJSON_HAS_STDSTRING)
......@@ -50,11 +52,13 @@ if(CCACHE_FOUND)
endif(CCACHE_FOUND)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "powerpc" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64le")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native")
else()
#FIXME: x86 is -march=native, but doesn't mean every arch is this option. To keep original project's compatibility, I leave this except POWER.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
if(${RAPIDJSON_ENABLE_INSTRUMENTATION_OPT})
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "powerpc" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64le")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native")
else()
#FIXME: x86 is -march=native, but doesn't mean every arch is this option. To keep original project's compatibility, I leave this except POWER.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
endif()
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror")
set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wsign-conversion)
......@@ -181,6 +185,8 @@ EXPORT( PACKAGE ${PROJECT_NAME} )
# ... for the build tree
SET( CONFIG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
SET( CONFIG_DIR ${CMAKE_CURRENT_BINARY_DIR})
SET( ${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_SOURCE_DIR}/include" )
CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY )
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in
......
......@@ -128,7 +128,7 @@ And the `InputStream` is type of input stream.
## Parse Error {#ParseError}
When the parse processing succeeded, the `Document` contains the parse results. When there is an error, the original DOM is *unchanged*. And the error state of parsing can be obtained by `bool HasParseError()`, `ParseErrorCode GetParseError()` and `size_t GetParseOffset()`.
When the parse processing succeeded, the `Document` contains the parse results. When there is an error, the original DOM is *unchanged*. And the error state of parsing can be obtained by `bool HasParseError()`, `ParseErrorCode GetParseError()` and `size_t GetErrorOffset()`.
Parse Error Code | Description
--------------------------------------------|---------------------------------------------------
......@@ -241,7 +241,7 @@ Some techniques about using DOM API is discussed here.
## DOM as SAX Event Publisher
In RapidJSON, stringifying a DOM with `Writer` may be look a little bit weired.
In RapidJSON, stringifying a DOM with `Writer` may be look a little bit weird.
~~~~~~~~~~cpp
// ...
......
......@@ -128,7 +128,7 @@ GenericDocument& GenericDocument::Parse(const Ch* str);
## 解析错误 {#ParseError}
当解析过程顺利完成,`Document` 便会含有解析结果。当过程出现错误,原来的 DOM 会*维持不变*。可使用 `bool HasParseError()``ParseErrorCode GetParseError()``size_t GetParseOffset()` 获取解析的错误状态。
当解析过程顺利完成,`Document` 便会含有解析结果。当过程出现错误,原来的 DOM 会*维持不变*。可使用 `bool HasParseError()``ParseErrorCode GetParseError()``size_t GetErrorOffset()` 获取解析的错误状态。
解析错误代号 | 描述
--------------------------------------------|---------------------------------------------------
......
......@@ -10,7 +10,7 @@ The earlier [RFC4627](http://www.ietf.org/rfc/rfc4627.txt) stated that,
> (in §6) JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON is written in UTF-8, JSON is 8bit compatible. When JSON is written in UTF-16 or UTF-32, the binary content-transfer-encoding must be used.
RapidJSON supports various encodings. It can also validate the encodings of JSON, and transconding JSON among encodings. All these features are implemented internally, without the need for external libraries (e.g. [ICU](http://site.icu-project.org/)).
RapidJSON supports various encodings. It can also validate the encodings of JSON, and transcoding JSON among encodings. All these features are implemented internally, without the need for external libraries (e.g. [ICU](http://site.icu-project.org/)).
[TOC]
......
......@@ -116,7 +116,7 @@
~~~~~~~~~~cpp
Value(kObjectType).Swap(d);
~~~~~~~~~~
or equivalent, but sightly longer to type:
or equivalent, but slightly longer to type:
~~~~~~~~~~cpp
d.Swap(Value(kObjectType).Move());
~~~~~~~~~~
......@@ -140,11 +140,11 @@
}
~~~~~~~~~~
The most important requirement to take care of document and value life-cycle as well as consistent memory managent using the right allocator during the value transfer.
The most important requirement to take care of document and value life-cycle as well as consistent memory management using the right allocator during the value transfer.
Simple yet most efficient way to achieve that is to modify the `address` definition above to initialize it with allocator of the `person` document, then we just add the root member of the value:
~~~~~~~~~~cpp
Documnet address(person.GetAllocator());
Document address(person.GetAllocator());
...
person["person"].AddMember("address", address["address"], person.GetAllocator());
~~~~~~~~~~
......@@ -174,7 +174,7 @@ Alternatively, if we don't want to explicitly refer to the root value of `addres
3. Why do I need to provide the length of string?
Since C string is null-terminated, the length of string needs to be computed via `strlen()`, with linear runtime complexity. This incurs an unncessary overhead of many operations, if the user already knows the length of string.
Since C string is null-terminated, the length of string needs to be computed via `strlen()`, with linear runtime complexity. This incurs an unnecessary overhead of many operations, if the user already knows the length of string.
Also, RapidJSON can handle `\u0000` (null character) within a string. If a string contains null characters, `strlen()` cannot return the true length of it. In such case user must provide the length of string explicitly.
......@@ -204,7 +204,7 @@ Alternatively, if we don't want to explicitly refer to the root value of `addres
2. Can it validate the encoding?
Yes, just pass `kParseValidateEncodingFlag` to `Parse()`. If there is invalid encoding in the stream, it wil generate `kParseErrorStringInvalidEncoding` error.
Yes, just pass `kParseValidateEncodingFlag` to `Parse()`. If there is invalid encoding in the stream, it will generate `kParseErrorStringInvalidEncoding` error.
3. What is surrogate pair? Does RapidJSON support it?
......@@ -248,7 +248,7 @@ Alternatively, if we don't want to explicitly refer to the root value of `addres
1. Is RapidJSON really fast?
Yes. It may be the fastest open source JSON library. There is a [benchmark](https://github.com/miloyip/nativejson-benchmark) for evaluating performance of C/C++ JSON libaries.
Yes. It may be the fastest open source JSON library. There is a [benchmark](https://github.com/miloyip/nativejson-benchmark) for evaluating performance of C/C++ JSON libraries.
2. Why is it fast?
......@@ -262,13 +262,13 @@ Alternatively, if we don't want to explicitly refer to the root value of `addres
The design of RapidJSON aims at reducing memory footprint.
In the SAX API, `Reader` consumes memory portional to maximum depth of JSON tree, plus maximum length of JSON string.
In the SAX API, `Reader` consumes memory proportional to maximum depth of JSON tree, plus maximum length of JSON string.
In the DOM API, each `Value` consumes exactly 16/24 bytes for 32/64-bit architecture respectively. RapidJSON also uses a special memory allocator to minimize overhead of allocations.
5. What is the purpose of being high performance?
Some applications need to process very large JSON files. Some server-side applications need to process huge amount of JSONs. Being high performance can improve both latency and throuput. In a broad sense, it will also save energy.
Some applications need to process very large JSON files. Some server-side applications need to process huge amount of JSONs. Being high performance can improve both latency and throughput. In a broad sense, it will also save energy.
## Gossip
......
# Performance
There is a [native JSON benchmark collection] [1] which evaluates speed, memory usage and code size of various operations among 37 JSON libaries.
There is a [native JSON benchmark collection] [1] which evaluates speed, memory usage and code size of various operations among 37 JSON libraries.
[1]: https://github.com/miloyip/nativejson-benchmark
......
......@@ -211,7 +211,7 @@ p.Stringify(sb);
std::cout << sb.GetString() << std::endl;
~~~
It can also stringify to URI fragment reprsentation by `StringifyUriFragment()`.
It can also stringify to URI fragment representation by `StringifyUriFragment()`.
# User-Supplied Tokens {#UserSuppliedTokens}
......
......@@ -126,7 +126,7 @@ When the `Reader` encounters a JSON number, it chooses a suitable C++ type mappi
When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `Key()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeat until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler; users who do not need this parameter may ignore it.
Arrays are similar to objects, 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.
Arrays are similar to objects, but simpler. At the beginning of an array, the `Reader` calls `BeginArray()`. 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 function returns a `bool`. Normally it should return `true`. If the handler encounters an error, it can return `false` to notify the event publisher to stop further processing.
......
......@@ -49,7 +49,7 @@ if (!d.Accept(validator)) {
Some notes:
* One `SchemaDocment` can be referenced by multiple `SchemaValidator`s. It will not be modified by `SchemaValidator`s.
* One `SchemaDocument` can be referenced by multiple `SchemaValidator`s. It will not be modified by `SchemaValidator`s.
* A `SchemaValidator` may be reused to validate multiple documents. To run it for other documents, call `validator.Reset()` first.
# Validation during parsing/serialization {#Fused}
......
......@@ -8,7 +8,7 @@ RapidJSON 实现了一个 [JSON Schema Draft v4](http://json-schema.org/document
[TOC]
## 基本用法
# 基本用法 {#BasicUsage}
首先,你要把 JSON Schema 解析成 `Document`,再把它编译成一个 `SchemaDocument`
......@@ -52,11 +52,11 @@ if (!d.Accept(validator)) {
* 一个 `SchemaDocment` 能被多个 `SchemaValidator` 引用。它不会被 `SchemaValidator` 修改。
* 可以重复使用一个 `SchemaValidator` 来校验多个文件。在校验其他文件前,须先调用 `validator.Reset()`
## 在解析/生成时进行校验
# 在解析/生成时进行校验 {#ParsingSerialization}
与大部分 JSON Schema 校验器有所不同,RapidJSON 提供了一个基于 SAX 的 schema 校验器实现。因此,你可以在输入流解析 JSON 的同时进行校验。若校验器遇到一个与 schema 不符的值,就会立即终止解析。这设计对于解析大型 JSON 文件时特别有用。
### DOM 解析
## DOM 解析 {#DomParsing}
在使用 DOM 进行解析时,`Document` 除了接收 SAX 事件外,还需做一些准备及结束工作,因此,为了连接 `Reader``SchemaValidator``Document` 要做多一点事情。`SchemaValidatingReader` 是一个辅助类去做那些工作。
......@@ -97,7 +97,7 @@ if (!reader.GetParseResult()) {
}
~~~
### SAX 解析
## SAX 解析 {#SaxParsing}
使用 SAX 解析时,情况就简单得多。若只需要校验 JSON 而无需进一步处理,那么仅需要:
......@@ -126,7 +126,7 @@ if (!reader.Parse(ss, validator)) {
}
~~~
### 生成
## 生成 {#Serialization}
我们也可以在生成(serialization)的时候进行校验。这能确保输出的 JSON 符合一个 JSON Schema。
......@@ -144,7 +144,7 @@ if (!d.Accept(validator)) {
当然,如果你的应用仅需要 SAX 风格的生成,那么只需要把 SAX 事件由原来发送到 `Writer`,改为发送到 `SchemaValidator`
## 远程 Schema
# 远程 Schema {#RemoteSchema}
JSON Schema 支持 [`$ref` 关键字](http://spacetelescope.github.io/understanding-json-schema/structuring.html),它是一个 [JSON pointer](doc/pointer.zh-cn.md) 引用至一个本地(local)或远程(remote) schema。本地指针的首字符是 `#`,而远程指针是一个相对或绝对 URI。例如:
......@@ -168,7 +168,7 @@ MyRemoteSchemaDocumentProvider provider;
SchemaDocument schema(sd, &provider);
~~~
## 标准的符合程度
# 标准的符合程度 {#Conformance}
RapidJSON 通过了 [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) (Json Schema draft 4) 中 263 个测试的 262 个。
......@@ -176,7 +176,7 @@ RapidJSON 通过了 [JSON Schema Test Suite](https://github.com/json-schema/JSON
除此以外,关于字符串类型的 `format` schema 关键字也会被忽略,因为标准中并没需求必须实现。
### 正则表达式
## 正则表达式 {#RegEx}
`pattern``patternProperties` 这两个 schema 关键字使用了正则表达式去匹配所需的模式。
......@@ -211,7 +211,7 @@ RapidJSON 实现了一个简单的 NFA 正则表达式引擎,并预设使用
对于使用 C++11 编译器的使用者,也可使用 `std::regex`,只需定义 `RAPIDJSON_SCHEMA_USE_INTERNALREGEX=0``RAPIDJSON_SCHEMA_USE_STDREGEX=1`。若你的 schema 无需使用 `pattern``patternProperties`,可以把两个宏都设为零,以禁用此功能,这样做可节省一些代码体积。
## 性能
# 性能 {#Performance}
大部分 C++ JSON 库都未支持 JSON Schema。因此我们尝试按照 [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark) 去评估 RapidJSON 的 JSON Schema 校验器。该评测测试了 11 个运行在 node.js 上的 JavaScript 库。
......
......@@ -68,7 +68,7 @@ printf("t = %s\n", document["t"].GetBool() ? "true" : "false");
t = true
~~~~~~~~~~
JSON null can be queryed with `IsNull()`.
JSON null can be queried with `IsNull()`.
~~~~~~~~~~cpp
printf("n = %s\n", document["n"].IsNull() ? "null" : "?");
~~~~~~~~~~
......
......@@ -32,6 +32,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
endif()
add_executable(archivertest archiver/archiver.cpp archiver/archivertest.cpp)
foreach (example ${EXAMPLES})
add_executable(${example} ${example}/${example}.cpp)
endforeach()
......
#include "archiver.h"
#include <cassert>
#include <stack>
#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;
struct JsonReaderStackItem {
enum State {
BeforeStart, //!< An object/array is in the stack but it is not yet called by StartObject()/StartArray().
Started, //!< An object/array is called by StartObject()/StartArray().
Closed //!< An array is closed after read all element, but before EndArray().
};
JsonReaderStackItem(const Value* value, State state) : value(value), state(state), index() {}
const Value* value;
State state;
SizeType index; // For array iteration
};
typedef std::stack<JsonReaderStackItem> JsonReaderStack;
#define DOCUMENT reinterpret_cast<Document*>(mDocument)
#define STACK (reinterpret_cast<JsonReaderStack*>(mStack))
#define TOP (STACK->top())
#define CURRENT (*TOP.value)
JsonReader::JsonReader(const char* json) : mDocument(), mStack(), mError(false) {
mDocument = new Document;
DOCUMENT->Parse(json);
if (DOCUMENT->HasParseError())
mError = true;
else {
mStack = new JsonReaderStack;
STACK->push(JsonReaderStackItem(DOCUMENT, JsonReaderStackItem::BeforeStart));
}
}
JsonReader::~JsonReader() {
delete DOCUMENT;
delete STACK;
}
// Archive concept
JsonReader& JsonReader::StartObject() {
if (!mError) {
if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::BeforeStart)
TOP.state = JsonReaderStackItem::Started;
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::EndObject() {
if (!mError) {
if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started)
Next();
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::Member(const char* name) {
if (!mError) {
if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started) {
Value::ConstMemberIterator memberItr = CURRENT.FindMember(name);
if (memberItr != CURRENT.MemberEnd())
STACK->push(JsonReaderStackItem(&memberItr->value, JsonReaderStackItem::BeforeStart));
else
mError = true;
}
else
mError = true;
}
return *this;
}
bool JsonReader::HasMember(const char* name) const {
if (!mError && CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started)
return CURRENT.HasMember(name);
return false;
}
JsonReader& JsonReader::StartArray(size_t* size) {
if (!mError) {
if (CURRENT.IsArray() && TOP.state == JsonReaderStackItem::BeforeStart) {
TOP.state = JsonReaderStackItem::Started;
if (size)
*size = CURRENT.Size();
if (!CURRENT.Empty()) {
const Value* value = &CURRENT[TOP.index];
STACK->push(JsonReaderStackItem(value, JsonReaderStackItem::BeforeStart));
}
else
TOP.state = JsonReaderStackItem::Closed;
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::EndArray() {
if (!mError) {
if (CURRENT.IsArray() && TOP.state == JsonReaderStackItem::Closed)
Next();
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::operator&(bool& b) {
if (!mError) {
if (CURRENT.IsBool()) {
b = CURRENT.GetBool();
Next();
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::operator&(unsigned& u) {
if (!mError) {
if (CURRENT.IsUint()) {
u = CURRENT.GetUint();
Next();
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::operator&(int& i) {
if (!mError) {
if (CURRENT.IsInt()) {
i = CURRENT.GetInt();
Next();
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::operator&(double& d) {
if (!mError) {
if (CURRENT.IsNumber()) {
d = CURRENT.GetDouble();
Next();
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::operator&(std::string& s) {
if (!mError) {
if (CURRENT.IsString()) {
s = CURRENT.GetString();
Next();
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::SetNull() {
// This function is for JsonWriter only.
mError = true;
return *this;
}
void JsonReader::Next() {
if (!mError) {
assert(!STACK->empty());
STACK->pop();
if (!STACK->empty() && CURRENT.IsArray()) {
if (TOP.state == JsonReaderStackItem::Started) { // Otherwise means reading array item pass end
if (TOP.index < CURRENT.Size() - 1) {
const Value* value = &CURRENT[++TOP.index];
STACK->push(JsonReaderStackItem(value, JsonReaderStackItem::BeforeStart));
}
else
TOP.state = JsonReaderStackItem::Closed;
}
else
mError = true;
}
}
}
#undef DOCUMENT
#undef STACK
#undef TOP
#undef CURRENT
////////////////////////////////////////////////////////////////////////////////
// JsonWriter
#define WRITER reinterpret_cast<PrettyWriter<StringBuffer>*>(mWriter)
#define STREAM reinterpret_cast<StringBuffer*>(mStream)
JsonWriter::JsonWriter() : mWriter(), mStream() {
mStream = new StringBuffer;
mWriter = new PrettyWriter<StringBuffer>(*STREAM);
}
JsonWriter::~JsonWriter() {
delete WRITER;
delete STREAM;
}
const char* JsonWriter::GetString() const {
return STREAM->GetString();
}
JsonWriter& JsonWriter::StartObject() {
WRITER->StartObject();
return *this;
}
JsonWriter& JsonWriter::EndObject() {
WRITER->EndObject();
return *this;
}
JsonWriter& JsonWriter::Member(const char* name) {
WRITER->String(name, static_cast<SizeType>(strlen(name)));
return *this;
}
bool JsonWriter::HasMember(const char*) const {
// This function is for JsonReader only.
assert(false);
return false;
}
JsonWriter& JsonWriter::StartArray(size_t*) {
WRITER->StartArray();
return *this;
}
JsonWriter& JsonWriter::EndArray() {
WRITER->EndArray();
return *this;
}
JsonWriter& JsonWriter::operator&(bool& b) {
WRITER->Bool(b);
return *this;
}
JsonWriter& JsonWriter::operator&(unsigned& u) {
WRITER->Uint(u);
return *this;
}
JsonWriter& JsonWriter::operator&(int& i) {
WRITER->Int(i);
return *this;
}
JsonWriter& JsonWriter::operator&(double& d) {
WRITER->Double(d);
return *this;
}
JsonWriter& JsonWriter::operator&(std::string& s) {
WRITER->String(s.c_str(), static_cast<SizeType>(s.size()));
return *this;
}
JsonWriter& JsonWriter::SetNull() {
WRITER->Null();
return *this;
}
#undef STREAM
#undef WRITER
#ifndef ARCHIVER_H_
#define ARCHIVER_H_
#include <cstddef>
#include <string>
/**
\class Archiver
\brief Archiver concept
Archiver can be a reader or writer for serialization or deserialization respectively.
class Archiver {
public:
/// \returns true if the archiver is in normal state. false if it has errors.
operator bool() const;
/// Starts an object
Archiver& StartObject();
/// After calling StartObject(), assign a member with a name
Archiver& Member(const char* name);
/// After calling StartObject(), check if a member presents
bool HasMember(const char* name) const;
/// Ends an object
Archiver& EndObject();
/// Starts an array
/// \param size If Archiver::IsReader is true, the size of array is written.
Archiver& StartArray(size_t* size = 0);
/// Ends an array
Archiver& EndArray();
/// Read/Write primitive types.
Archiver& operator&(bool& b);
Archiver& operator&(unsigned& u);
Archiver& operator&(int& i);
Archiver& operator&(double& d);
Archiver& operator&(std::string& s);
/// Write primitive types.
Archiver& SetNull();
//! Whether it is a reader.
static const bool IsReader;
//! Whether it is a writer.
static const bool IsWriter;
};
*/
/// Represents a JSON reader which implements Archiver concept.
class JsonReader {
public:
/// Constructor.
/**
\param json A non-const source json string for in-situ parsing.
\note in-situ means the source JSON string will be modified after parsing.
*/
JsonReader(const char* json);
/// Destructor.
~JsonReader();
// Archive concept
operator bool() const { return !mError; }
JsonReader& StartObject();
JsonReader& Member(const char* name);
bool HasMember(const char* name) const;
JsonReader& EndObject();
JsonReader& StartArray(size_t* size = nullptr);
JsonReader& EndArray();
JsonReader& operator&(bool& b);
JsonReader& operator&(unsigned& u);
JsonReader& operator&(int& i);
JsonReader& operator&(double& d);
JsonReader& operator&(std::string& s);
JsonReader& SetNull();
static const bool IsReader = true;
static const bool IsWriter = !IsReader;
private:
void Next();
// PIMPL
void* mDocument; ///< DOM result of parsing.
void* mStack; ///< Stack for iterating the DOM
bool mError; ///< Whether an error is occured.
};
class JsonWriter {
public:
/// Constructor.
JsonWriter();
/// Destructor.
~JsonWriter();
/// Obtains the serialized JSON string.
const char* GetString() const;
// Archive concept
operator bool() const { return true; }
JsonWriter& StartObject();
JsonWriter& Member(const char* name);
bool HasMember(const char* name) const;
JsonWriter& EndObject();
JsonWriter& StartArray(size_t* size = 0);
JsonWriter& EndArray();
JsonWriter& operator&(bool& b);
JsonWriter& operator&(unsigned& u);
JsonWriter& operator&(int& i);
JsonWriter& operator&(double& d);
JsonWriter& operator&(std::string& s);
JsonWriter& SetNull();
static const bool IsReader = false;
static const bool IsWriter = !IsReader;
private:
// PIMPL idiom
void* mWriter; ///< JSON writer.
void* mStream; ///< Stream buffer.
};
#endif // ARCHIVER_H__
#include "archiver.h"
#include <iostream>
#include <vector>
//////////////////////////////////////////////////////////////////////////////
// Test1: simple object
struct Student {
std::string name;
unsigned age;
double height;
bool canSwim;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Student& s) {
ar.StartObject();
ar.Member("name") & s.name;
ar.Member("age") & s.age;
ar.Member("height") & s.height;
ar.Member("canSwim") & s.canSwim;
return ar.EndObject();
}
std::ostream& operator<<(std::ostream& os, const Student& s) {
return os << s.name << " " << s.age << " " << s.height << " " << s.canSwim;
}
void test1() {
std::string json;
// Serialize
{
Student s = { "Lua", 9, 150.5, true };
JsonWriter writer;
writer & s;
json = writer.GetString();
std::cout << json << std::endl;
}
// Deserialize
{
Student s;
JsonReader reader(json.c_str());
reader & s;
std::cout << s << std::endl;
}
}
//////////////////////////////////////////////////////////////////////////////
// Test2: std::vector <=> JSON array
//
// You can map a JSON array to other data structures as well
struct Group {
std::string groupName;
std::vector<Student> students;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Group& g) {
ar.StartObject();
ar.Member("groupName");
ar & g.groupName;
ar.Member("students");
size_t studentCount = g.students.size();
ar.StartArray(&studentCount);
if (ar.IsReader)
g.students.resize(studentCount);
for (size_t i = 0; i < studentCount; i++)
ar & g.students[i];
ar.EndArray();
return ar.EndObject();
}
std::ostream& operator<<(std::ostream& os, const Group& g) {
os << g.groupName << std::endl;
for (std::vector<Student>::const_iterator itr = g.students.begin(); itr != g.students.end(); ++itr)
os << *itr << std::endl;
return os;
}
void test2() {
std::string json;
// Serialize
{
Group g;
g.groupName = "Rainbow";
Student s1 = { "Lua", 9, 150.5, true };
Student s2 = { "Mio", 7, 120.0, false };
g.students.push_back(s1);
g.students.push_back(s2);
JsonWriter writer;
writer & g;
json = writer.GetString();
std::cout << json << std::endl;
}
// Deserialize
{
Group g;
JsonReader reader(json.c_str());
reader & g;
std::cout << g << std::endl;
}
}
//////////////////////////////////////////////////////////////////////////////
// Test3: polymorphism & friend
//
// Note that friendship is not necessary but make things simpler.
class Shape {
public:
virtual ~Shape() {}
virtual const char* GetType() const = 0;
virtual void Print(std::ostream& os) const = 0;
protected:
Shape() {}
Shape(double x, double y) : x_(x), y_(y) {}
template <typename Archiver>
friend Archiver& operator&(Archiver& ar, Shape& s);
double x_, y_;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Shape& s) {
ar.Member("x") & s.x_;
ar.Member("y") & s.y_;
return ar;
}
class Circle : public Shape {
public:
Circle() {}
Circle(double x, double y, double radius) : Shape(x, y), radius_(radius) {}
~Circle() {}
const char* GetType() const { return "Circle"; }
void Print(std::ostream& os) const {
os << "Circle (" << x_ << ", " << y_ << ")" << " radius = " << radius_;
}
private:
template <typename Archiver>
friend Archiver& operator&(Archiver& ar, Circle& c);
double radius_;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Circle& c) {
ar & static_cast<Shape&>(c);
ar.Member("radius") & c.radius_;
return ar;
}
class Box : public Shape {
public:
Box() {}
Box(double x, double y, double width, double height) : Shape(x, y), width_(width), height_(height) {}
~Box() {}
const char* GetType() const { return "Box"; }
void Print(std::ostream& os) const {
os << "Box (" << x_ << ", " << y_ << ")" << " width = " << width_ << " height = " << height_;
}
private:
template <typename Archiver>
friend Archiver& operator&(Archiver& ar, Box& b);
double width_, height_;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Box& b) {
ar & static_cast<Shape&>(b);
ar.Member("width") & b.width_;
ar.Member("height") & b.height_;
return ar;
}
class Canvas {
public:
Canvas() {}
~Canvas() { Clear(); }
void Clear() {
for (std::vector<Shape*>::iterator itr = shapes_.begin(); itr != shapes_.end(); ++itr)
delete *itr;
}
void AddShape(Shape* shape) { shapes_.push_back(shape); }
void Print(std::ostream& os) {
for (std::vector<Shape*>::iterator itr = shapes_.begin(); itr != shapes_.end(); ++itr) {
(*itr)->Print(os);
std::cout << std::endl;
}
}
private:
template <typename Archiver>
friend Archiver& operator&(Archiver& ar, Canvas& c);
std::vector<Shape*> shapes_;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Shape*& shape) {
std::string type = ar.IsReader ? "" : shape->GetType();
ar.StartObject();
ar.Member("type") & type;
if (type == "Circle") {
if (ar.IsReader) shape = new Circle;
ar & static_cast<Circle&>(*shape);
}
else if (type == "Box") {
if (ar.IsReader) shape = new Box;
ar & static_cast<Box&>(*shape);
}
return ar.EndObject();
}
template <typename Archiver>
Archiver& operator&(Archiver& ar, Canvas& c) {
size_t shapeCount = c.shapes_.size();
ar.StartArray(&shapeCount);
if (ar.IsReader) {
c.Clear();
c.shapes_.resize(shapeCount);
}
for (size_t i = 0; i < shapeCount; i++)
ar & c.shapes_[i];
return ar.EndArray();
}
void test3() {
std::string json;
// Serialize
{
Canvas c;
c.AddShape(new Circle(1.0, 2.0, 3.0));
c.AddShape(new Box(4.0, 5.0, 6.0, 7.0));
JsonWriter writer;
writer & c;
json = writer.GetString();
std::cout << json << std::endl;
}
// Deserialize
{
Canvas c;
JsonReader reader(json.c_str());
reader & c;
c.Print(std::cout);
}
}
//////////////////////////////////////////////////////////////////////////////
int main() {
test1();
test2();
test3();
}
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_
#define RAPIDJSON_CURSORSTREAMWRAPPER_H_
#include "stream.h"
#if defined(__GNUC__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#if defined(_MSC_VER) && _MSC_VER <= 1800
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4702) // unreachable code
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Cursor stream wrapper for counting line and column number if error exists.
/*!
\tparam InputStream Any stream that implements Stream Concept
*/
template <typename InputStream, typename Encoding = UTF8<> >
class CursorStreamWrapper : public GenericStreamWrapper<InputStream, Encoding> {
public:
typedef typename Encoding::Ch Ch;
CursorStreamWrapper(InputStream& is):
GenericStreamWrapper<InputStream, Encoding>(is), line_(1), col_(0) {}
// counting line and column number
Ch Take() {
Ch ch = this->is_.Take();
if(ch == '\n') {
line_ ++;
col_ = 0;
} else {
col_ ++;
}
return ch;
}
//! Get the error line number, if error exists.
size_t GetLine() const { return line_; }
//! Get the error column number, if error exists.
size_t GetColumn() const { return col_; }
private:
size_t line_; //!< Current Line
size_t col_; //!< Current Column
};
#if defined(_MSC_VER) && _MSC_VER <= 1800
RAPIDJSON_DIAG_POP
#endif
#if defined(__GNUC__)
RAPIDJSON_DIAG_POP
#endif
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_
......@@ -45,7 +45,7 @@ RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_N
#endif // __GNUC__
#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
#include <iterator> // std::iterator, std::random_access_iterator_tag
#include <iterator> // std::random_access_iterator_tag
#endif
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
......@@ -98,16 +98,13 @@ struct GenericMember {
\see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator
*/
template <bool Const, typename Encoding, typename Allocator>
class GenericMemberIterator
: public std::iterator<std::random_access_iterator_tag
, typename internal::MaybeAddConst<Const,GenericMember<Encoding,Allocator> >::Type> {
class GenericMemberIterator {
friend class GenericValue<Encoding,Allocator>;
template <bool, typename, typename> friend class GenericMemberIterator;
typedef GenericMember<Encoding,Allocator> PlainType;
typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
typedef std::iterator<std::random_access_iterator_tag,ValueType> BaseType;
public:
//! Iterator type itself
......@@ -117,12 +114,21 @@ public:
//! Non-constant iterator type
typedef GenericMemberIterator<false,Encoding,Allocator> NonConstIterator;
/** \name std::iterator_traits support */
//@{
typedef ValueType value_type;
typedef ValueType * pointer;
typedef ValueType & reference;
typedef std::ptrdiff_t difference_type;
typedef std::random_access_iterator_tag iterator_category;
//@}
//! Pointer to (const) GenericMember
typedef typename BaseType::pointer Pointer;
typedef pointer Pointer;
//! Reference to (const) GenericMember
typedef typename BaseType::reference Reference;
typedef reference Reference;
//! Signed integer type (e.g. \c ptrdiff_t)
typedef typename BaseType::difference_type DifferenceType;
typedef difference_type DifferenceType;
//! Default constructor (singular value)
/*! Creates an iterator pointing to no element.
......@@ -1070,6 +1076,9 @@ public:
//! Get the number of members in the object.
SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; }
//! Get the capacity of object.
SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; }
//! Check whether the object is empty.
bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; }
......@@ -1138,6 +1147,21 @@ public:
/*! \pre IsObject() == true */
MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); }
//! Request the object to have enough capacity to store members.
/*! \param newCapacity The capacity that the object at least need to have.
\param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
\return The value itself for fluent API.
\note Linear time complexity.
*/
GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) {
RAPIDJSON_ASSERT(IsObject());
if (newCapacity > data_.o.capacity) {
SetMembersPointer(reinterpret_cast<Member*>(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member))));
data_.o.capacity = newCapacity;
}
return *this;
}
//! Check whether a member exists in the object.
/*!
\param name Member name to be searched.
......@@ -1243,17 +1267,8 @@ public:
RAPIDJSON_ASSERT(name.IsString());
ObjectData& o = data_.o;
if (o.size >= o.capacity) {
if (o.capacity == 0) {
o.capacity = kDefaultObjectCapacity;
SetMembersPointer(reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member))));
}
else {
SizeType oldCapacity = o.capacity;
o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5
SetMembersPointer(reinterpret_cast<Member*>(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member))));
}
}
if (o.size >= o.capacity)
MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator);
Member* members = GetMembersPointer();
members[o.size].name.RawAssign(name);
members[o.size].value.RawAssign(value);
......@@ -2548,6 +2563,7 @@ public:
~GenericObject() {}
SizeType MemberCount() const { return value_.MemberCount(); }
SizeType MemberCapacity() const { return value_.MemberCapacity(); }
bool ObjectEmpty() const { return value_.ObjectEmpty(); }
template <typename T> ValueType& operator[](T* name) const { return value_[name]; }
template <typename SourceAllocator> ValueType& operator[](const GenericValue<EncodingType, SourceAllocator>& name) const { return value_[name]; }
......@@ -2556,6 +2572,7 @@ public:
#endif
MemberIterator MemberBegin() const { return value_.MemberBegin(); }
MemberIterator MemberEnd() const { return value_.MemberEnd(); }
GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; }
bool HasMember(const Ch* name) const { return value_.HasMember(name); }
#if RAPIDJSON_HAS_STDSTRING
bool HasMember(const std::basic_string<Ch>& name) const { return value_.HasMember(name); }
......
......@@ -384,7 +384,7 @@ struct UTF16BE : UTF16<CharType> {
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
c |= static_cast<uint8_t>(is.Take());
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
return static_cast<CharType>(c);
}
......
......@@ -68,7 +68,7 @@ private:
++current_;
else if (!eof_) {
count_ += readCount_;
readCount_ = fread(buffer_, 1, bufferSize_, fp_);
readCount_ = std::fread(buffer_, 1, bufferSize_, fp_);
bufferLast_ = buffer_ + readCount_ - 1;
current_ = buffer_;
......
......@@ -25,7 +25,7 @@ RAPIDJSON_DIAG_OFF(unreachable-code)
RAPIDJSON_NAMESPACE_BEGIN
//! Wrapper of C file stream for input using fread().
//! Wrapper of C file stream for output using fwrite().
/*!
\note implements Stream concept
*/
......@@ -62,7 +62,7 @@ public:
void Flush() {
if (current_ != buffer_) {
size_t result = fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
size_t result = std::fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
if (result < static_cast<size_t>(current_ - buffer_)) {
// failure deliberately ignored at this time
// added to avoid warn_unused_result build errors
......
......@@ -544,7 +544,8 @@ public:
/*! \param stackAllocator 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(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {}
GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) :
stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {}
//! Parse JSON text.
/*! \tparam parseFlags Combination of \ref ParseFlag.
......
......@@ -26,7 +26,7 @@
#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
#endif
#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
#define RAPIDJSON_SCHEMA_USE_STDREGEX 1
#else
#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
......@@ -418,6 +418,7 @@ public:
not_(),
type_((1 << kTotalSchemaType) - 1), // typeless
validatorCount_(),
notValidatorIndex_(),
properties_(),
additionalPropertiesSchema_(),
patternProperties_(),
......@@ -1063,7 +1064,7 @@ private:
};
#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
typedef internal::GenericRegex<EncodingType> RegexType;
typedef internal::GenericRegex<EncodingType, AllocatorType> RegexType;
#elif RAPIDJSON_SCHEMA_USE_STDREGEX
typedef std::basic_regex<Ch> RegexType;
#else
......@@ -1123,7 +1124,7 @@ private:
template <typename ValueType>
RegexType* CreatePattern(const ValueType& value) {
if (value.IsString()) {
RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_);
if (!r->IsValid()) {
r->~RegexType();
AllocatorType::Free(r);
......
// Tencent is pleased to support the open source community by making RapidJSON available.
//
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
......@@ -7,9 +7,9 @@
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#include "rapidjson.h"
......@@ -100,6 +100,50 @@ inline void PutN(Stream& stream, Ch c, size_t n) {
PutUnsafe(stream, c);
}
///////////////////////////////////////////////////////////////////////////////
// GenericStreamWrapper
//! A Stream Wrapper
/*! \tThis string stream is a wrapper for any stream by just forwarding any
\treceived message to the origin stream.
\note implements Stream concept
*/
#if defined(_MSC_VER) && _MSC_VER <= 1800
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4702) // unreachable code
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
#endif
template <typename InputStream, typename Encoding = UTF8<> >
class GenericStreamWrapper {
public:
typedef typename Encoding::Ch Ch;
GenericStreamWrapper(InputStream& is): is_(is) {}
Ch Peek() const { return is_.Peek(); }
Ch Take() { return is_.Take(); }
size_t Tell() { return is_.Tell(); }
Ch* PutBegin() { return is_.PutBegin(); }
void Put(Ch ch) { is_.Put(ch); }
void Flush() { is_.Flush(); }
size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); }
// wrapper for MemoryStream
const Ch* Peek4() const { return is_.Peek4(); }
// wrapper for AutoUTFInputStream
UTFType GetType() const { return is_.GetType(); }
bool HasBOM() const { return is_.HasBOM(); }
protected:
InputStream& is_;
};
#if defined(_MSC_VER) && _MSC_VER <= 1800
RAPIDJSON_DIAG_POP
#endif
///////////////////////////////////////////////////////////////////////////////
// StringStream
......
......@@ -12,7 +12,7 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights
* RapidJSON Documentation
* [English](http://rapidjson.org/)
* [简体中文](http://rapidjson.org/zh-cn/)
* [GitBook](https://www.gitbook.com/book/Tencent/rapidjson/) with downloadable PDF/EPUB/MOBI, without API reference.
* [GitBook](https://www.gitbook.com/book/miloyip/rapidjson/) with downloadable PDF/EPUB/MOBI, without API reference.
## Build status
......@@ -43,7 +43,7 @@ RapidJSON is a JSON parser and generator for C++. It was inspired by [RapidXml](
More features can be read [here](doc/features.md).
JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC7159/ECMA-404, with optional support of relaxed syntax. More information about JSON can be obtained at
JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in full compliance with RFC7159/ECMA-404, with optional support of relaxed syntax. More information about JSON can be obtained at
* [Introducing JSON](http://json.org/)
* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159)
* [Standard ECMA-404: The JSON Data Interchange Format](https://www.ecma-international.org/publications/standards/Ecma-404.htm)
......
......@@ -12,7 +12,7 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights
* RapidJSON 文档
* [English](http://rapidjson.org/)
* [简体中文](http://rapidjson.org/zh-cn/)
* [GitBook](https://www.gitbook.com/book/Tencent/rapidjson/) 可下载 PDF/EPUB/MOBI,但不含 API 参考手册。
* [GitBook](https://www.gitbook.com/book/miloyip/rapidjson/details/zh-cn) 可下载 PDF/EPUB/MOBI,但不含 API 参考手册。
## Build 状态
......
......@@ -3,6 +3,7 @@ include(CheckCXXCompilerFlag)
set(UNITTEST_SOURCES
allocatorstest.cpp
bigintegertest.cpp
cursorstreamwrappertest.cpp
documenttest.cpp
dtoatest.cpp
encodedstreamtest.cpp
......
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#include "unittest.h"
#include "rapidjson/document.h"
#include "rapidjson/cursorstreamwrapper.h"
using namespace rapidjson;
// static const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}";
bool testJson(const char *json, size_t &line, size_t &col) {
StringStream ss(json);
CursorStreamWrapper<StringStream> csw(ss);
Document document;
document.ParseStream(csw);
bool ret = document.HasParseError();
if (ret) {
col = csw.GetColumn();
line = csw.GetLine();
}
return ret;
}
TEST(CursorStreamWrapper, MissingFirstBracket) {
const char json[] = "\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}";
size_t col, line;
bool ret = testJson(json, line, col);
EXPECT_TRUE(ret);
EXPECT_EQ(line, 3);
EXPECT_EQ(col, 0);
}
TEST(CursorStreamWrapper, MissingQuotes) {
const char json[] = "{\"string\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}";
size_t col, line;
bool ret = testJson(json, line, col);
EXPECT_TRUE(ret);
EXPECT_EQ(line, 1);
EXPECT_EQ(col, 8);
}
TEST(CursorStreamWrapper, MissingColon) {
const char json[] = "{\"string\"\n\n\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}";
size_t col, line;
bool ret = testJson(json, line, col);
EXPECT_TRUE(ret);
EXPECT_EQ(line, 3);
EXPECT_EQ(col, 0);
}
TEST(CursorStreamWrapper, MissingSecondQuotes) {
const char json[] = "{\"string\"\n\n:my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}";
size_t col, line;
bool ret = testJson(json, line, col);
EXPECT_TRUE(ret);
EXPECT_EQ(line, 3);
EXPECT_EQ(col, 1);
}
TEST(CursorStreamWrapper, MissingComma) {
const char json[] = "{\"string\"\n\n:\"my string\"\"array\"\n:[\"1\", \"2\", \"3\"]}";
size_t col, line;
bool ret = testJson(json, line, col);
EXPECT_TRUE(ret);
EXPECT_EQ(line, 3);
EXPECT_EQ(col, 12);
}
TEST(CursorStreamWrapper, MissingArrayBracket) {
const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:\"1\", \"2\", \"3\"]}";
size_t col, line;
bool ret = testJson(json, line, col);
EXPECT_TRUE(ret);
EXPECT_EQ(line, 4);
EXPECT_EQ(col, 9);
}
TEST(CursorStreamWrapper, MissingArrayComma) {
const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\" \"2\", \"3\"]}";
size_t col, line;
bool ret = testJson(json, line, col);
EXPECT_TRUE(ret);
EXPECT_EQ(line, 4);
EXPECT_EQ(col, 6);
}
TEST(CursorStreamWrapper, MissingLastArrayBracket) {
const char json8[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"}";
size_t col, line;
bool ret = testJson(json8, line, col);
EXPECT_TRUE(ret);
EXPECT_EQ(line, 4);
EXPECT_EQ(col, 15);
}
TEST(CursorStreamWrapper, MissingLastBracket) {
const char json9[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]";
size_t col, line;
bool ret = testJson(json9, line, col);
EXPECT_TRUE(ret);
EXPECT_EQ(line, 4);
EXPECT_EQ(col, 16);
}
......@@ -117,7 +117,12 @@ public:
#pragma GCC diagnostic pop
#endif
// Not using noexcept for testing RAPIDJSON_ASSERT()
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
#ifndef RAPIDJSON_ASSERT
#define RAPIDJSON_ASSERT(x) (!(x) ? throw AssertException(RAPIDJSON_STRINGIFY(x)) : (void)0u)
#endif
class Random {
public:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment