|
57 | 57 | } |
58 | 58 |
|
59 | 59 |
|
| 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 | + |
60 | 79 | def _slot_examples_for_json_schema( |
61 | 80 | examples: list[Example], |
62 | 81 | *, |
@@ -857,19 +876,27 @@ def get_subschema_for_slot( |
857 | 876 |
|
858 | 877 | bool_subschema = JsonSchema() |
859 | 878 | 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 | + ) |
861 | 882 | if not slot.required and not prop.is_array and include_null: |
862 | 883 | bool_subschema["anyOf"].append({"type": "null"}) |
863 | 884 |
|
864 | 885 | 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 | + ) |
866 | 889 |
|
867 | 890 | 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 | + ) |
869 | 894 |
|
870 | 895 | if slot.none_of is not None and len(slot.none_of) > 0: |
871 | 896 | 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 | + ) |
873 | 900 | } |
874 | 901 |
|
875 | 902 | if bool_subschema: |
|
0 commit comments