Merge branch 'master' into violationDetails
This commit is contained in:
commit
b1e556d713
3
.gitignore
vendored
3
.gitignore
vendored
@ -23,3 +23,6 @@ Doxyfile
|
|||||||
Doxyfile.zh-cn
|
Doxyfile.zh-cn
|
||||||
DartConfiguration.tcl
|
DartConfiguration.tcl
|
||||||
*.nupkg
|
*.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)
|
* Redo all documentation (English, Simplified Chinese)
|
||||||
|
|
||||||
### Changed
|
### 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)
|
* Migrating from Premake to CMAKE (#192)
|
||||||
* Resolve all warning reports
|
* 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_ASAN "Build rapidjson with address sanitizer (gcc/clang)" OFF)
|
||||||
option(RAPIDJSON_BUILD_UBSAN "Build rapidjson with undefined behavior 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)
|
option(RAPIDJSON_HAS_STDSTRING "" OFF)
|
||||||
if(RAPIDJSON_HAS_STDSTRING)
|
if(RAPIDJSON_HAS_STDSTRING)
|
||||||
add_definitions(-DRAPIDJSON_HAS_STDSTRING)
|
add_definitions(-DRAPIDJSON_HAS_STDSTRING)
|
||||||
@ -50,12 +52,14 @@ if(CCACHE_FOUND)
|
|||||||
endif(CCACHE_FOUND)
|
endif(CCACHE_FOUND)
|
||||||
|
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
|
if(${RAPIDJSON_ENABLE_INSTRUMENTATION_OPT})
|
||||||
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "powerpc" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64le")
|
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")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native")
|
||||||
else()
|
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.
|
#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")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||||
endif()
|
endif()
|
||||||
|
endif()
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror")
|
||||||
set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wsign-conversion)
|
set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wsign-conversion)
|
||||||
if (RAPIDJSON_BUILD_CXX11)
|
if (RAPIDJSON_BUILD_CXX11)
|
||||||
@ -181,6 +185,8 @@ EXPORT( PACKAGE ${PROJECT_NAME} )
|
|||||||
# ... for the build tree
|
# ... for the build tree
|
||||||
SET( CONFIG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
SET( CONFIG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
SET( CONFIG_DIR ${CMAKE_CURRENT_BINARY_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
|
CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY )
|
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY )
|
||||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in
|
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in
|
||||||
|
BIN
bin/jsonschema/remotes/.DS_Store
vendored
BIN
bin/jsonschema/remotes/.DS_Store
vendored
Binary file not shown.
BIN
bin/jsonschema/tests/.DS_Store
vendored
BIN
bin/jsonschema/tests/.DS_Store
vendored
Binary file not shown.
BIN
bin/jsonschema/tests/draft4/.DS_Store
vendored
BIN
bin/jsonschema/tests/draft4/.DS_Store
vendored
Binary file not shown.
@ -128,7 +128,7 @@ And the `InputStream` is type of input stream.
|
|||||||
|
|
||||||
## Parse Error {#ParseError}
|
## 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
|
Parse Error Code | Description
|
||||||
--------------------------------------------|---------------------------------------------------
|
--------------------------------------------|---------------------------------------------------
|
||||||
@ -241,7 +241,7 @@ Some techniques about using DOM API is discussed here.
|
|||||||
|
|
||||||
## DOM as SAX Event Publisher
|
## 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
|
~~~~~~~~~~cpp
|
||||||
// ...
|
// ...
|
||||||
|
@ -128,7 +128,7 @@ GenericDocument& GenericDocument::Parse(const Ch* str);
|
|||||||
|
|
||||||
## 解析错误 {#ParseError}
|
## 解析错误 {#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.
|
> (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]
|
[TOC]
|
||||||
|
|
||||||
|
16
doc/faq.md
16
doc/faq.md
@ -116,7 +116,7 @@
|
|||||||
~~~~~~~~~~cpp
|
~~~~~~~~~~cpp
|
||||||
Value(kObjectType).Swap(d);
|
Value(kObjectType).Swap(d);
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
or equivalent, but sightly longer to type:
|
or equivalent, but slightly longer to type:
|
||||||
~~~~~~~~~~cpp
|
~~~~~~~~~~cpp
|
||||||
d.Swap(Value(kObjectType).Move());
|
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:
|
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
|
~~~~~~~~~~cpp
|
||||||
Documnet address(person.GetAllocator());
|
Document address(person.GetAllocator());
|
||||||
...
|
...
|
||||||
person["person"].AddMember("address", address["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?
|
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.
|
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?
|
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?
|
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?
|
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?
|
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.
|
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.
|
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?
|
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
|
## Gossip
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Performance
|
# 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
|
[1]: https://github.com/miloyip/nativejson-benchmark
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ p.Stringify(sb);
|
|||||||
std::cout << sb.GetString() << std::endl;
|
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}
|
# 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.
|
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.
|
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:
|
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.
|
* A `SchemaValidator` may be reused to validate multiple documents. To run it for other documents, call `validator.Reset()` first.
|
||||||
|
|
||||||
# Validation during parsing/serialization {#Fused}
|
# Validation during parsing/serialization {#Fused}
|
||||||
|
@ -8,7 +8,7 @@ RapidJSON 实现了一个 [JSON Schema Draft v4](http://json-schema.org/document
|
|||||||
|
|
||||||
[TOC]
|
[TOC]
|
||||||
|
|
||||||
## 基本用法
|
# 基本用法 {#BasicUsage}
|
||||||
|
|
||||||
首先,你要把 JSON Schema 解析成 `Document`,再把它编译成一个 `SchemaDocument`。
|
首先,你要把 JSON Schema 解析成 `Document`,再把它编译成一个 `SchemaDocument`。
|
||||||
|
|
||||||
@ -52,11 +52,11 @@ if (!d.Accept(validator)) {
|
|||||||
* 一个 `SchemaDocment` 能被多个 `SchemaValidator` 引用。它不会被 `SchemaValidator` 修改。
|
* 一个 `SchemaDocment` 能被多个 `SchemaValidator` 引用。它不会被 `SchemaValidator` 修改。
|
||||||
* 可以重复使用一个 `SchemaValidator` 来校验多个文件。在校验其他文件前,须先调用 `validator.Reset()`。
|
* 可以重复使用一个 `SchemaValidator` 来校验多个文件。在校验其他文件前,须先调用 `validator.Reset()`。
|
||||||
|
|
||||||
## 在解析/生成时进行校验
|
# 在解析/生成时进行校验 {#ParsingSerialization}
|
||||||
|
|
||||||
与大部分 JSON Schema 校验器有所不同,RapidJSON 提供了一个基于 SAX 的 schema 校验器实现。因此,你可以在输入流解析 JSON 的同时进行校验。若校验器遇到一个与 schema 不符的值,就会立即终止解析。这设计对于解析大型 JSON 文件时特别有用。
|
与大部分 JSON Schema 校验器有所不同,RapidJSON 提供了一个基于 SAX 的 schema 校验器实现。因此,你可以在输入流解析 JSON 的同时进行校验。若校验器遇到一个与 schema 不符的值,就会立即终止解析。这设计对于解析大型 JSON 文件时特别有用。
|
||||||
|
|
||||||
### DOM 解析
|
## DOM 解析 {#DomParsing}
|
||||||
|
|
||||||
在使用 DOM 进行解析时,`Document` 除了接收 SAX 事件外,还需做一些准备及结束工作,因此,为了连接 `Reader`、`SchemaValidator` 和 `Document` 要做多一点事情。`SchemaValidatingReader` 是一个辅助类去做那些工作。
|
在使用 DOM 进行解析时,`Document` 除了接收 SAX 事件外,还需做一些准备及结束工作,因此,为了连接 `Reader`、`SchemaValidator` 和 `Document` 要做多一点事情。`SchemaValidatingReader` 是一个辅助类去做那些工作。
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ if (!reader.GetParseResult()) {
|
|||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
### SAX 解析
|
## SAX 解析 {#SaxParsing}
|
||||||
|
|
||||||
使用 SAX 解析时,情况就简单得多。若只需要校验 JSON 而无需进一步处理,那么仅需要:
|
使用 SAX 解析时,情况就简单得多。若只需要校验 JSON 而无需进一步处理,那么仅需要:
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ if (!reader.Parse(ss, validator)) {
|
|||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
### 生成
|
## 生成 {#Serialization}
|
||||||
|
|
||||||
我们也可以在生成(serialization)的时候进行校验。这能确保输出的 JSON 符合一个 JSON Schema。
|
我们也可以在生成(serialization)的时候进行校验。这能确保输出的 JSON 符合一个 JSON Schema。
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ if (!d.Accept(validator)) {
|
|||||||
|
|
||||||
当然,如果你的应用仅需要 SAX 风格的生成,那么只需要把 SAX 事件由原来发送到 `Writer`,改为发送到 `SchemaValidator`。
|
当然,如果你的应用仅需要 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。例如:
|
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);
|
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 个。
|
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 关键字也会被忽略,因为标准中并没需求必须实现。
|
除此以外,关于字符串类型的 `format` schema 关键字也会被忽略,因为标准中并没需求必须实现。
|
||||||
|
|
||||||
### 正则表达式
|
## 正则表达式 {#RegEx}
|
||||||
|
|
||||||
`pattern` 及 `patternProperties` 这两个 schema 关键字使用了正则表达式去匹配所需的模式。
|
`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`,可以把两个宏都设为零,以禁用此功能,这样做可节省一些代码体积。
|
对于使用 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 库。
|
大部分 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
|
t = true
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
JSON null can be queryed with `IsNull()`.
|
JSON null can be queried with `IsNull()`.
|
||||||
~~~~~~~~~~cpp
|
~~~~~~~~~~cpp
|
||||||
printf("n = %s\n", document["n"].IsNull() ? "null" : "?");
|
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")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_executable(archivertest archiver/archiver.cpp archiver/archivertest.cpp)
|
||||||
|
|
||||||
foreach (example ${EXAMPLES})
|
foreach (example ${EXAMPLES})
|
||||||
add_executable(${example} ${example}/${example}.cpp)
|
add_executable(${example} ${example}/${example}.cpp)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
292
example/archiver/archiver.cpp
Normal file
292
example/archiver/archiver.cpp
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
#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
|
139
example/archiver/archiver.h
Normal file
139
example/archiver/archiver.h
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#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__
|
281
example/archiver/archivertest.cpp
Normal file
281
example/archiver/archivertest.cpp
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
#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();
|
||||||
|
}
|
78
include/rapidjson/cursorstreamwrapper.h
Normal file
78
include/rapidjson/cursorstreamwrapper.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// 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__
|
#endif // __GNUC__
|
||||||
|
|
||||||
#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
|
#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
|
||||||
#include <iterator> // std::iterator, std::random_access_iterator_tag
|
#include <iterator> // std::random_access_iterator_tag
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||||
@ -98,16 +98,13 @@ struct GenericMember {
|
|||||||
\see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator
|
\see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator
|
||||||
*/
|
*/
|
||||||
template <bool Const, typename Encoding, typename Allocator>
|
template <bool Const, typename Encoding, typename Allocator>
|
||||||
class GenericMemberIterator
|
class GenericMemberIterator {
|
||||||
: public std::iterator<std::random_access_iterator_tag
|
|
||||||
, typename internal::MaybeAddConst<Const,GenericMember<Encoding,Allocator> >::Type> {
|
|
||||||
|
|
||||||
friend class GenericValue<Encoding,Allocator>;
|
friend class GenericValue<Encoding,Allocator>;
|
||||||
template <bool, typename, typename> friend class GenericMemberIterator;
|
template <bool, typename, typename> friend class GenericMemberIterator;
|
||||||
|
|
||||||
typedef GenericMember<Encoding,Allocator> PlainType;
|
typedef GenericMember<Encoding,Allocator> PlainType;
|
||||||
typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
|
typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
|
||||||
typedef std::iterator<std::random_access_iterator_tag,ValueType> BaseType;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//! Iterator type itself
|
//! Iterator type itself
|
||||||
@ -117,12 +114,21 @@ public:
|
|||||||
//! Non-constant iterator type
|
//! Non-constant iterator type
|
||||||
typedef GenericMemberIterator<false,Encoding,Allocator> NonConstIterator;
|
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
|
//! Pointer to (const) GenericMember
|
||||||
typedef typename BaseType::pointer Pointer;
|
typedef pointer Pointer;
|
||||||
//! Reference to (const) GenericMember
|
//! Reference to (const) GenericMember
|
||||||
typedef typename BaseType::reference Reference;
|
typedef reference Reference;
|
||||||
//! Signed integer type (e.g. \c ptrdiff_t)
|
//! Signed integer type (e.g. \c ptrdiff_t)
|
||||||
typedef typename BaseType::difference_type DifferenceType;
|
typedef difference_type DifferenceType;
|
||||||
|
|
||||||
//! Default constructor (singular value)
|
//! Default constructor (singular value)
|
||||||
/*! Creates an iterator pointing to no element.
|
/*! Creates an iterator pointing to no element.
|
||||||
@ -1070,6 +1076,9 @@ public:
|
|||||||
//! Get the number of members in the object.
|
//! Get the number of members in the object.
|
||||||
SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; }
|
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.
|
//! Check whether the object is empty.
|
||||||
bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; }
|
bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; }
|
||||||
|
|
||||||
@ -1138,6 +1147,21 @@ public:
|
|||||||
/*! \pre IsObject() == true */
|
/*! \pre IsObject() == true */
|
||||||
MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); }
|
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.
|
//! Check whether a member exists in the object.
|
||||||
/*!
|
/*!
|
||||||
\param name Member name to be searched.
|
\param name Member name to be searched.
|
||||||
@ -1243,17 +1267,8 @@ public:
|
|||||||
RAPIDJSON_ASSERT(name.IsString());
|
RAPIDJSON_ASSERT(name.IsString());
|
||||||
|
|
||||||
ObjectData& o = data_.o;
|
ObjectData& o = data_.o;
|
||||||
if (o.size >= o.capacity) {
|
if (o.size >= o.capacity)
|
||||||
if (o.capacity == 0) {
|
MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator);
|
||||||
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))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Member* members = GetMembersPointer();
|
Member* members = GetMembersPointer();
|
||||||
members[o.size].name.RawAssign(name);
|
members[o.size].name.RawAssign(name);
|
||||||
members[o.size].value.RawAssign(value);
|
members[o.size].value.RawAssign(value);
|
||||||
@ -2548,6 +2563,7 @@ public:
|
|||||||
~GenericObject() {}
|
~GenericObject() {}
|
||||||
|
|
||||||
SizeType MemberCount() const { return value_.MemberCount(); }
|
SizeType MemberCount() const { return value_.MemberCount(); }
|
||||||
|
SizeType MemberCapacity() const { return value_.MemberCapacity(); }
|
||||||
bool ObjectEmpty() const { return value_.ObjectEmpty(); }
|
bool ObjectEmpty() const { return value_.ObjectEmpty(); }
|
||||||
template <typename T> ValueType& operator[](T* name) const { return value_[name]; }
|
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]; }
|
template <typename SourceAllocator> ValueType& operator[](const GenericValue<EncodingType, SourceAllocator>& name) const { return value_[name]; }
|
||||||
@ -2556,6 +2572,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
MemberIterator MemberBegin() const { return value_.MemberBegin(); }
|
MemberIterator MemberBegin() const { return value_.MemberBegin(); }
|
||||||
MemberIterator MemberEnd() const { return value_.MemberEnd(); }
|
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); }
|
bool HasMember(const Ch* name) const { return value_.HasMember(name); }
|
||||||
#if RAPIDJSON_HAS_STDSTRING
|
#if RAPIDJSON_HAS_STDSTRING
|
||||||
bool HasMember(const std::basic_string<Ch>& name) const { return value_.HasMember(name); }
|
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) {
|
static CharType Take(InputByteStream& is) {
|
||||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||||
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
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);
|
return static_cast<CharType>(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ private:
|
|||||||
++current_;
|
++current_;
|
||||||
else if (!eof_) {
|
else if (!eof_) {
|
||||||
count_ += readCount_;
|
count_ += readCount_;
|
||||||
readCount_ = fread(buffer_, 1, bufferSize_, fp_);
|
readCount_ = std::fread(buffer_, 1, bufferSize_, fp_);
|
||||||
bufferLast_ = buffer_ + readCount_ - 1;
|
bufferLast_ = buffer_ + readCount_ - 1;
|
||||||
current_ = buffer_;
|
current_ = buffer_;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ RAPIDJSON_DIAG_OFF(unreachable-code)
|
|||||||
|
|
||||||
RAPIDJSON_NAMESPACE_BEGIN
|
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
|
\note implements Stream concept
|
||||||
*/
|
*/
|
||||||
@ -62,7 +62,7 @@ public:
|
|||||||
|
|
||||||
void Flush() {
|
void Flush() {
|
||||||
if (current_ != buffer_) {
|
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_)) {
|
if (result < static_cast<size_t>(current_ - buffer_)) {
|
||||||
// failure deliberately ignored at this time
|
// failure deliberately ignored at this time
|
||||||
// added to avoid warn_unused_result build errors
|
// 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 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)
|
\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.
|
//! Parse JSON text.
|
||||||
/*! \tparam parseFlags Combination of \ref ParseFlag.
|
/*! \tparam parseFlags Combination of \ref ParseFlag.
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
|
#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
|
||||||
#endif
|
#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
|
#define RAPIDJSON_SCHEMA_USE_STDREGEX 1
|
||||||
#else
|
#else
|
||||||
#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
|
#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
|
||||||
@ -418,6 +418,7 @@ public:
|
|||||||
not_(),
|
not_(),
|
||||||
type_((1 << kTotalSchemaType) - 1), // typeless
|
type_((1 << kTotalSchemaType) - 1), // typeless
|
||||||
validatorCount_(),
|
validatorCount_(),
|
||||||
|
notValidatorIndex_(),
|
||||||
properties_(),
|
properties_(),
|
||||||
additionalPropertiesSchema_(),
|
additionalPropertiesSchema_(),
|
||||||
patternProperties_(),
|
patternProperties_(),
|
||||||
@ -1063,7 +1064,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
|
#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
|
||||||
typedef internal::GenericRegex<EncodingType> RegexType;
|
typedef internal::GenericRegex<EncodingType, AllocatorType> RegexType;
|
||||||
#elif RAPIDJSON_SCHEMA_USE_STDREGEX
|
#elif RAPIDJSON_SCHEMA_USE_STDREGEX
|
||||||
typedef std::basic_regex<Ch> RegexType;
|
typedef std::basic_regex<Ch> RegexType;
|
||||||
#else
|
#else
|
||||||
@ -1123,7 +1124,7 @@ private:
|
|||||||
template <typename ValueType>
|
template <typename ValueType>
|
||||||
RegexType* CreatePattern(const ValueType& value) {
|
RegexType* CreatePattern(const ValueType& value) {
|
||||||
if (value.IsString()) {
|
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()) {
|
if (!r->IsValid()) {
|
||||||
r->~RegexType();
|
r->~RegexType();
|
||||||
AllocatorType::Free(r);
|
AllocatorType::Free(r);
|
||||||
|
@ -100,6 +100,50 @@ inline void PutN(Stream& stream, Ch c, size_t n) {
|
|||||||
PutUnsafe(stream, c);
|
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
|
// StringStream
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights
|
|||||||
* RapidJSON Documentation
|
* RapidJSON Documentation
|
||||||
* [English](http://rapidjson.org/)
|
* [English](http://rapidjson.org/)
|
||||||
* [简体中文](http://rapidjson.org/zh-cn/)
|
* [简体中文](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
|
## 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).
|
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/)
|
* [Introducing JSON](http://json.org/)
|
||||||
* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159)
|
* [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)
|
* [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 文档
|
* RapidJSON 文档
|
||||||
* [English](http://rapidjson.org/)
|
* [English](http://rapidjson.org/)
|
||||||
* [简体中文](http://rapidjson.org/zh-cn/)
|
* [简体中文](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 状态
|
## Build 状态
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ include(CheckCXXCompilerFlag)
|
|||||||
set(UNITTEST_SOURCES
|
set(UNITTEST_SOURCES
|
||||||
allocatorstest.cpp
|
allocatorstest.cpp
|
||||||
bigintegertest.cpp
|
bigintegertest.cpp
|
||||||
|
cursorstreamwrappertest.cpp
|
||||||
documenttest.cpp
|
documenttest.cpp
|
||||||
dtoatest.cpp
|
dtoatest.cpp
|
||||||
encodedstreamtest.cpp
|
encodedstreamtest.cpp
|
||||||
|
115
test/unittest/cursorstreamwrappertest.cpp
Normal file
115
test/unittest/cursorstreamwrappertest.cpp
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// 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
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#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)
|
#define RAPIDJSON_ASSERT(x) (!(x) ? throw AssertException(RAPIDJSON_STRINGIFY(x)) : (void)0u)
|
||||||
|
#endif
|
||||||
|
|
||||||
class Random {
|
class Random {
|
||||||
public:
|
public:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user