CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Successful in 57s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Successful in 50s
CI / pre-commit (pull_request) Successful in 52s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Successful in 1m41s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Successful in 1m29s
Per the README contract, an object schema without an explicit `additionalProperties` was documented as rejected at generation time, but the code defaulted it to `false` and silently generated a strict parser that rejects valid documents with extra properties. Make the code match the documented contract by raising GenError when `additionalProperties` is absent, with a clear message instructing the author to set it explicitly to false. Update the README prose section to list absent additionalProperties alongside true, and fix existing schemas and tests to declare additionalProperties explicitly. Closes #55
102 lines
4.1 KiB
Markdown
102 lines
4.1 KiB
Markdown
# 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>` |
|
|
| `["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
|
|
- `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.
|