Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON schemas with circular references #9435

Closed
3 of 13 tasks
lord-haffi opened this issue May 14, 2024 · 1 comment
Closed
3 of 13 tasks

JSON schemas with circular references #9435

lord-haffi opened this issue May 14, 2024 · 1 comment

Comments

@lord-haffi
Copy link

Initial Checks

  • I have searched Google & GitHub for similar requests and couldn't find anything
  • I have read and followed the docs and still think this feature is missing

Description

When using models with circular references, the generated JSON-schema (using model_json_schema) has an unexpected output (at least for me). Instead of defining the model on the root there will be an allOf present with a reference to the model in $defs. I believe it was solved this way to avoid redundance since the model must be present in $defs in order to correctly model the circular reference. However, I think this redundance sometimes can be helpful. Currently I'm modifying the result schema by myself to achieve this. I think an option for this on model_json_schema would be best. Or at least a short description in the documentation.

import json
import re
from pydantic import BaseModel

class A(BaseModel):
    b: "B"
    c: str

class B(BaseModel):
    a: A

A.model_rebuild()
result_schema = A.model_json_schema()
# result_schema = {
#   "$defs": {
#     "A": {
#       "properties": {
#         "b": {
#           "$ref": "#/$defs/B"
#         },
#         "c": {
#           "title": "C",
#           "type": "string"
#         }
#       },
#       "required": [
#         "b",
#         "c"
#       ],
#       "title": "A",
#       "type": "object"
#     },
#     "B": {
#       "properties": {
#         "a": {
#           "$ref": "#/$defs/A"
#         }
#       },
#       "required": [
#         "a"
#       ],
#       "title": "B",
#       "type": "object"
#     }
#   },
#   "allOf": [
#     {
#       "$ref": "#/$defs/A"
#     }
#   ]
# }
if {"allOf", "$defs"} == set(result_schema.keys()):
    assert (
        len(result_schema["allOf"]) == 1
    ), "Internal error: Assumed circular reference but structure is unexpected"
    # This is the case for schemas containing circular references
    reference_pattern = re.compile(r"^#/\$defs/(?P<cls_name>\w+)$")
    reference_match = reference_pattern.fullmatch(result_schema["allOf"][0]["$ref"])
    result_schema_to_merge = result_schema["$defs"][reference_match.group("cls_name")]
    del result_schema["allOf"]
    result_schema.update(result_schema_to_merge)
# result_schema = {
#   "$defs": {
#     "A": {
#       "properties": {
#         "b": {
#           "$ref": "#/$defs/B"
#         },
#         "c": {
#           "title": "C",
#           "type": "string"
#         }
#       },
#       "required": [
#         "b",
#         "c"
#       ],
#       "title": "A",
#       "type": "object"
#     },
#     "B": {
#       "properties": {
#         "a": {
#           "$ref": "#/$defs/A"
#         }
#       },
#       "required": [
#         "a"
#       ],
#       "title": "B",
#       "type": "object"
#     }
#   },
#   "properties": {
#     "b": {
#       "$ref": "#/$defs/B"
#     },
#     "c": {
#       "title": "C",
#       "type": "string"
#     }
#   },
#   "required": [
#     "b",
#     "c"
#   ],
#   "title": "A",
#   "type": "object"
# }

Affected Components

@sydney-runkle
Copy link
Member

Hi @lord-haffi,

Thanks for your question - indeed, we simplify the schema in this way to represent the circular reference without unneeded redundancy.

Modification of the schema in the way that you're requesting seems relatively unique, so I think these changes belong in user code for now. That being said, if requests such as this one get significant traction, we could reconsider adding increased support (or at least examples for how to achieve such schema to the docs).

Closing this as not planned, based on that analysis, for now.

@sydney-runkle sydney-runkle closed this as not planned Won't fix, can't repro, duplicate, stale May 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants