<!-- HTML header for doxygen 1.8.7--> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/> <meta http-equiv="X-UA-Compatible" content="IE=9"/> <meta name="generator" content="Doxygen 1.8.14"/> <title>RapidJSON: Schema</title> <link href="tabs.css" rel="stylesheet" type="text/css"/> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="dynsections.js"></script> <link href="navtree.css" rel="stylesheet" type="text/css"/> <script type="text/javascript" src="resize.js"></script> <script type="text/javascript" src="navtreedata.js"></script> <script type="text/javascript" src="navtree.js"></script> <script type="text/javascript"> /* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */ $(document).ready(initResizable); /* @license-end */</script> <link href="search/search.css" rel="stylesheet" type="text/css"/> <script type="text/javascript" src="search/searchdata.js"></script> <script type="text/javascript" src="search/search.js"></script> <script type="text/javascript"> /* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */ $(document).ready(function() { init_search(); }); /* @license-end */ </script> <link href="doxygen.css" rel="stylesheet" type="text/css" /> <link href="doxygenextra.css" rel="stylesheet" type="text/css"/> </head> <body> <div id="top"><!-- do not remove this div, it is closed by doxygen! --> <div id="topbanner"><a href="https://github.com/miloyip/rapidjson" title="RapidJSON GitHub"><i class="githublogo"></i></a></div> <div id="MSearchBox" class="MSearchBoxInactive"> <span class="left"> <img id="MSearchSelect" src="search/mag_sel.png" onmouseover="return searchBox.OnSearchSelectShow()" onmouseout="return searchBox.OnSearchSelectHide()" alt=""/> <input type="text" id="MSearchField" value="Search" accesskey="S" onfocus="searchBox.OnSearchFieldFocus(true)" onblur="searchBox.OnSearchFieldFocus(false)" onkeyup="searchBox.OnSearchFieldChange(event)"/> </span><span class="right"> <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a> </span> </div> <!-- end header part --> <!-- Generated by Doxygen 1.8.14 --> <script type="text/javascript"> /* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */ var searchBox = new SearchBox("searchBox", "search",false,'Search'); /* @license-end */ </script> </div><!-- top --> <div id="side-nav" class="ui-resizable side-nav-resizable"> <div id="nav-tree"> <div id="nav-tree-contents"> <div id="nav-sync" class="sync"></div> </div> </div> <div id="splitbar" style="-moz-user-select:none;" class="ui-resizable-handle"> </div> </div> <script type="text/javascript"> /* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */ $(document).ready(function(){initNavTree('md_doc_schema.html','');}); /* @license-end */ </script> <div id="doc-content"> <!-- window showing the filter options --> <div id="MSearchSelectWindow" onmouseover="return searchBox.OnSearchSelectShow()" onmouseout="return searchBox.OnSearchSelectHide()" onkeydown="return searchBox.OnSearchSelectKey(event)"> </div> <!-- iframe showing the search results (closed by default) --> <div id="MSearchResultsWindow"> <iframe src="javascript:void(0)" frameborder="0" name="MSearchResults" id="MSearchResults"> </iframe> </div> <div class="header"> <div class="headertitle"> <div class="title">Schema </div> </div> </div><!--header--> <div class="contents"> <div class="textblock"><p>(This feature was released in v1.1.0)</p> <p>JSON Schema is a draft standard for describing the format of JSON data. The schema itself is also JSON data. By validating a JSON structure with JSON Schema, your code can safely access the DOM without manually checking types, or whether a key exists, etc. It can also ensure that the serialized JSON conform to a specified schema.</p> <p>RapidJSON implemented a JSON Schema validator for <a href="http://json-schema.org/documentation.html">JSON Schema Draft v4</a>. If you are not familiar with JSON Schema, you may refer to <a href="http://spacetelescope.github.io/understanding-json-schema/">Understanding JSON Schema</a>.</p> <h2>Basic Usage</h2> <p>First of all, you need to parse a JSON Schema into <code>Document</code>, and then compile the <code>Document</code> into a <code>SchemaDocument</code>.</p> <p>Secondly, construct a <code>SchemaValidator</code> with the <code>SchemaDocument</code>. It is similar to a <code>Writer</code> in the sense of handling SAX events. So, you can use <code>document.Accept(validator)</code> to validate a document, and then check the validity.</p> <div class="fragment"><div class="line"><span class="preprocessor">#include "rapidjson/schema.h"</span></div><div class="line"></div><div class="line"><span class="comment">// ...</span></div><div class="line"></div><div class="line"><a class="code" href="namespacerapidjson.html#ace11b5b575baf1cccd5ba5f8586dcdc8">Document</a> sd;</div><div class="line"><span class="keywordflow">if</span> (!sd.Parse(schemaJson).HasParseError()) {</div><div class="line"> <span class="comment">// the schema is not a valid JSON.</span></div><div class="line"> <span class="comment">// ... </span></div><div class="line">}</div><div class="line"><a class="code" href="namespacerapidjson.html#a52bbb5d64d1319495089e1713a0653cf">SchemaDocument</a> schema(sd); <span class="comment">// Compile a Document to SchemaDocument</span></div><div class="line"><span class="comment">// sd is no longer needed here.</span></div><div class="line"></div><div class="line"><a class="code" href="namespacerapidjson.html#ace11b5b575baf1cccd5ba5f8586dcdc8">Document</a> d;</div><div class="line"><span class="keywordflow">if</span> (!d.Parse(inputJson).HasParseError()) {</div><div class="line"> <span class="comment">// the input is not a valid JSON.</span></div><div class="line"> <span class="comment">// ... </span></div><div class="line">}</div><div class="line"></div><div class="line">SchemaValidator validator(schema);</div><div class="line"><span class="keywordflow">if</span> (!d.Accept(validator)) {</div><div class="line"> <span class="comment">// Input JSON is invalid according to the schema</span></div><div class="line"> <span class="comment">// Output diagnostic information</span></div><div class="line"> <a class="code" href="namespacerapidjson.html#ac0765ea91f41539645c4b78689d03f21">StringBuffer</a> sb;</div><div class="line"> validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);</div><div class="line"> printf(<span class="stringliteral">"Invalid schema: %s\n"</span>, sb.GetString());</div><div class="line"> printf(<span class="stringliteral">"Invalid keyword: %s\n"</span>, validator.GetInvalidSchemaKeyword());</div><div class="line"> sb.Clear();</div><div class="line"> validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);</div><div class="line"> printf(<span class="stringliteral">"Invalid document: %s\n"</span>, sb.GetString());</div><div class="line">}</div></div><!-- fragment --><p>Some notes:</p> <ul> <li>One <code>SchemaDocment</code> can be referenced by multiple <code>SchemaValidator</code>s. It will not be modified by <code>SchemaValidator</code>s.</li> <li>A <code>SchemaValidator</code> may be reused to validate multiple documents. To run it for other documents, call <code>validator.Reset()</code> first.</li> </ul> <h2>Validation during parsing/serialization</h2> <p>Unlike most JSON Schema validator implementations, RapidJSON provides a SAX-based schema validator. Therefore, you can parse a JSON from a stream while validating it on the fly. If the validator encounters a JSON value that invalidates the supplied schema, the parsing will be terminated immediately. This design is especially useful for parsing large JSON files.</p> <h3>DOM parsing</h3> <p>For using DOM in parsing, <code>Document</code> needs some preparation and finalizing tasks, in addition to receiving SAX events, thus it needs some work to route the reader, validator and the document. <code>SchemaValidatingReader</code> is a helper class that doing such work.</p> <div class="fragment"><div class="line"><span class="preprocessor">#include "rapidjson/filereadstream.h"</span></div><div class="line"></div><div class="line"><span class="comment">// ...</span></div><div class="line"><a class="code" href="namespacerapidjson.html#a52bbb5d64d1319495089e1713a0653cf">SchemaDocument</a> schema(sd); <span class="comment">// Compile a Document to SchemaDocument</span></div><div class="line"></div><div class="line"><span class="comment">// Use reader to parse the JSON</span></div><div class="line">FILE* fp = fopen(<span class="stringliteral">"big.json"</span>, <span class="stringliteral">"r"</span>);</div><div class="line">FileReadStream is(fp, buffer, <span class="keyword">sizeof</span>(buffer));</div><div class="line"></div><div class="line"><span class="comment">// Parse JSON from reader, validate the SAX events, and store in d.</span></div><div class="line"><a class="code" href="namespacerapidjson.html#ace11b5b575baf1cccd5ba5f8586dcdc8">Document</a> d;</div><div class="line">SchemaValidatingReader<kParseDefaultFlags, FileReadStream, UTF8<> > reader(is, schema);</div><div class="line">d.Populate(reader);</div><div class="line"></div><div class="line"><span class="keywordflow">if</span> (!reader.GetParseResult()) {</div><div class="line"> <span class="comment">// Not a valid JSON</span></div><div class="line"> <span class="comment">// When reader.GetParseResult().Code() == kParseErrorTermination,</span></div><div class="line"> <span class="comment">// it may be terminated by:</span></div><div class="line"> <span class="comment">// (1) the validator found that the JSON is invalid according to schema; or</span></div><div class="line"> <span class="comment">// (2) the input stream has I/O error.</span></div><div class="line"></div><div class="line"> <span class="comment">// Check the validation result</span></div><div class="line"> <span class="keywordflow">if</span> (!reader.IsValid()) {</div><div class="line"> <span class="comment">// Input JSON is invalid according to the schema</span></div><div class="line"> <span class="comment">// Output diagnostic information</span></div><div class="line"> <a class="code" href="namespacerapidjson.html#ac0765ea91f41539645c4b78689d03f21">StringBuffer</a> sb;</div><div class="line"> reader.GetInvalidSchemaPointer().StringifyUriFragment(sb);</div><div class="line"> printf(<span class="stringliteral">"Invalid schema: %s\n"</span>, sb.GetString());</div><div class="line"> printf(<span class="stringliteral">"Invalid keyword: %s\n"</span>, reader.GetInvalidSchemaKeyword());</div><div class="line"> sb.Clear();</div><div class="line"> reader.GetInvalidDocumentPointer().StringifyUriFragment(sb);</div><div class="line"> printf(<span class="stringliteral">"Invalid document: %s\n"</span>, sb.GetString());</div><div class="line"> }</div><div class="line">}</div></div><!-- fragment --><h3>SAX parsing</h3> <p>For using SAX in parsing, it is much simpler. If it only need to validate the JSON without further processing, it is simply:</p> <div class="fragment"><div class="line">SchemaValidator validator(schema);</div><div class="line">Reader reader;</div><div class="line">if (!reader.Parse(stream, validator)) {</div><div class="line"> if (!validator.IsValid()) {</div><div class="line"> // ... </div><div class="line"> }</div><div class="line">}</div></div><!-- fragment --><p>This is exactly the method used in the <a href="example/schemavalidator/schemavalidator.cpp">schemavalidator</a> example. The distinct advantage is low memory usage, no matter how big the JSON was (the memory usage depends on the complexity of the schema).</p> <p>If you need to handle the SAX events further, then you need to use the template class <code>GenericSchemaValidator</code> to set the output handler of the validator:</p> <div class="fragment"><div class="line">MyHandler handler;</div><div class="line">GenericSchemaValidator<SchemaDocument, MyHandler> validator(schema, handler);</div><div class="line">Reader reader;</div><div class="line">if (!reader.Parse(ss, validator)) {</div><div class="line"> if (!validator.IsValid()) {</div><div class="line"> // ... </div><div class="line"> }</div><div class="line">}</div></div><!-- fragment --><h3>Serialization</h3> <p>It is also possible to do validation during serializing. This can ensure the result JSON is valid according to the JSON schema.</p> <div class="fragment"><div class="line">StringBuffer sb;</div><div class="line">Writer<StringBuffer> writer(sb);</div><div class="line">GenericSchemaValidator<SchemaDocument, Writer<StringBuffer> > validator(s, writer);</div><div class="line">if (!d.Accept(validator)) {</div><div class="line"> // Some problem during Accept(), it may be validation or encoding issues.</div><div class="line"> if (!validator.IsValid()) {</div><div class="line"> // ...</div><div class="line"> }</div><div class="line">}</div></div><!-- fragment --><p>Of course, if your application only needs SAX-style serialization, it can simply send SAX events to <code>SchemaValidator</code> instead of <code>Writer</code>.</p> <h2>Remote Schema</h2> <p>JSON Schema supports <a href="http://spacetelescope.github.io/understanding-json-schema/structuring.html"><code>$ref</code> keyword</a>, which is a <a class="el" href="md_doc_pointer.html">JSON pointer</a> referencing to a local or remote schema. Local pointer is prefixed with <code>#</code>, while remote pointer is an relative or absolute URI. For example:</p> <div class="fragment"><div class="line">{ "$ref": "definitions.json#/address" }</div></div><!-- fragment --><p>As <code>SchemaDocument</code> does not know how to resolve such URI, it needs a user-provided <code>IRemoteSchemaDocumentProvider</code> instance to do so.</p> <div class="fragment"><div class="line">class MyRemoteSchemaDocumentProvider : public IRemoteSchemaDocumentProvider {</div><div class="line">public:</div><div class="line"> virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeTyp length) {</div><div class="line"> // Resolve the uri and returns a pointer to that schema.</div><div class="line"> }</div><div class="line">};</div><div class="line"></div><div class="line">// ...</div><div class="line"></div><div class="line">MyRemoteSchemaDocumentProvider provider;</div><div class="line">SchemaDocument schema(sd, &provider);</div></div><!-- fragment --><h2>Conformance</h2> <p>RapidJSON passed 262 out of 263 tests in <a href="https://github.com/json-schema/JSON-Schema-Test-Suite">JSON Schema Test Suite</a> (Json Schema draft 4).</p> <p>The failed test is "changed scope ref invalid" of "change resolution scope" in <code>refRemote.json</code>. It is due to that <code>id</code> schema keyword and URI combining function are not implemented.</p> <p>Besides, the <code>format</code> schema keyword for string values is ignored, since it is not required by the specification.</p> <h3>Regular Expression</h3> <p>The schema keyword <code>pattern</code> and <code>patternProperties</code> uses regular expression to match the required pattern.</p> <p>RapidJSON implemented a simple NFA regular expression engine, which is used by default. It supports the following syntax.</p> <table class="markdownTable"> <tr class="markdownTableHead"> <th class="markdownTableHeadNone">Syntax </th><th class="markdownTableHeadNone">Description </th></tr> <tr class="markdownTableBody" class="markdownTableRowOdd"> <td class="markdownTableBodyNone"><code>ab</code> </td><td class="markdownTableBodyNone">Concatenation </td></tr> </table> <p>|<code>a|b</code> | Alternation | |<code>a?</code> | Zero or one | |<code>a*</code> | Zero or more | |<code>a+</code> | One or more | |<code>a{3}</code> | Exactly 3 times | |<code>a{3,}</code> | At least 3 times | |<code>a{3,5}</code>| 3 to 5 times | |<code>(ab)</code> | Grouping | |<code>^a</code> | At the beginning | |<code>a$</code> | At the end | |<code>.</code> | Any character | |<code>[abc]</code> | Character classes | |<code>[a-c]</code> | Character class range | |<code>[a-z0-9_]</code> | Character class combination | |<code>[^abc]</code> | Negated character classes | |<code>[^a-c]</code> | Negated character class range | |<code>[\b]</code> | Backspace (U+0008) | |<code>\|</code>, <code>\\</code>, ... | Escape characters | |<code>\f</code> | Form feed (U+000C) | |<code>\n</code> | Line feed (U+000A) | |<code>\r</code> | Carriage return (U+000D) | |<code>\t</code> | Tab (U+0009) | |<code>\v</code> | Vertical tab (U+000B) |</p> <p>For C++11 compiler, it is also possible to use the <code>std::regex</code> by defining <code>RAPIDJSON_SCHEMA_USE_INTERNALREGEX=0</code> and <code>RAPIDJSON_SCHEMA_USE_STDREGEX=1</code>. If your schemas do not need <code>pattern</code> and <code>patternProperties</code>, you can set both macros to zero to disable this feature, which will reduce some code size.</p> <h2>Performance</h2> <p>Most C++ JSON libraries do not yet support JSON Schema. So we tried to evaluate the performance of RapidJSON's JSON Schema validator according to <a href="https://github.com/ebdrup/json-schema-benchmark">json-schema-benchmark</a>, which tests 11 JavaScript libraries running on Node.js.</p> <p>That benchmark runs validations on <a href="https://github.com/json-schema/JSON-Schema-Test-Suite">JSON Schema Test Suite</a>, in which some test suites and tests are excluded. We made the same benchmarking procedure in <a href="test/perftest/schematest.cpp"><code>schematest.cpp</code></a>.</p> <p>On a Mac Book Pro (2.8 GHz Intel Core i7), the following results are collected.</p> <table class="markdownTable"> <tr class="markdownTableHead"> <th class="markdownTableHeadNone">Validator </th><th class="markdownTableHeadCenter">Relative speed </th><th class="markdownTableHeadCenter">Number of test runs per second </th></tr> <tr class="markdownTableBody" class="markdownTableRowOdd"> <td class="markdownTableBodyNone">RapidJSON </td><td class="markdownTableBodyCenter">155% </td><td class="markdownTableBodyCenter">30682 </td></tr> <tr class="markdownTableBody" class="markdownTableRowEven"> <td class="markdownTableBodyNone"><a href="https://github.com/epoberezkin/ajv"><code>ajv</code></a> </td><td class="markdownTableBodyCenter">100% </td><td class="markdownTableBodyCenter">19770 (± 1.31%) </td></tr> <tr class="markdownTableBody" class="markdownTableRowOdd"> <td class="markdownTableBodyNone"><a href="https://github.com/mafintosh/is-my-json-valid"><code>is-my-json-valid</code></a> </td><td class="markdownTableBodyCenter">70% </td><td class="markdownTableBodyCenter">13835 (± 2.84%) </td></tr> <tr class="markdownTableBody" class="markdownTableRowEven"> <td class="markdownTableBodyNone"><a href="https://github.com/bugventure/jsen"><code>jsen</code></a> </td><td class="markdownTableBodyCenter">57.7% </td><td class="markdownTableBodyCenter">11411 (± 1.27%) </td></tr> <tr class="markdownTableBody" class="markdownTableRowOdd"> <td class="markdownTableBodyNone"><a href="https://github.com/AlexeyGrishin/schemasaurus"><code>schemasaurus</code></a> </td><td class="markdownTableBodyCenter">26% </td><td class="markdownTableBodyCenter">5145 (± 1.62%) </td></tr> <tr class="markdownTableBody" class="markdownTableRowEven"> <td class="markdownTableBodyNone"><a href="https://github.com/playlyfe/themis"><code>themis</code></a> </td><td class="markdownTableBodyCenter">19.9% </td><td class="markdownTableBodyCenter">3935 (± 2.69%) </td></tr> <tr class="markdownTableBody" class="markdownTableRowOdd"> <td class="markdownTableBodyNone"><a href="https://github.com/zaggino/z-schema"><code>z-schema</code></a> </td><td class="markdownTableBodyCenter">7% </td><td class="markdownTableBodyCenter">1388 (± 0.84%) </td></tr> <tr class="markdownTableBody" class="markdownTableRowEven"> <td class="markdownTableBodyNone"><a href="https://github.com/pandastrike/jsck#readme"><code>jsck</code></a> </td><td class="markdownTableBodyCenter">3.1% </td><td class="markdownTableBodyCenter">606 (± 2.84%) </td></tr> <tr class="markdownTableBody" class="markdownTableRowOdd"> <td class="markdownTableBodyNone"><a href="https://github.com/tdegrunt/jsonschema#readme"><code>jsonschema</code></a> </td><td class="markdownTableBodyCenter">0.9% </td><td class="markdownTableBodyCenter">185 (± 1.01%) </td></tr> <tr class="markdownTableBody" class="markdownTableRowEven"> <td class="markdownTableBodyNone"><a href="https://github.com/Prestaul/skeemas#readme"><code>skeemas</code></a> </td><td class="markdownTableBodyCenter">0.8% </td><td class="markdownTableBodyCenter">154 (± 0.79%) </td></tr> <tr class="markdownTableBody" class="markdownTableRowOdd"> <td class="markdownTableBodyNone">tv4 </td><td class="markdownTableBodyCenter">0.5% </td><td class="markdownTableBodyCenter">93 (± 0.94%) </td></tr> <tr class="markdownTableBody" class="markdownTableRowEven"> <td class="markdownTableBodyNone"><a href="https://github.com/natesilva/jayschema"><code>jayschema</code></a> </td><td class="markdownTableBodyCenter">0.1% </td><td class="markdownTableBodyCenter">21 (± 1.14%) </td></tr> </table> <p>That is, RapidJSON is about 1.5x faster than the fastest JavaScript library (ajv). And 1400x faster than the slowest one. </p> </div></div><!-- contents --> </div><!-- doc-content --> <!-- HTML footer for doxygen 1.8.7--> <!-- start footer part --> <div id="nav-path" class="navpath"><!-- id is needed for treeview function! --> <ul> </ul> </div> </body> </html>