Skip to content

Commit beb4eb3

Browse files
committed
feat(jsonschemagen): add --include-null/--no-include-null CLI option
Expose the existing JsonSchemaGenerator.include_null field on the gen-json-schema CLI. --no-include-null forbids explicit JSON null in optional slots so optionality is expressed only via absence from required (JSON Schema Validation 6.5.3), keeping the bare value type (6.1.1) — needed for strict parity with reference schemas that forbid null. Default unchanged (include_null=True). Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
1 parent a660a53 commit beb4eb3

2 files changed

Lines changed: 47 additions & 0 deletions

File tree

packages/linkml/src/linkml/generators/jsonschemagen.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,17 @@ def serialize(self, **kwargs) -> str:
10311031
show_default=True,
10321032
help="If set, expand subproperty_of constraints to enum constraints.",
10331033
)
1034+
@click.option(
1035+
"--include-null/--no-include-null",
1036+
default=True,
1037+
show_default=True,
1038+
help="""
1039+
If set (default), optional slots accept an explicit JSON null (type is emitted as
1040+
["<type>", "null"]). Use --no-include-null to forbid explicit null, so optionality
1041+
is expressed solely by absence from "required" — required for strict parity with a
1042+
reference schema that declares a bare type (JSON Schema Validation 6.1.1 / 6.5.3).
1043+
""",
1044+
)
10341045
@click.version_option(__version__, "-V", "--version")
10351046
def cli(yamlfile, **kwargs):
10361047
"""Generate JSON Schema representation of a LinkML model"""

tests/linkml/test_generators/test_jsonschemagen.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,3 +1090,39 @@ def test_add_lax_def_missing_required():
10901090
schema["$defs"]["NormalClass"] = {"type": "object", "properties": {"id": {}}, "required": ["id", "name"]}
10911091
schema.add_lax_def("NormalClass", "id")
10921092
assert schema["$defs"]["NormalClass__identifier_optional"]["required"] == ["name"]
1093+
1094+
1095+
def test_no_include_null_omits_null_from_optional_slots():
1096+
"""``include_null=False`` must drop ``null`` from optional slot types.
1097+
1098+
By default an optional (non-required) slot is rendered as ``["<type>", "null"]``
1099+
so an explicit JSON ``null`` validates. With ``include_null=False`` optionality is
1100+
expressed solely by absence from ``required`` (JSON Schema Validation 6.5.3), and
1101+
the slot keeps its bare type (6.1.1) — required for strict parity with reference
1102+
schemas that forbid ``null``.
1103+
"""
1104+
schema = """
1105+
id: https://example.org/test-include-null
1106+
name: test-include-null
1107+
prefixes:
1108+
linkml: https://w3id.org/linkml/
1109+
default_range: string
1110+
imports:
1111+
- linkml:types
1112+
classes:
1113+
C:
1114+
attributes:
1115+
opt:
1116+
range: string
1117+
req:
1118+
range: string
1119+
required: true
1120+
"""
1121+
with_null = json.loads(JsonSchemaGenerator(schema, include_null=True).serialize())
1122+
without_null = json.loads(JsonSchemaGenerator(schema, include_null=False).serialize())
1123+
1124+
assert with_null["$defs"]["C"]["properties"]["opt"]["type"] == ["string", "null"]
1125+
assert without_null["$defs"]["C"]["properties"]["opt"]["type"] == "string"
1126+
# required slots are unaffected either way
1127+
assert without_null["$defs"]["C"]["properties"]["req"]["type"] == "string"
1128+
assert with_null["$defs"]["C"]["properties"]["req"]["type"] == "string"

0 commit comments

Comments
 (0)