Repository navigation
Conversation
jdsika
force-pushed
the
feat/jsonschemagen-propertynames-from-key-pattern
branch
from
June 18, 2026 08:25
8a292b2 to
29f18b0
Compare
jdsika
force-pushed
the
feat/jsonschemagen-propertynames-from-key-pattern
branch
2 times, most recently
from
June 18, 2026 13:45
908094b to
b49cce8
Compare
jdsika
force-pushed
the
feat/jsonschemagen-propertynames-from-key-pattern
branch
from
July 3, 2026 13:06
d981f6b to
db6fd2b
Compare
rmessaou
force-pushed
the
feat/jsonschemagen-propertynames-from-key-pattern
branch
from
July 8, 2026 08:49
c33cc3f to
5add490
Compare
This was referenced Jul 10, 2026
jdsika
force-pushed
the
feat/jsonschemagen-propertynames-from-key-pattern
branch
2 times, most recently
from
September 11, 2026 14:02
0ccabd7 to
41c9f93
Compare
jdsika
force-pushed
the
feat/jsonschemagen-propertynames-from-key-pattern
branch
from
October 2, 2026 11:48
41c9f93 to
8fac490
Compare
…nstraints For an inlined-as-dict slot whose range class has an identifier/key slot, render the key slot's string-applicable constraints onto JSON Schema propertyNames (draft-06+) instead of dropping them. In the inlined-dict form the mapping key is the identifier value, so the key slot's constraints constrain the keys. JSON object keys are always strings, so only pattern, enum (equals_string_in) and a string const (equals_string) are emitted; numeric minimum/maximum, numeric const (equals_number) and allOf are excluded -- a numeric const would otherwise reject every key. structured_pattern is honored when materialize_patterns is enabled, consistent with value patterns. Backward compatible: emitted only when a string-applicable key constraint applies. Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de> Includes the documentation for the emitted propertyNames constraint. Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
jdsika
force-pushed
the
feat/jsonschemagen-propertynames-from-key-pattern
branch
from
October 14, 2026 07:19
8fac490 to
a228736
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Mirror of linkml#3716, same head. Review there; this PR tracks the fork stack.
Summary
For an inlined-as-dict slot (
inlined: true,inlined_as_list: false) whose range classhas an
identifier/keyslot, emit that key slot's string-applicable constraints ontoJSON Schema
propertyNames. Previously these constraints were silently dropped, so thegenerated schema was strictly looser than the model declares — any key string was accepted.
"entries": { "type": "object", "additionalProperties": {"$ref": "#/$defs/Entry__identifier_optional"}, + "propertyNames": {"pattern": "^[0-9]+$"} }Before this change
{"entries": {"banana": {...}}}validated, despite the model declaringthat identifiers are digit strings.
Why (grounded at both ends)
inlined-dict form the dictionary key is the identifier value of the range class and
is not duplicated inside each value object. So constraints declared on the identifier/key
slot are, by LinkML's own serialization, constraints on the object keys.
propertyNames, Applicator vocabulary) —the subschema is applied to each property name, and "the value of each property name will
be a string."
propertyNamescomposes conjunctively with the existingadditionalPropertiesvalue schema, so keys and values are constrained independently.gen-json-schemaemits"$schema": .../draft/2019-09/schema;propertyNameshas existedsince draft-06, so it is safe for the emitted dialect.
What is emitted
A new public helper,
JsonSchemaGenerator.get_key_constraints_for_slot(), filters theexisting
get_value_constraints_for_slot()result down to the subset that can be satisfiedby a string:
propertyNameskeywordpattern— written on the slot, resolved from astructured_pattern, or inherited from the slot'srangetypepatternequals_string_inenum(string values only)equals_string(a string)constIntentionally excluded, because object keys are always strings:
const(equals_number) — would reject every key;minimum/maximum— unsatisfiable by a string;allOf(fromrange_expression), and the permissible values of anenum-rangedidentifier — out of scope, no
propertyNamesis emitted for those.The filtering is by value type, not by originating keyword (
isinstance(..., str)forpattern/const, all-strings forenum), so a future non-string constraint source cannotsilently produce a
propertyNamesschema that rejects every key.Note on type-derived patterns
Because
get_value_constraints_for_slot()also resolves the slot's type, an identifierwhose range carries a pattern — e.g.
range: ncname, or a user-definedtypeof: stringwith apattern— now gains apropertyNamesentry even when the slotitself declares no constraint:
This is deliberate and is the same pattern already applied to the identifier inside the
value object (
Entry__identifier_optional.properties.key.pattern), so a bare key and aredundantly repeated in-object identifier are now validated identically. The generated
schema becomes stricter only in ways the model already required.
Compatibility
propertyNamesis emitted only when the key slot yields at least one string-applicableconstraint; otherwise the output is unchanged. To check this empirically I regenerated JSON
Schema for the 139 schemas under
tests/linkml/test_generators/input,tests/linkml/test_validator/inputandexamples/onmainand on this branch:0 files differ. The broader suite (
test_generators,test_validator,test_issues)is also green: 2120 passed, 54 skipped, 4 xfailed.
Only the inlined-as-dict form is affected; inlined-as-list and non-inlined references are
untouched.
How was this tested?
13 new tests in
tests/linkml/test_generators/test_jsonschemagen.py:Positive
patternon the key slot →propertyNames.pattern, parametrized overidentifier: trueand
key: true;equals_string_in→propertyNames.enum; a stringequals_string→propertyNames.const;structured_pattern→ resolved and emitted;ncnamepattern and a user-defined type, asserting the emitted key pattern is identicalto the one on the in-object identifier.
Behavioral (
jsonschema.validate)ValidationError, forpattern,string
constandstructured_pattern.Negative controls
const(equals_number) and numericminimum/maximumemit nopropertyNames, so numeric-looking string keys are not wrongly rejected;enum-ranged identifier emits nopropertyNames(documented boundary);get_key_constraints_for_slot()directly, covering a non-stringconst,a numeric bound, and
None.test_jsonschemagen.py: 181 passed, 1 skipped, 261 subtests passed.ruff checkandruff format --checkare clean.Notes for reviewers
main, and the two original commits (feature + docs) squashedinto one. The conflict in
test_jsonschemagen.pywas an append-at-end collision; bothsides' tests are kept, verified by reconstructing the file from upstream plus this PR's
162 appended lines.
bc06f0f5(Resolve structured patterns when computing induced slots and types)materialize_patternsis deprecated and structured patterns resolve automatically. Theearlier revision of this PR gated
structured_patternbehind that flag; that test wasrewritten to assert the current behaviour and no longer uses the deprecated option.
Checklist
AI Assistance
If you used AI tools while preparing this PR, you are still the author and responsible for
understanding, verifying, and defending your submission. Please engage with reviewers
personally rather than through your agent during feedback and revisions. See our
AI Covenant for details.