diff --git a/contrib/schemagen/example.schema.json b/contrib/schemagen/example.schema.json index 1bd6180..e1c0f6b 100644 --- a/contrib/schemagen/example.schema.json +++ b/contrib/schemagen/example.schema.json @@ -71,6 +71,24 @@ }, "tree": { "$ref": "#/$defs/Node" + }, + "nullable_hobbies": { + "type": "array", + "items": { + "type": [ + "string", + "null" + ] + } + }, + "nullable_scores": { + "type": "array", + "items": { + "type": [ + "integer", + "null" + ] + } } }, "$defs": { diff --git a/contrib/schemagen/test_gen.cpp b/contrib/schemagen/test_gen.cpp index b507b93..577e983 100644 --- a/contrib/schemagen/test_gen.cpp +++ b/contrib/schemagen/test_gen.cpp @@ -86,7 +86,9 @@ int main() { "value": 1, "children": [ { "value": 2 }, { "value": 3 } ], "next": { "value": 99 } - } + }, + "nullable_hobbies": [null, "math", null, "lace", null], + "nullable_scores": [null, 10, null, 20, null] })"; RootBuilder b; WeaselJsonStatus s = parseStrided(b, json); @@ -116,6 +118,16 @@ int main() { CHECK(r.tree && r.tree->children && (*r.tree->children)[0].value == 2); CHECK(r.tree && r.tree->children && (*r.tree->children)[1].value == 3); CHECK(r.tree && r.tree->next && r.tree->next->value == 99); + CHECK(r.nullable_hobbies.has_value() && r.nullable_hobbies->size() == 5); + CHECK(r.nullable_hobbies && !(*r.nullable_hobbies)[0] && + (*r.nullable_hobbies)[1] && *(*r.nullable_hobbies)[1] == "math" && + !(*r.nullable_hobbies)[2] && (*r.nullable_hobbies)[3] && + *(*r.nullable_hobbies)[3] == "lace" && !(*r.nullable_hobbies)[4]); + CHECK(r.nullable_scores.has_value() && r.nullable_scores->size() == 5); + CHECK(r.nullable_scores && !(*r.nullable_scores)[0] && + (*r.nullable_scores)[1] && *(*r.nullable_scores)[1] == 10 && + !(*r.nullable_scores)[2] && (*r.nullable_scores)[3] && + *(*r.nullable_scores)[3] == 20 && !(*r.nullable_scores)[4]); printf("ok happy path (byte-strided)\n"); } } diff --git a/contrib/schemagen/weaseljson_schemagen.py b/contrib/schemagen/weaseljson_schemagen.py index e7a1a6c..34d6d52 100644 --- a/contrib/schemagen/weaseljson_schemagen.py +++ b/contrib/schemagen/weaseljson_schemagen.py @@ -690,6 +690,34 @@ namespace {ns} {{""" " }" ) + def _is_array_kind(self): + arrs = [n for n, _ in self.arr_types] + if not arrs: + return " bool isArrayKind(Kind) const { return false; }" + cases = " ".join(f"case Kind::{n}:" for n in arrs) + return ( + " bool isArrayKind(Kind k) const {\n" + f" switch (k) {{ {cases} return true; default: return false; }}\n" + " }" + ) + + def _append_null(self): + lines = [ + " void appendNull(Frame &f) {", + " switch (f.kind) {", + ] + for name, tarr in self.arr_types: + vectype = self.base_cpp(tarr) + lines.append(f" case Kind::{name}: {{") + lines.append(f" auto *v = ({vectype} *)f.dest;") + lines.append(" v->emplace_back();") + lines.append(" return;") + lines.append(" }") + lines.append(" default: return;") + lines.append(" }") + lines.append(" }") + return "\n".join(lines) + def _is_strict(self): strict = [n for n, o in self.b.objects.items() if o.strict] if not strict: @@ -964,6 +992,10 @@ private: valueComplete(); }} +{self._is_array_kind()} + +{self._append_null()} + void cbNull() {{ if (error_) return; Frame &f = stack_.back(); @@ -971,6 +1003,7 @@ private: SlotInfo si = slotInfoG(f); if (si.cat == Cat::Skip) {{ valueComplete(); return; }} if (!si.nullable) {{ reject(); return; }} + if (isArrayKind(f.kind)) appendNull(f); // keep null array elements valueComplete(); // leave optional empty / pointer null }}