Skip to content

Commit 591eefa

Browse files
authored
fix(jsonschemagen): avoid duplicate subschemas
Signed-off-by: Silvano Cirujano Cuesta <silvano.cirujano-cuesta@siemens.com>
1 parent 82b98be commit 591eefa

3 files changed

Lines changed: 31 additions & 10 deletions

File tree

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

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,25 @@
5757
}
5858

5959

60+
def _deduplicate_subschemas(subschemas: list["JsonSchema"]) -> list["JsonSchema"]:
61+
"""Return *subschemas* with duplicate entries removed, preserving order.
62+
63+
Two subschemas are considered duplicates when their JSON representations are
64+
identical. This can occur, for example, when multiple ``any_of`` branches
65+
point to different classes whose identifier slot shares the same scalar type
66+
(e.g. both ``Person.id`` and ``Organization.id`` have ``range: string``),
67+
producing redundant ``{"type": "string"}`` entries.
68+
"""
69+
seen: set[str] = set()
70+
result: list[JsonSchema] = []
71+
for schema in subschemas:
72+
key = json.dumps(schema, sort_keys=True)
73+
if key not in seen:
74+
seen.add(key)
75+
result.append(schema)
76+
return result
77+
78+
6079
def _slot_examples_for_json_schema(
6180
examples: list[Example],
6281
*,
@@ -857,19 +876,27 @@ def get_subschema_for_slot(
857876

858877
bool_subschema = JsonSchema()
859878
if slot.any_of is not None and len(slot.any_of) > 0:
860-
bool_subschema["anyOf"] = [self.get_subschema_for_slot(s, include_null=False) for s in slot.any_of]
879+
bool_subschema["anyOf"] = _deduplicate_subschemas(
880+
[self.get_subschema_for_slot(s, include_null=False) for s in slot.any_of]
881+
)
861882
if not slot.required and not prop.is_array and include_null:
862883
bool_subschema["anyOf"].append({"type": "null"})
863884

864885
if slot.all_of is not None and len(slot.all_of) > 0:
865-
bool_subschema["allOf"] = [self.get_subschema_for_slot(s, include_null=False) for s in slot.all_of]
886+
bool_subschema["allOf"] = _deduplicate_subschemas(
887+
[self.get_subschema_for_slot(s, include_null=False) for s in slot.all_of]
888+
)
866889

867890
if slot.exactly_one_of is not None and len(slot.exactly_one_of) > 0:
868-
bool_subschema["oneOf"] = [self.get_subschema_for_slot(s, include_null=False) for s in slot.exactly_one_of]
891+
bool_subschema["oneOf"] = _deduplicate_subschemas(
892+
[self.get_subschema_for_slot(s, include_null=False) for s in slot.exactly_one_of]
893+
)
869894

870895
if slot.none_of is not None and len(slot.none_of) > 0:
871896
bool_subschema["not"] = {
872-
"anyOf": [self.get_subschema_for_slot(s, include_null=False) for s in slot.none_of]
897+
"anyOf": _deduplicate_subschemas(
898+
[self.get_subschema_for_slot(s, include_null=False) for s in slot.none_of]
899+
)
873900
}
874901

875902
if bool_subschema:

tests/linkml/test_scripts/__snapshots__/genjsonschema/meta.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,6 @@
122122
"properties": {
123123
"attribute2": {
124124
"anyOf": [
125-
{
126-
"type": "string"
127-
},
128125
{
129126
"type": "string"
130127
},

tests/linkml/test_scripts/__snapshots__/genjsonschema/meta_inline.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,6 @@
122122
"properties": {
123123
"attribute2": {
124124
"anyOf": [
125-
{
126-
"type": "string"
127-
},
128125
{
129126
"type": "string"
130127
},

0 commit comments

Comments
 (0)