forked from weaselab/weaseljson
schemagen: keep null elements in arrays with nullable item types
For array types whose items are nullable ({"type": ["T", "null"]}),
the generated builder previously called valueComplete() on cbNull() without
appending anything to the owning vector. Null entries were silently dropped,
so vector indices no longer matched JSON array indices.
Generate isArrayKind() / appendNull() helpers and have cbNull() append a
default-constructed element when the current frame is an array. For
std::optional<T> items this appends an empty optional; for std::unique_ptr<T>
items it appends a null pointer. Add nullable string/integer array fields to
the example schema and test coverage to verify indices are preserved.
Fixes #5
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user