fixed adding file problem
[c11concurrency-benchmarks.git] / gdax-orderbook-hpp / demo / dependencies / rapidjson-1.1.0 / doc / schema.zh-cn.md
1 # Schema
2
3 (本功能于 v1.1.0 发布)
4
5 JSON Schema 是描述 JSON 格式的一个标准草案。一个 schema 本身也是一个 JSON。使用 JSON Schema 去校验 JSON,可以让你的代码安全地访问 DOM,而无须检查类型或键值是否存在等。这也能确保输出的 JSON 是符合指定的 schema。
6
7 RapidJSON 实现了一个 [JSON Schema Draft v4](http://json-schema.org/documentation.html) 的校验器。若你不熟悉 JSON Schema,可以参考 [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/)。
8
9 [TOC]
10
11 ## 基本用法
12
13 首先,你要把 JSON Schema 解析成 `Document`,再把它编译成一个 `SchemaDocument`。
14
15 然后,利用该 `SchemaDocument` 创建一个 `SchemaValidator`。它与 `Writer` 相似,都是能够处理 SAX 事件的。因此,你可以用 `document.Accept(validator)` 去校验一个 JSON,然后再获取校验结果。
16
17 ~~~cpp
18 #include "rapidjson/schema.h"
19
20 // ...
21
22 Document sd;
23 if (!sd.Parse(schemaJson).HasParseError()) {
24     // 此 schema 不是合法的 JSON
25     // ...       
26 }
27 SchemaDocument schema(sd); // 把一个 Document 编译至 SchemaDocument
28 // 之后不再需要 sd
29
30 Document d;
31 if (!d.Parse(inputJson).HasParseError()) {
32     // 输入不是一个合法的 JSON
33     // ...       
34 }
35
36 SchemaValidator validator(schema);
37 if (!d.Accept(validator)) {
38     // 输入的 JSON 不合乎 schema
39     // 打印诊断信息
40     StringBuffer sb;
41     validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
42     printf("Invalid schema: %s\n", sb.GetString());
43     printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());
44     sb.Clear();
45     validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
46     printf("Invalid document: %s\n", sb.GetString());
47 }
48 ~~~
49
50 一些注意点:
51
52 * 一个 `SchemaDocment` 能被多个 `SchemaValidator` 引用。它不会被 `SchemaValidator` 修改。
53 * 可以重复使用一个 `SchemaValidator` 来校验多个文件。在校验其他文件前,须先调用 `validator.Reset()`。
54
55 ## 在解析/生成时进行校验
56
57 与大部分 JSON Schema 校验器有所不同,RapidJSON 提供了一个基于 SAX 的 schema 校验器实现。因此,你可以在输入流解析 JSON 的同时进行校验。若校验器遇到一个与 schema 不符的值,就会立即终止解析。这设计对于解析大型 JSON 文件时特别有用。
58
59 ### DOM 解析
60
61 在使用 DOM 进行解析时,`Document` 除了接收 SAX 事件外,还需做一些准备及结束工作,因此,为了连接 `Reader`、`SchemaValidator` 和 `Document` 要做多一点事情。`SchemaValidatingReader` 是一个辅助类去做那些工作。
62
63 ~~~cpp
64 #include "rapidjson/filereadstream.h"
65
66 // ...
67 SchemaDocument schema(sd); // 把一个 Document 编译至 SchemaDocument
68
69 // 使用 reader 解析 JSON
70 FILE* fp = fopen("big.json", "r");
71 FileReadStream is(fp, buffer, sizeof(buffer));
72
73 // 用 reader 解析 JSON,校验它的 SAX 事件,并存储至 d
74 Document d;
75 SchemaValidatingReader<kParseDefaultFlags, FileReadStream, UTF8<> > reader(is, schema);
76 d.Populate(reader);
77
78 if (!reader.GetParseResult()) {
79     // 不是一个合法的 JSON
80     // 当 reader.GetParseResult().Code() == kParseErrorTermination,
81     // 它可能是被以下原因中止:
82     // (1) 校验器发现 JSON 不合乎 schema;或
83     // (2) 输入流有 I/O 错误。
84
85     // 检查校验结果
86     if (!reader.IsValid()) {
87         // 输入的 JSON 不合乎 schema
88         // 打印诊断信息
89         StringBuffer sb;
90         reader.GetInvalidSchemaPointer().StringifyUriFragment(sb);
91         printf("Invalid schema: %s\n", sb.GetString());
92         printf("Invalid keyword: %s\n", reader.GetInvalidSchemaKeyword());
93         sb.Clear();
94         reader.GetInvalidDocumentPointer().StringifyUriFragment(sb);
95         printf("Invalid document: %s\n", sb.GetString());
96     }
97 }
98 ~~~
99
100 ### SAX 解析
101
102 使用 SAX 解析时,情况就简单得多。若只需要校验 JSON 而无需进一步处理,那么仅需要:
103
104 ~~~
105 SchemaValidator validator(schema);
106 Reader reader;
107 if (!reader.Parse(stream, validator)) {
108     if (!validator.IsValid()) {
109         // ...    
110     }
111 }
112 ~~~
113
114 这种方式和 [schemavalidator](example/schemavalidator/schemavalidator.cpp) 例子完全相同。这带来的独特优势是,无论 JSON 多巨大,永远维持低内存用量(内存用量只与 Schema 的复杂度相关)。
115
116 若你需要进一步处理 SAX 事件,便可使用模板类 `GenericSchemaValidator` 去设置校验器的输出 `Handler`:
117
118 ~~~
119 MyHandler handler;
120 GenericSchemaValidator<SchemaDocument, MyHandler> validator(schema, handler);
121 Reader reader;
122 if (!reader.Parse(ss, validator)) {
123     if (!validator.IsValid()) {
124         // ...    
125     }
126 }
127 ~~~
128
129 ### 生成
130
131 我们也可以在生成(serialization)的时候进行校验。这能确保输出的 JSON 符合一个 JSON Schema。
132
133 ~~~
134 StringBuffer sb;
135 Writer<StringBuffer> writer(sb);
136 GenericSchemaValidator<SchemaDocument, Writer<StringBuffer> > validator(s, writer);
137 if (!d.Accept(validator)) {
138     // Some problem during Accept(), it may be validation or encoding issues.
139     if (!validator.IsValid()) {
140         // ...
141     }
142 }
143 ~~~
144
145 当然,如果你的应用仅需要 SAX 风格的生成,那么只需要把 SAX 事件由原来发送到 `Writer`,改为发送到 `SchemaValidator`。
146
147 ## 远程 Schema
148
149 JSON Schema 支持 [`$ref` 关键字](http://spacetelescope.github.io/understanding-json-schema/structuring.html),它是一个 [JSON pointer](doc/pointer.zh-cn.md) 引用至一个本地(local)或远程(remote) schema。本地指针的首字符是 `#`,而远程指针是一个相对或绝对 URI。例如:
150
151 ~~~js
152 { "$ref": "definitions.json#/address" }
153 ~~~
154
155 由于 `SchemaDocument` 并不知道如何处理那些 URI,它需要使用者提供一个 `IRemoteSchemaDocumentProvider` 的实例去处理。
156
157 ~~~
158 class MyRemoteSchemaDocumentProvider : public IRemoteSchemaDocumentProvider {
159 public:
160     virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeTyp length) {
161         // Resolve the uri and returns a pointer to that schema.
162     }
163 };
164
165 // ...
166
167 MyRemoteSchemaDocumentProvider provider;
168 SchemaDocument schema(sd, &provider);
169 ~~~
170
171 ## 标准的符合程度
172
173 RapidJSON 通过了 [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) (Json Schema draft 4) 中 263 个测试的 262 个。
174
175 没通过的测试是 `refRemote.json` 中的 "change resolution scope" - "changed scope ref invalid"。这是由于未实现 `id` schema 关键字及 URI 合并功能。
176
177 除此以外,关于字符串类型的 `format` schema 关键字也会被忽略,因为标准中并没需求必须实现。
178
179 ### 正则表达式
180
181 `pattern` 及 `patternProperties` 这两个 schema 关键字使用了正则表达式去匹配所需的模式。
182
183 RapidJSON 实现了一个简单的 NFA 正则表达式引擎,并预设使用。它支持以下语法。
184
185 |语法|描述|
186 |------|-----------|
187 |`ab`    | 串联 |
188 |`a|b`   | 交替 |
189 |`a?`    | 零或一次 |
190 |`a*`    | 零或多次 |
191 |`a+`    | 一或多次 |
192 |`a{3}`  | 刚好 3 次 |
193 |`a{3,}` | 至少 3 次 |
194 |`a{3,5}`| 3 至 5 次 |
195 |`(ab)`  | 分组 |
196 |`^a`    | 在开始处 |
197 |`a$`    | 在结束处 |
198 |`.`     | 任何字符 |
199 |`[abc]` | 字符组 |
200 |`[a-c]` | 字符组范围 |
201 |`[a-z0-9_]` | 字符组组合 |
202 |`[^abc]` | 字符组取反 |
203 |`[^a-c]` | 字符组范围取反 |
204 |`[\b]`   | 退格符 (U+0008) |
205 |`\|`, `\\`, ...  | 转义字符 |
206 |`\f` | 馈页 (U+000C) |
207 |`\n` | 馈行 (U+000A) |
208 |`\r` | 回车 (U+000D) |
209 |`\t` | 制表 (U+0009) |
210 |`\v` | 垂直制表 (U+000B) |
211
212 对于使用 C++11 编译器的使用者,也可使用 `std::regex`,只需定义 `RAPIDJSON_SCHEMA_USE_INTERNALREGEX=0` 及 `RAPIDJSON_SCHEMA_USE_STDREGEX=1`。若你的 schema 无需使用 `pattern` 或 `patternProperties`,可以把两个宏都设为零,以禁用此功能,这样做可节省一些代码体积。
213
214 ## 性能
215
216 大部分 C++ JSON 库都未支持 JSON Schema。因此我们尝试按照 [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark) 去评估 RapidJSON 的 JSON Schema 校验器。该评测测试了 11 个运行在 node.js 上的 JavaScript 库。
217
218 该评测校验 [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) 中的测试,当中排除了一些测试套件及个别测试。我们在 [`schematest.cpp`](test/perftest/schematest.cpp) 实现了相同的评测。
219
220 在 MacBook Pro (2.8 GHz Intel Core i7) 上收集到以下结果。
221
222 |校验器|相对速度|每秒执行的测试数目|
223 |---------|:------------:|:----------------------------:|
224 |RapidJSON|155%|30682|
225 |[`ajv`](https://github.com/epoberezkin/ajv)|100%|19770 (± 1.31%)|
226 |[`is-my-json-valid`](https://github.com/mafintosh/is-my-json-valid)|70%|13835 (± 2.84%)|
227 |[`jsen`](https://github.com/bugventure/jsen)|57.7%|11411 (± 1.27%)|
228 |[`schemasaurus`](https://github.com/AlexeyGrishin/schemasaurus)|26%|5145 (± 1.62%)|
229 |[`themis`](https://github.com/playlyfe/themis)|19.9%|3935 (± 2.69%)|
230 |[`z-schema`](https://github.com/zaggino/z-schema)|7%|1388 (± 0.84%)|
231 |[`jsck`](https://github.com/pandastrike/jsck#readme)|3.1%|606 (± 2.84%)|
232 |[`jsonschema`](https://github.com/tdegrunt/jsonschema#readme)|0.9%|185 (± 1.01%)|
233 |[`skeemas`](https://github.com/Prestaul/skeemas#readme)|0.8%|154 (± 0.79%)|
234 |tv4|0.5%|93 (± 0.94%)|
235 |[`jayschema`](https://github.com/natesilva/jayschema)|0.1%|21 (± 1.14%)|
236
237 换言之,RapidJSON 比最快的 JavaScript 库(ajv)快约 1.5x。比最慢的快 1400x。