Reviewed-on: #58
weaseljson schemagen
Generates a C++ type and a streaming builder parser from a JSON Schema, on top of weaseljson.
The builder copies incoming bytes straight into their final destinations in the
result struct as they arrive (no intermediate DOM). When parsing completes you
take() ownership of the result.
python3 weaseljson_schemagen.py schema.json -o parsed.h --namespace myschema
Usage
#include "parsed.h"
using namespace myschema;
RootBuilder b;
WeaselJsonStatus s = b.feed(buf, len); // call repeatedly with chunks; buf may
// be modified in place (unescaping)
if (s == WeaselJson_AGAIN) s = b.finish();
if (s == WeaselJson_OK) {
Root value = b.take(); // ownership of the parsed result
}
feed/finish return the usual WeaselJsonStatus; WeaselJson_OK means the
document is both valid JSON and schema-valid, and WeaselJson_REJECT covers
both malformed JSON and schema violations. The builder holds interior pointers
into the result, so it is non-movable.
Schema -> C++ mapping
| JSON Schema | C++ |
|---|---|
object with properties |
struct |
| required property | value member |
| non-required property | std::optional<T> |
["T", "null"] (nullable) |
std::optional<T> (accepts null) |
string / integer / number / boolean |
std::string / int64_t / double / bool |
enum of strings |
enum class : int |
array with items |
std::vector<T> |
$ref to $defs/definitions |
the referenced named struct |
recursive $ref |
std::unique_ptr<T> (cycle broken) |
additionalProperties: false |
unknown keys rejected |
additionalProperties absent / true |
not supported (rejected at generation) |
Schema violations (rejected at parse time)
- missing required property (top-level or nested)
- wrong JSON type for a property / array element
nullfor a non-nullable slot- value not in a string
enum - a number not representable in the target type (e.g.
1.5for aninteger) - duplicate object keys
- unknown key in any object
Not supported (rejected at generation time, no fallback)
oneOf / anyOf / allOf / not / if-then-else,
patternProperties, additionalProperties absent or set to true,
additionalProperties with a schema (typed map), prefixItems (tuples),
const,
dependentSchemas/dependentRequired, union type lists other than
["T", "null"], non-string enums, and remote ($ref to other documents).
Notes
- Strings (the bulk payload) are appended directly into their destination
std::string, even when split acrossfeedcalls. Numbers are accumulated in a small scratch buffer and converted on completion, since anint64_t/doublecannot hold partial digits. test_gen.cppgenerates fromexample.schema.jsonand exercises the parser byte-by-byte (covering chunked strings/numbers), plus the rejection cases.
Testing
The schemagen tests are registered with CTest and run as part of the default
ctest invocation from the build directory:
cmake -S . -B build
make -C build -j "$(nproc)"
cd build
ctest --output-on-failure
For local development you can also use the convenience script:
cd contrib/schemagen
./run_tests.sh
Both regenerate the example parser (gen.h) and a regression parser with 40
required properties (big.h), compile test_gen.cpp and test_big.cpp, and run
them. test_big.cpp specifically covers issue #3: it checks that a 40-property
object accepts all fields, rejects a missing field at index 32, and rejects
duplicate keys around the 32-bit boundary.