# 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. ```sh python3 weaseljson_schemagen.py schema.json -o parsed.h --namespace myschema ``` ## Usage ```cpp #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", "null"]` (nullable) | `std::optional` (accepts `null`) | | `string` / `integer` / `number` / `boolean` | `std::string` / `int64_t` / `double` / `bool` | | `enum` of strings | `enum class : int` | | `array` with `items` | `std::vector` | | `$ref` to `$defs`/`definitions` | the referenced named struct | | recursive `$ref` | `std::unique_ptr` (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 - `null` for a non-nullable slot - value not in a string `enum` - a number not representable in the target type (e.g. `1.5` for an `integer`) - 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 across `feed` calls. Numbers are accumulated in a small scratch buffer and converted on completion, since an `int64_t`/`double` cannot hold partial digits. - `test_gen.cpp` generates from `example.schema.json` and 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: ```sh 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: ```sh 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.