Repository: cercide/fastapi-xml (language: python)

Issue:
Reenable schema generation
I think this one is obvious.


Discussion / hints:

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 828601a..eed26e9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -15,7 +15,7 @@ repos:
       - id: trailing-whitespace
       - id: end-of-file-fixer
       - id: debug-statements
-  - repo: https://gitlab.com/pycqa/flake8
+  - repo: https://github.com/pycqa/flake8
     rev: 5.0.4
     hooks:
       - id: flake8
@@ -25,11 +25,11 @@ repos:
           flake8-comprehensions,
         ]
   - repo: https://github.com/pre-commit/mirrors-mypy
-    rev: v0.971
+    rev: 'v1.8.0'
     hooks:
       - id: mypy
         additional_dependencies: [fastapi, pydantic, xsdata]
-        args: ["--strict", "--no-warn-return-any", "--implicit-reexport"]
+        args: ["--strict", "--no-warn-return-any", "--implicit-reexport", "--exclude", "test_.*"]
   - repo: https://github.com/myint/docformatter
     rev: v1.5.0
     hooks:
@@ -40,7 +40,7 @@ repos:
     hooks:
       - id: doc8
   - repo: https://github.com/python-poetry/poetry
-    rev: '1.2.2'
+    rev: '1.7.1'
     hooks:
       - id: poetry-check
       - id: poetry-lock
diff --git a/fastapi_xml/__init__.py b/fastapi_xml/__init__.py
index 5b37bf1..6caa5a3 100644
--- a/fastapi_xml/__init__.py
+++ b/fastapi_xml/__init__.py
@@ -1,26 +1,16 @@
 """This packaged adds xml support to :mod:`fastapi`."""
-from . import nonjson
-from . import xmlbody
-from .nonjson import add_openapi_extension
-from .nonjson import NonJsonResponse
-from .nonjson import NonJsonRoute
-from .xmlbody import XmlAppResponse
+from .openapi import add_openapi_extension
+from .response import XmlAppResponse
+from .response import XmlTextResponse
+from .route import XmlRoute
 from .xmlbody import XmlBody
-from .xmlbody import XmlResponse
-from .xmlbody import XmlTextResponse
 
 __all__ = [
-    "nonjson",
-    "xmlbody",
-    "NonJsonResponse",
-    "NonJsonRoute",
-    "XmlResponse",
+    "XmlRoute",
     "XmlTextResponse",
     "XmlAppResponse",
     "XmlBody",
     "add_openapi_extension",
 ]
 
-__version__ = "1.0.0b1"
-
-nonjson.OPENAPI_SCHEMA_MODIFIER.append(xmlbody.add_openapi_xml_schema)
+__version__ = "1.1.0"
diff --git a/fastapi_xml/decoder.py b/fastapi_xml/decoder.py
new file mode 100644
index 0000000..5be3d0b
--- /dev/null
+++ b/fastapi_xml/decoder.py
@@ -0,0 +1,69 @@
+from dataclasses import asdict
+from dataclasses import is_dataclass
+from typing import Any
+from typing import Callable
+from typing import ClassVar
+from typing import Dict
+from typing import Optional
+
+from fastapi._compat import ModelField
+from starlette.requests import Request
+from xsdata.exceptions import ParserError
+from xsdata.formats.dataclass.context import XmlContext
+from xsdata.formats.dataclass.parsers import XmlParser
+
+DEFAULT_XML_CONTEXT: XmlContext = XmlContext()
+
+__all__ = ["BodyDecodeError", "XmlDecoder"]
+
+
+class BodyDecodeError(ValueError):
+    pass
+
+
+class XmlDecoder:
+    xml_parser_factory: ClassVar[Callable[[], XmlParser]] = lambda: XmlParser(
+        context=DEFAULT_XML_CONTEXT
+    )
+    xml_parser: ClassVar[Optional[XmlParser]] = None
+
+    @classmethod
+    def get_parser(cls) -> XmlParser:
+        """
+        The get_parser function is a class method that returns an instance of
+        the XmlParser class. The first time it is called, it creates an
+        instance and stores it in the xml_parser attribute. Subsequent calls
+        return this same object.
+
+        :return: An instance of XmlParser
+        """
+        if cls.xml_parser is None:
+            cls.xml_parser = cls.xml_parser_factory()
+        return cls.xml_parser
+
+    @classmethod
+    def decode(
+        cls, request: Request, model_field: ModelField, body: bytes
+    ) -> Optional[Dict[str, Any]]:
+        """
+        This method decodes an xml body.
+
+        :param request: the original request
+        :param model_field:   the model field to deal with
+        :param body:    the original http body
+
+
+        :return: The Decoder MUST return None, if the decoding failed for any reason.
+                Else, it MUST return a mapping for pydantic's constructor
+        """
+        xml_parser = cls.get_parser()
+        clazz = model_field.type_
+        if not is_dataclass(clazz):
+            return None
+
+        try:
+            result: object = xml_parser.from_bytes(body, clazz=clazz)
+        except ParserError as e:
+            raise BodyDecodeError(str(e)) from e
+        else:
+            return asdict(result)  # type: ignore
diff --git a/fastapi_xml/nonjson.py b/fastapi_xml/nonjson.py
deleted file mode 100644
index e5183d1..0000000
--- a/fastapi_xml/nonjson.py
+++ /dev/null
@@ -1,822 +0,0 @@
-import asyncio
-import email.message
-import json
-import sys
-import traceback
-from abc import abstractmethod
-from types import MethodType
-from typing import Any
-from typing import Callable
-from typing import ClassVar
-from typing import Coroutine
-from typing import Dict
-from typing import Iterable
-from typing import List
-from typing import Mapping
-from typing import Optional
-from typing import Tuple
-from typing import Type
-from typing import Union
-
-from fastapi import FastAPI
-from fastapi import params
-from fastapi.datastructures import Default
-from fastapi.datastructures import DefaultPlaceholder
-from fastapi.datastructures import FormData
-from fastapi.dependencies.models import Dependant
-from fastapi.dependencies.utils import solve_dependencies
-from fastapi.encoders import DictIntStrAny
-from fastapi.encoders import jsonable_encoder
-from fastapi.encoders import SetIntStr
-from fastapi.exceptions import RequestValidationError
-from fastapi.openapi.utils import get_openapi
-from fastapi.openapi.utils import OpenAPI
-from fastapi.routing import APIRoute
-from fastapi.routing import run_endpoint_function
-from fastapi.routing import serialize_response
-from pydantic.error_wrappers import ErrorWrapper
-from pydantic.fields import ModelField
-from pydantic.fields import Undefined
-from starlette.background import BackgroundTasks
-from starlette.exceptions import HTTPException
-from starlette.requests import Request
-from starlette.responses import JSONResponse
-from starlette.responses import Response
-
-OpenApiSchemaModifier = Callable[[FastAPI, OpenAPI, Optional[Mapping[str, Any]]], bool]
-
-OPENAPI_SCHEMA_MODIFIER: List[OpenApiSchemaModifier] = []
-
-
-class BodyDecodeError(ValueError):
-    pass
-
-
-class NonJsonRoute(APIRoute):
-    def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]:
-        return self.get_request_handler(
-            dependant=self.dependant,
-            body_field=self.body_field,
-            status_code=self.status_code,
-            response_class=self.response_class,
-            response_field=self.secure_cloned_response_field,
-            response_model_include=self.response_model_include,
-            response_model_exclude=self.response_model_exclude,
-            response_model_by_alias=self.response_model_by_alias,
-            response_model_exclude_unset=self.response_model_exclude_unset,
-            response_model_exclude_defaults=self.response_model_exclude_defaults,
-            response_model_exclude_none=self.response_model_exclude_none,
-            dependency_overrides_provider=self.dependency_overrides_provider,
-        )
-
-    @staticmethod
-    async def _original_fastapi_body_decode(
-        request: Request,
-        is_body_form: bool,
-        body_field: Optional[ModelField],
-    ) -> Union[Dict[str, Any], FormData, None]:  # pragma: nocover
-        # Repository: https://github.com/tiangolo/fastapi
-        # fastapi's license copy is blow.
-        #
-        # The MIT License (MIT)
-        #
-        # Copyright (c) 2018 Sebastián Ramírez
-        #
-        # Permission is hereby granted, free of charge, to any person obtaining a copy
-        # of this software and associated documentation files (the "Software"), to deal
-        # in the Software without restriction, including without limitation the rights
-        # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-        # copies of the Software, and to permit persons to whom the Software is
-        # furnished to do so, subject to the following conditions:
-        #
-        # The above copyright notice and this permission notice shall be included in
-        # all copies or substantial portions of the Software.
-        #
-        # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-        # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-        # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-        # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-        # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-        # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-        # THE SOFTWARE.
-        try:
-            body: Any = None
-            if body_field:
-                if is_body_form:
-                    body = await request.form()
-                else:
-                    body_bytes = await request.body()
-                    if body_bytes:
-                        json_body: Any = Undefined
-                        content_type_value = request.headers.get("content-type")
-                        if not content_type_value:
-                            json_body = await request.json()
-                        else:
-                            message = email.message.Message()
-                            message["content-type"] = content_type_value
-                            if message.get_content_maintype() == "application":
-                                subtype = message.get_content_subtype()
-                                if subtype == "json" or subtype.endswith("+json"):
-                                    json_body = await request.json()
-                        if json_body != Undefined:
-                            body = json_body
-                        else:
-                            body = body_bytes
-        except json.JSONDecodeError as e:  # pragma: no cover
-            raise RequestValidationError([ErrorWrapper(e, ("body", e.pos))], body=e.doc)
-        except Exception as e:  # pragma: no cover
-            raise HTTPException(
-                status_code=400, detail="There was an error parsing the body"
-            ) from e
-        return body
-
-    @staticmethod
-    async def _original_fastapi_return_response(
-        raw_response: Any,
-        background_tasks: Optional[BackgroundTasks],
-        sub_response: Response,
-        is_coroutine: bool,
-        actual_response_class: "Type[Response]",
-        status_code: Optional[int] = None,
-        response_field: Optional[ModelField] = None,
-        response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
-        response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
-        response_model_by_alias: bool = True,
-        response_model_exclude_unset: bool = False,
-        response_model_exclude_defaults: bool = False,
-        response_model_exclude_none: bool = False,
-    ) -> Response:  # pragma: nocover
-        # Repository: https://github.com/tiangolo/fastapi
-        # fastapi's license copy is blow.
-        #
-        # The MIT License (MIT)
-        #
-        # Copyright (c) 2018 Sebastián Ramírez
-        #
-        # Permission is hereby granted, free of charge, to any person obtaining a copy
-        # of this software and associated documentation files (the "Software"), to deal
-        # in the Software without restriction, including without limitation the rights
-        # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-        # copies of the Software, and to permit persons to whom the Software is
-        # furnished to do so, subject to the following conditions:
-        #
-        # The above copyright notice and this permission notice shall be included in
-        # all copies or substantial portions of the Software.
-        #
-        # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-        # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-        # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-        # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-        # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-        # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-        # THE SOFTWARE.
-        if isinstance(raw_response, Response):
-            if raw_response.background is None:
-                raw_response.background = background_tasks
-            return raw_response
-
-        response_data = await serialize_response(
-            field=response_field,
-            response_content=raw_response,
-            include=response_model_include,
-            exclude=response_model_exclude,
-            by_alias=response_model_by_alias,
-            exclude_unset=response_model_exclude_unset,
-            exclude_defaults=response_model_exclude_defaults,
-            exclude_none=response_model_exclude_none,
-            is_coroutine=is_coroutine,
-        )
-        response_args: Dict[str, Any] = {"background": background_tasks}
-        # If status_code was set, use it, otherwise use the default from the
-        # response class, in the case of redirect it's 307
-        if status_code is not None:
-            response_args["status_code"] = status_code
-        response = actual_response_class(response_data, **response_args)
-        response.headers.raw.extend(sub_response.headers.raw)
-        if sub_response.status_code:
-            response.status_code = sub_response.status_code
-        return response
-
-    @staticmethod
-    def _original_fastapi_prepare_request_handler(
-        *,
-        dependant: Dependant,
-        body_field: Optional[ModelField],
-        response_class: Union["Type[Response]", DefaultPlaceholder],
-    ) -> Tuple[bool, bool, "Type[Response]"]:
-        # Repository: https://github.com/tiangolo/fastapi
-        #
-        # The MIT License (MIT)
-        #
-        # Copyright (c) 2018 Sebastián Ramírez
-        #
-        # Permission is hereby granted, free of charge, to any person obtaining a copy
-        # of this software and associated documentation files (the "Software"), to deal
-        # in the Software without restriction, including without limitation the rights
-        # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-        # copies of the Software, and to permit persons to whom the Software is
-        # furnished to do so, subject to the following conditions:
-        #
-        # The above copyright notice and this permission notice shall be included in
-        # all copies or substantial portions of the Software.
-        #
-        # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-        # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-        # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-        # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-        # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-        # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-        # THE SOFTWARE.
-        assert dependant.call is not None, "dependant.call must be a function"
-        is_coroutine = asyncio.iscoroutinefunction(dependant.call)
-        is_body_form = body_field is not None and isinstance(
-            body_field.field_info, params.Form
-        )
-        if isinstance(response_class, DefaultPlaceholder):
-            actual_response_class: "Type[Response]" = (
-                response_class.value
-            )  # pragma: no cover
-        else:
-            actual_response_class = response_class
-        return is_coroutine, is_body_form, actual_response_class
-
-    @staticmethod
-    async def _original_fastapi_call_endpoint(
-        *,
-        request: Request,
-        dependant: Dependant,
-        is_coroutine: bool,
-        body: Optional[Union[Dict[str, Any], FormData]],
-        dependency_overrides_provider: Optional[Any] = None,
-    ) -> Tuple[Any, Optional[BackgroundTasks], Response]:  # pragma: no cover
-
-        # Repository: https://github.com/tiangolo/fastapi
-        #
-        # The MIT License (MIT)
-        #
-        # Copyright (c) 2018 Sebastián Ramírez
-        #
-        # Permission is hereby granted, free of charge, to any person obtaining a copy
-        # of this software and associated documentation files (the "Software"), to deal
-        # in the Software without restriction, including without limitation the rights
-        # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-        # copies of the Software, and to permit persons to whom the Software is
-        # furnished to do so, subject to the following conditions:
-        #
-        # The above copyright notice and this permission notice shall be included in
-        # all copies or substantial portions of the Software.
-        #
-        # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-        # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-        # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-        # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-        # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-        # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-        # THE SOFTWARE.
-        solved_result = await solve_dependencies(
-            request=request,
-            dependant=dependant,
-            body=body,
-            dependency_overrides_provider=dependency_overrides_provider,
-        )
-        values, errors, background_tasks, sub_response, _ = solved_result
-        if errors:
-            raise RequestValidationError(errors, body=body)
-        else:
-            raw_response = await run_endpoint_function(
-                dependant=dependant, values=values, is_coroutine=is_coroutine
-            )
-            return raw_response, background_tasks, sub_response
-
-    @staticmethod
-    async def _request_handler(
-        *,
-        request: Request,
-        dependant: Dependant,
-        is_body_form: bool,
-        body_field: Optional[ModelField],
-        is_coroutine: bool,
-        actual_response_class: "Type[Response]",
-        status_code: Optional[int] = None,
-        response_field: Optional[ModelField] = None,
-        response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
-        response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
-        response_model_by_alias: bool = True,
-        response_model_exclude_unset: bool = False,
-        response_model_exclude_defaults: bool = False,
-        response_model_exclude_none: bool = False,
-        dependency_overrides_provider: Optional[Any] = None,
-    ) -> Response:
-        """
-        .. testsetup::
-
-            >>> from dataclasses import field
-            >>> from dataclasses import is_dataclass
-            >>> from dataclasses import dataclass
-            >>> from fastapi_xml import XmlBody
-            >>> from fastapi_xml import XmlAppResponse
-            >>> from fastapi import FastAPI
-            >>> from fastapi import Request
-            >>> from pydantic.config import BaseConfig
-            >>> from xsdata.formats.dataclass.parsers import XmlParser
-            >>> from xsdata.formats.dataclass.serializers import XmlSerializer
-            >>> current_decoder = BodyDecoder.__decoder__
-            >>> current_decoder_map = BodyDecoder.__content_type__
-            >>> BodyDecoder.__decoder__ = []
-            >>> BodyDecoder.__content_type__ = {}
-
-        .. doctest:: Test Scope
-
-            >>> class TestResponse(NonJsonResponse):
-            ...     media_type = "application/test"
-            ...     def render(self, content: Any) -> bytes:
-            ...        assert isinstance(content, Dummy)
-            ...        return b'{"x": content.x}'
-
-            >>> class TestDecoder(BodyDecoder):
-            ...     supported_content_type = [TestResponse.media_type]
-            ...
-            ...     @classmethod
-            ...     def decode(
-            ...         cls, request: Request, field: ModelField, body: bytes
-            ...     ) -> Optional[Dict[str, Any]]:
-            ...         if body == b"raise BodyDecodeError":
-            ...             raise BodyDecodeError("body decode exception test")
-            ...         elif body == b"raise any":
-            ...             raise Exception("something bad happened")
-            ...         else:
-            ...             return json.loads(body.decode())
-            >>> BodyDecoder.register(TestDecoder)
-
-            >>> @dataclass
-            ... class Dummy:
-            ...    x: str
-
-            >>> def dummy_endpoint(x: Dummy) -> Dummy:
-            ...     assert type(x) == Dummy
-            ...     x.x = "success"
-            ...     return x
-
-            >>> test_scope = {
-            ...     "type": "http",
-            ...     "query_string": "",
-            ...     "headers": [(b"content-type", TestResponse.media_type.encode())]
-            ... }
-            >>> model_field = ModelField(
-            ...     name="x",
-            ...     type_=Dummy,
-            ...     model_config=BaseConfig,
-            ...     class_validators=None
-            ... )
-            >>> test_dependant = Dependant(
-            ...     body_params=[model_field],
-            ...     call=dummy_endpoint,
-            ... )
-
-        .. doctest:: Test valid request
-
-            >>> test_request = Request(scope=test_scope)
-            >>> test_request._body = b'{"x": "test"}'
-            >>> test_rq_handler = NonJsonRoute._request_handler(
-            ...     dependant=test_dependant,
-            ...     request=test_request,
-            ...     is_coroutine=False,
-            ...     is_body_form=False,
-            ...     actual_response_class=TestResponse,
-            ...     body_field=model_field,
-            ... )
-            >>> test_result = asyncio.run(test_rq_handler)
-            >>> assert isinstance(test_result, TestResponse)
-            >>> assert test_result.media_type == TestResponse.media_type
-            >>> assert test_result.body == test_result.body
-
-        .. doctest:: Test BodyDecodeError
-
-            >>> test_request._body = b"raise BodyDecodeError"
-            >>> test_rq_handler = NonJsonRoute._request_handler(
-            ...     dependant=test_dependant,
-            ...     request=test_request,
-            ...     is_coroutine=False,
-            ...     is_body_form=False,
-            ...     actual_response_class=TestResponse,
-            ...     body_field=model_field,
-            ... )
-            >>> test_result = asyncio.run(test_rq_handler)
-            Traceback (most recent call last):
-            starlette.exceptions.HTTPException
-
-        .. testcleanup::
-
-            >>> BodyDecoder.__decoder__ = current_decoder
-            >>> BodyDecoder.__content_type__ = current_decoder_map
-        """
-
-        body = await NonJsonRoute._original_fastapi_body_decode(
-            request, is_body_form, body_field
-        )
-
-        if isinstance(body, bytes):
-            try:
-                body = BodyDecoder.run_decoder(request, body_field, body)
-            except BodyDecodeError as e:
-                raise HTTPException(status_code=400, detail=str(e)) from e
-            except Exception:  # pragma: nocover
-                # run_decoder cannot trigger this exception handler since run_decoder
-                # raises BodyDecodeError always. However, better safe than sorry
-                raise HTTPException(
-                    status_code=400, detail="There was an error parsing the body"
-                )
-        (
-            raw_response,
-            background_tasks,
-            sub_response,
-        ) = await NonJsonRoute._original_fastapi_call_endpoint(
-            request=request,
-            dependant=dependant,
-            is_coroutine=is_coroutine,
-            body=body,
-            dependency_overrides_provider=dependency_overrides_provider,
-        )
-
-        if (
-            not isinstance(raw_response, Response)
-            and isinstance(actual_response_class, type)
-            and issubclass(actual_response_class, NonJsonResponse)
-        ):
-            raw_response = actual_response_class(content=raw_response)
-
-        return await NonJsonRoute._original_fastapi_return_response(
-            raw_response=raw_response,
-            background_tasks=background_tasks,
-            sub_response=sub_response,
-            is_coroutine=is_coroutine,
-            actual_response_class=actual_response_class,
-            status_code=status_code,
-            response_field=response_field,
-            response_model_include=response_model_include,
-            response_model_exclude=response_model_exclude,
-            response_model_by_alias=response_model_by_alias,
-            response_model_exclude_unset=response_model_exclude_unset,
-            response_model_exclude_defaults=response_model_exclude_defaults,
-            response_model_exclude_none=response_model_exclude_none,
-        )
-
-    @staticmethod
-    def get_request_handler(
-        dependant: Dependant,
-        body_field: Optional[ModelField] = None,
-        status_code: Optional[int] = None,
-        response_class: Union["Type[Response]", DefaultPlaceholder] = Default(
-            JSONResponse
-        ),
-        response_field: Optional[ModelField] = None,
-        response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
-        response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
-        response_model_by_alias: bool = True,
-        response_model_exclude_unset: bool = False,
-        response_model_exclude_defaults: bool = False,
-        response_model_exclude_none: bool = False,
-        dependency_overrides_provider: Optional[Any] = None,
-    ) -> Callable[[Request], Coroutine[Any, Any, Response]]:
-        """
-        If fastapi fails to decode the body, this request handler will use
-        :class:`BodyDecoder` to decode the body.
-
-        Furthermore, any API endpoint may use :class:`NonJsonResponse`
-        to serialize any data into a non-json format.
-        """
-        (
-            is_coroutine,
-            is_body_form,
-            actual_response_class,
-        ) = NonJsonRoute._original_fastapi_prepare_request_handler(
-            dependant=dependant, body_field=body_field, response_class=response_class
-        )
-
-        wrapped_func = NonJsonRoute._request_handler
-
-        async def wrapper(request: Request) -> Any:
-            return await wrapped_func(
-                request=request,
-                dependant=dependant,
-                is_coroutine=is_coroutine,
-                is_body_form=is_body_form,
-                body_field=body_field,
-                actual_response_class=actual_response_class,
-                status_code=status_code,
-                response_field=response_field,
-                response_model_include=response_model_include,
-                response_model_exclude=response_model_exclude,
-                response_model_by_alias=response_model_by_alias,
-                response_model_exclude_unset=response_model_exclude_unset,
-                response_model_exclude_defaults=response_model_exclude_defaults,
-                response_model_exclude_none=response_model_exclude_none,
-                dependency_overrides_provider=dependency_overrides_provider,
-            )
-
-        wrapper.__wrapped_func__ = wrapped_func  # type: ignore[attr-defined]
-
-        return wrapper
-
-
-class BodyDecoder:
-    __decoder__: ClassVar[
-        List["Type[BodyDecoder]"]
-    ] = []  #: a collection for all body decoders
-    __content_type__: ClassVar[Dict[str, List["Type[BodyDecoder]"]]] = {}
-    supported_content_type: ClassVar[Iterable[str]] = []
-
-    @classmethod
-    def register(cls, decoder: "Type[BodyDecoder]") -> None:
-        """
-        adds a decoder to :attr:`__decoder__`.
-
-        The decoder is queried by :meth:`run_decoder`.
-
-        .. testsetup::
-
-            >>> current_decoder = BodyDecoder.__decoder__
-            >>> current_decoder_map = BodyDecoder.__content_type__
-            >>> BodyDecoder.__decoder__ = []
-            >>> BodyDecoder.__content_type__ = {}
-
-        .. doctest:: registration
-
-            >>> ct = "application/test"
-            >>> class TestDecoder(BodyDecoder):
-            ...     supported_content_type: ClassVar[Iterable[str]] = [ct]
-            >>> BodyDecoder.register(TestDecoder)
-            >>> assert ct in BodyDecoder.__content_type__
-            >>> assert isinstance(BodyDecoder.__content_type__[ct], list)
-            >>> assert len(BodyDecoder.__content_type__[ct]) == 1
-            >>> assert BodyDecoder.__content_type__[ct][0] == TestDecoder
-            >>> assert len(BodyDecoder.__decoder__) == 1
-            >>> assert BodyDecoder.__decoder__[0] == TestDecoder
-
-        .. testcleanup::
-
-            >>> BodyDecoder.__decoder__ = current_decoder
-            >>> BodyDecoder.__content_type__ = current_decoder_map
-        """
-
-        for content_type in decoder.supported_content_type:
-            if content_type not in cls.__content_type__:
-                cls.__content_type__[content_type] = []
-            cls.__content_type__[content_type].append(decoder)
-        cls.__decoder__.append(decoder)
-
-    @classmethod
-    def _get_decoder_collection(
-        cls, content_type: Optional[str]
-    ) -> Iterable["Type[BodyDecoder]"]:
-        """
-        >>> a = BodyDecoder._get_decoder_collection("application/xml")
-        >>> b = BodyDecoder._get_decoder_collection("text/xml")
-        >>> c = BodyDecoder._get_decoder_collection("asdf/xml")
-        >>> d = BodyDecoder._get_decoder_collection(None)
-        >>> la = list(a)
-        ...
-        >>> assert len(BodyDecoder.__content_type__) > 0
-        >>> assert len(BodyDecoder.__decoder__) > 0
-        >>> assert a == b
-        >>> assert id(a) != id(b)
-        >>> assert id(c) == id(d)
-        >>> assert id(la[0]) != id(c)
-        """
-        if content_type in cls.__content_type__:
-            return cls.__content_type__[content_type]
-        else:
-            return cls.__decoder__
-
-    @classmethod
-    def run_decoder(
-        cls, request: Request, field: ModelField, body: bytes
-    ) -> Union[bytes, Dict[str, Any]]:
-        """
-        Decodes the HTTP body using any decoder from :attr:`__decoder__`.
-
-        :param request: the original request
-        :param field:   the model field to deal with
-        :param body:    the original http body
-
-        :return: A decoded body if any decoder was capable to handel the body. Else,
-                 the unmodified body.
-
-        :raises BodyDecodeError: if any decoder raised an exception. Writes the
-                                 error traceback to stderr if the underlying exception
-                                 is not an instance of :class:`BodyDecodeError`
-
-        .. testsetup::
-
-            >>> from dataclasses import dataclass
-            >>> from dataclasses import field
-            >>> from fastapi_xml import XmlBody
-
-            >>> app = FastAPI()
-            >>> app.router.route_class = NonJsonRoute
-
-            >>> @dataclass
-            ... class Dummy:
-            ...     x: str = field(metadata={"type": "Element"})
-
-            >>> @app.router.post("/")
-            ... def endpoint(x: Dummy = XmlBody()) -> None:  # pragma: nocover
-            ...     pass
-
-            >>> route = app.routes[-1]
-            >>> assert isinstance(route, APIRoute)
-            >>> body_field = route.body_field
-
-        .. doctest:: unsupported content type
-
-            >>> scope = {
-            ...     "type": "http",
-            ...     "query_string": "",
-            ...     "headers": [(b"content-type", b"text/html")],
-            ... }
-            >>> test_request = Request(scope=scope)
-            >>> test_body = b"invalid"
-            >>> test_request.headers._list = [("content-type", "text/html")]
-            >>> test_result = BodyDecoder.run_decoder(
-            ...     test_request,
-            ...     body_field,
-            ...     test_body
-            ... )
-            >>> assert test_result == test_body
-
-        .. doctest:: successfull decoding
-
-            >>> scope["headers"] = [(b"content-type", b"application/xml")]
-            >>> test_request = Request(scope=scope)
-            >>> test_result = BodyDecoder.run_decoder(
-            ...     test_request,
-            ...     body_field,
-            ...     b"<Dummy><x>foo</x></Dummy>"
-            ... )
-            >>> assert isinstance(test_result, dict)
-            >>> assert test_result["x"] == "foo"
-
-        .. doctest:: decoding error
-
-            >>> test_result = BodyDecoder.run_decoder(
-            ...     test_request,
-            ...     body_field,
-            ...     b"invalid"
-            ... )
-            Traceback (most recent call last):
-            fastapi_xml.nonjson.BodyDecodeError: syntax error: line 1, column 0
-
-        .. doctest:: unexcepted exception
-
-            >>> test_result = BodyDecoder.run_decoder(
-            ...     test_request,
-            ...     None,
-            ...     b"<Dummy><x>foo</x></Dummy>"
-            ... )  # ignore: type
-            Traceback (most recent call last):
-            fastapi_xml.nonjson.BodyDecodeError: body decoding failed.
-        """
-        content_type = request.headers.get("content-type")
-        content_type = content_type.lower() if isinstance(content_type, str) else None
-        decoder_collection = cls._get_decoder_collection(content_type)
-
-        for decoder in decoder_collection:
-            try:
-                result = decoder.decode(request, field, body)
-            except BodyDecodeError:
-                raise
-            except Exception as e:
-                sys.stderr.write(traceback.format_exc() + "\n")
-                raise BodyDecodeError("body decoding failed.") from e
-            if result is not None:
-                return result
-        return body
-
-    @classmethod
-    @abstractmethod
-    def decode(
-        cls, request: Request, field: ModelField, body: bytes
-    ) -> Optional[Dict[str, Any]]:  # pragma: nocover
-        """
-        This method decodes the body. Any Implementation must review if the
-        body has the correct format. If not, this method MUST return None. For
-        instance, an xml decoder is not capable to decode binary data. Hence,
-        the xml decoder validates if the body is valid xml first, and proceeds
-        decoding afterwards.
-
-        :param request: the original request
-        :param field:   the model field to deal with
-        :param body:    the original http body
-
-        :raises BodyDecodeError: if this is the correct decoder but the body is invalid
-                                 for some reason.
-                                 The error message should not contain sensible data
-                                 since :meth:`run_decoder` will forward it.
-
-        :return: The Decoder MUST return None, if the decoding failed for any reason.
-                Else, it MUST return a mapping for pydantic's constructor
-        """
-        raise NotImplementedError()
-
-
-class NonJsonResponse(JSONResponse):
-    """fastapi.openapi.utils.get_openapi_path does not support any
-    response_schema except for JSONResponse:"""
-
-
-def _get_unmodified_openapi(app: FastAPI) -> OpenAPI:
-    """
-    .. testsetup::
-
-        >>> from dataclasses import dataclass
-        >>> from dataclasses import field
-        >>> from fastapi_xml.xmlbody import XmlBody
-
-        >>> app = FastAPI()
-        >>> app.router.route_class = NonJsonRoute
-
-        >>> @dataclass
-        ... class Dummy:
-        ...     x: str = field(metadata={"type": "Element"})
-        ...
-        >>> @app.router.post("/")
-        ... def endpoint(x: Dummy = XmlBody()) -> None:  # pragma: nocover
-        ...     pass
-
-     .. doctest:: schema unchanged
-
-        >>> a = app.openapi()
-        >>> b = _get_unmodified_openapi(app).dict(exclude_none=True, by_alias=True)
-        >>> assert a == b
-    """
-    openapi_schema = get_openapi(
-        title=app.title,
-        version=app.version,
-        openapi_version=app.openapi_version,
-        description=app.description,
-        terms_of_service=app.terms_of_service,
-        contact=app.contact,
-        license_info=app.license_info,
-        routes=app.routes,
-        tags=app.openapi_tags,
-        servers=app.servers,
-    )
-    return OpenAPI(**openapi_schema)
-
-
-def _extend_openapi(app: FastAPI, **extension_kwargs: Any) -> Dict[str, Any]:
-    """
-    .. testsetup::
-
-        >>> from dataclasses import dataclass
-        >>> from fastapi_xml.xmlbody import XmlBody
-        >>> @dataclass
-        ... class Dummy:
-        ...     x: str
-        >>> app = FastAPI()
-        >>> @app.router.post("/")
-        ... def endpoint(x: Dummy = XmlBody()) -> None:  # pragma: nocover
-        ...     pass
-
-     .. doctest:: predefined schema
-
-        >>> predefined = dict()
-        >>> app.openapi_schema = predefined
-        >>> result = _extend_openapi(app)
-        >>> assert id(result) == id(predefined)
-        >>> app.openapi_schema = None
-
-     .. doctest:: schema modification
-
-        >>> openapi_unmodified = _get_unmodified_openapi(app)
-        >>> openapi_modified   = _extend_openapi(app)
-        >>> assert openapi_modified != openapi_unmodified
-    """
-    if app.openapi_schema is not None:
-        return app.openapi_schema
-    openapi = _get_unmodified_openapi(app)
-
-    for modifier in OPENAPI_SCHEMA_MODIFIER:
-        modifier(app, openapi, extension_kwargs)
-
-    app.openapi_schema = jsonable_encoder(openapi, by_alias=True, exclude_none=True)
-    return app.openapi_schema
-
-
-def add_openapi_extension(app: FastAPI, **extension_kwargs: Any) -> None:
-    """
-    .. testsetup::
-
-        >>> from fastapi import FastAPI
-        >>> from dataclasses import dataclass
-        >>> from fastapi_xml.xmlbody import XmlBody
-
-     .. doctest::
-
-        >>> app = FastAPI()
-        >>> add_openapi_extension(app)
-        >>> assert isinstance(app.openapi, MethodType)
-        >>> assert app.openapi.__func__ == _extend_openapi
-        >>> assert isinstance(app.openapi(), dict)
-    """
-    app.openapi = MethodType(_extend_openapi, app)  # type: ignore[assignment]
diff --git a/fastapi_xml/openapi.py b/fastapi_xml/openapi.py
new file mode 100644
index 0000000..2ba255e
--- /dev/null
+++ b/fastapi_xml/openapi.py
@@ -0,0 +1,355 @@
+from dataclasses import Field
+from dataclasses import fields
+from dataclasses import is_dataclass
+from types import MethodType
+from typing import Any
+from typing import Callable
+from typing import Dict
+from typing import List
+from typing import Mapping
+from typing import Optional
+from typing import Type
+from typing import TYPE_CHECKING
+
+from fastapi import FastAPI
+from fastapi.encoders import jsonable_encoder
+from fastapi.openapi.models import Components
+from fastapi.openapi.models import Schema
+from fastapi.openapi.models import XML
+from fastapi.openapi.utils import get_fields_from_routes
+from fastapi.openapi.utils import get_openapi
+from fastapi.openapi.utils import OpenAPI
+from pydantic import TypeAdapter
+from pydantic.dataclasses import dataclass as pydantic_dataclass
+from xsdata.formats.dataclass.models.elements import XmlType
+
+from .decoder import DEFAULT_XML_CONTEXT
+
+OpenApiSchemaModifier = Callable[[FastAPI, OpenAPI, Optional[Mapping[str, Any]]], bool]
+OPENAPI_SCHEMA_MODIFIER: List[OpenApiSchemaModifier] = []
+
+if TYPE_CHECKING:  # pragma: nocover
+    from pydantic.dataclasses import PydanticDataclass
+
+
+def _get_element_name_generator(meta: "Type[object]") -> Callable[[str], str]:
+    """
+    The _get_element_name_generator function is a helper function that returns
+    the element_name_generator attribute of the given config class, or if it
+    does not exist, returns DEFAULT_XML_CONTEXT.element_name_generator. The
+    element name generator is used to generate XML tag names for elements in an
+    XML document.
+
+    :param meta: a model xml configuration class
+    :return: A function that takes a string and returns a string
+    """
+    return getattr(
+        meta, "element_name_generator", DEFAULT_XML_CONTEXT.element_name_generator
+    )
+
+
+def _get_attribute_name_generator(meta: "Type[object]") -> Callable[[str], str]:
+    """
+    The _get_attribute_name_generator function is a helper function that
+    returns the attribute_name_generator attribute of the given meta class, or
+    if it does not exist, returns DEFAULT_XML_CONTEXT.attribute_name_generator.
+    This allows for customizing how XML attributes are converted to Python
+    object attributes.
+
+    :param meta: the config meta class
+    :return: A function that takes a string and returns a string
+    """
+    return getattr(
+        meta, "attribute_name_generator", DEFAULT_XML_CONTEXT.attribute_name_generator
+    )
+
+
+def _add_model_schema(
+    dclazz: Type[object], model_schema: Schema, ns_map: Mapping[str, str]
+) -> None:
+    """
+    The _add_model_schema function adds an XML schema to the given model
+    schema.
+
+    :param dclazz: A dataclass for which an OpenAPI schema is created.
+    :param model_schema: The current schema information for dclazz
+    :param ns_map: Map the namespace to a prefix
+    """
+    model_meta = getattr(dclazz, "Meta", type)
+    namespace = getattr(model_meta, "namespace", None)
+    prefix = None if not isinstance(namespace, str) else ns_map.get(namespace)
+    xml_schema = XML(
+        name=getattr(model_meta, "name", dclazz.__name__),
+        namespace=namespace,
+        prefix=prefix,
+        attribute=None,
+        wrapped=None,
+    )
+    model_schema.xml = xml_schema
+
+
+def _is_xml_schema_empty(xml_schema: XML) -> bool:
+    """
+    The _is_xml_schema_empty function is used to determine if an XML schema has
+    been defined.
+
+    :param xml_schema: The XML schema to check
+    :return: True if the xml schema is empty
+    """
+    return (
+        xml_schema.name is None
+        and xml_schema.namespace is None
+        and xml_schema.prefix is None
+        and xml_schema.attribute is None
+        and xml_schema.wrapped is None
+    )
+
+
+def _switch_ref_to_all_of(prop: Schema, xml_schema: XML) -> None:
+    """
+    The _switch_ref_to_all_of function is used to convert a property that has
+    both an XML Schema and a $ref to one that uses allOf instead.
+
+    This is necessary because the OpenAPI Specification does not allow
+    for both an XML Schema and a $ref to be present on the same
+    property. The _switch_ref_to_all_of function will take in a
+    property, check if it has an XML Schema, and then create an allOf
+    array with the original ref as its only item.
+    """
+    if not _is_xml_schema_empty(xml_schema):
+        prop.xml = xml_schema
+        if prop.ref is not None:
+            prop.allOf = [Schema(**{"$ref": prop.ref})]  # type: ignore
+            prop.ref = None
+
+
+def _add_field_schema(
+    dclazz: Type[object],
+    model_field: Field[Any],
+    model_schema: Schema,
+    ns_map: Mapping[str, str],
+) -> None:
+    """
+    The _add_field_schema function is responsible for adding the XML schema
+    information to a dataclass model schema. This function adjust the schema
+    based on the given data field.
+
+    :param dclazz: A dataclass for which an OpenAPI schema is created.
+    :param model_field: The data field currently in focus.
+    :param model_schema: Schema: Pass the schema of for dclazz.
+    :param ns_map: Map a namespace to a prefix
+    """
+    if model_schema.properties is None:
+        return
+
+    model_meta = getattr(dclazz, "Meta", type)
+    prop = model_schema.properties[model_field.name]
+    assert isinstance(prop, Schema)
+
+    namespace = model_field.metadata.get("namespace")
+    is_attribute = model_field.metadata.get("type") == XmlType.ATTRIBUTE
+    meta_name = model_field.metadata.get("name")
+    wrapper_name = model_field.metadata.get("wrapper")
+    prefix = None if not isinstance(namespace, str) else ns_map.get(namespace)
+    name_gen = (
+        _get_attribute_name_generator(model_meta)
+        if is_attribute
+        else _get_element_name_generator(model_meta)
+    )
+
+    if wrapper_name is None:
+        field_name = name_gen(meta_name) if meta_name is not None else None
+        array_name = None
+    else:
+        field_name = name_gen(wrapper_name)
+        array_name = name_gen(meta_name) if meta_name is not None else None
+
+    if wrapper_name is not None and prop.type != "array":
+        raise TypeError(
+            f"invalid wrapping type on {dclazz.__name__}.{model_field.name}: "
+            f"{prop.type}"
+        )
+
+    xml_schema = XML(
+        name=field_name,
+        namespace=namespace,
+        prefix=prefix,
+        attribute=is_attribute if is_attribute is True else None,
+        wrapped=True if wrapper_name is not None else None,
+    )
+    if not _is_xml_schema_empty(xml_schema):
+        prop.xml = xml_schema
+
+    if prop.type == "array":
+        if not isinstance(prop.items, Schema):
+            raise TypeError(
+                f"missing property items on {model_schema.title}.{model_field.name}"
+            )
+        _switch_ref_to_all_of(
+            prop.items,
+            XML(
+                name=array_name,
+                namespace=None,
+                prefix=None,
+                attribute=None,
+                wrapped=None,
+            ),
+        )
+
+
+def _get_route_models(
+    app: FastAPI, openapi: OpenAPI
+) -> List[Type["PydanticDataclass"]]:
+    """
+    The _get_route_models function is used to get the Pydantic models that are
+    defined in the OpenAPI schema.
+
+    :param app: Access the routes of the api
+    :param openapi: Get the schemas from the openapi object
+    :return: A list of pydantic dataclasses that are used as route parameters or response bodies
+    """
+    if isinstance(openapi.components, Components) and isinstance(
+        openapi.components.schemas, dict
+    ):
+        return [
+            pydantic_dataclass(field.type_)
+            for field in get_fields_from_routes(app.routes)
+            if is_dataclass(field.type_)
+            and field.type_.__name__ in openapi.components.schemas
+        ]
+    else:  # pragma: nocover
+        return []
+
+
+def add_openapi_xml_schema(
+    app: FastAPI, openapi: OpenAPI, ns_map: Optional[Mapping[str, str]] = None
+) -> bool:
+    """
+    The add_openapi_xml_schema function adds XML schema information to the
+    OpenAPI document.
+
+    :param app: Get the models from the routes
+    :param openapi: Add the xml schema to the openapi object
+    :param ns_map: Map xml namespaces to prefixes
+    :return: True if it has added any schemas
+    """
+    if openapi.components is None or openapi.components.schemas is None:
+        return False
+
+    ns_map = ns_map or {}
+    flat_models = _get_route_models(app, openapi)
+
+    model_counter = 0
+    field_counter = 0
+    for model in flat_models:
+        schema = Schema(
+            **TypeAdapter(model).json_schema(
+                by_alias=True, ref_template="#/components/schemas/{model}"
+            )
+        )
+        openapi.components.schemas[model.__name__] = schema
+        _add_model_schema(model, schema, ns_map)
+        model_counter += 1
+
+        for field in fields(model):
+            _add_field_schema(model, field, schema, ns_map)
+            field_counter += 1
+    return model_counter > 0 and field_counter > 0
+
+
+def _get_unmodified_openapi(app: FastAPI) -> OpenAPI:
+    """
+    .. testsetup::
+
+        >>> from dataclasses import dataclass
+        >>> from dataclasses import field
+        >>> from fastapi_xml.xmlbody import XmlBody
+        >>> from fastapi_xml.route import XmlRoute
+
+        >>> app = FastAPI()
+        >>> app.router.route_class = XmlRoute
+
+        >>> @dataclass
+        ... class Dummy:
+        ...     x: str = field(metadata={"type": "Element"})
+        ...
+        >>> @app.router.post("/")
+        ... def endpoint(x: Dummy = XmlBody()) -> None:  # pragma: nocover
+        ...     pass
+
+     .. doctest:: schema unchanged
+
+        >>> a = app.openapi()
+        >>> b = _get_unmodified_openapi(app).model_dump(exclude_none=True, by_alias=True)
+        >>> assert a == b
+    """
+    openapi_schema = get_openapi(
+        title=app.title,
+        version=app.version,
+        openapi_version=app.openapi_version,
+        description=app.description,
+        terms_of_service=app.terms_of_service,
+        contact=app.contact,
+        license_info=app.license_info,
+        routes=app.routes,
+        tags=app.openapi_tags,
+        servers=app.servers,
+    )
+    return OpenAPI(**openapi_schema)
+
+
+def _extend_openapi(app: FastAPI, **extension_kwargs: Any) -> Dict[str, Any]:
+    """
+    .. testsetup::
+
+        >>> from dataclasses import dataclass
+        >>> from fastapi_xml.xmlbody import XmlBody
+        >>> @dataclass
+        ... class Dummy:
+        ...     x: str
+        >>> app = FastAPI()
+        >>> @app.router.post("/")
+        ... def endpoint(x: Dummy = XmlBody()) -> None:  # pragma: nocover
+        ...     pass
+
+     .. doctest:: predefined schema
+
+        >>> predefined = dict()
+        >>> app.openapi_schema = predefined
+        >>> result = _extend_openapi(app)
+        >>> assert id(result) == id(predefined)
+        >>> app.openapi_schema = None
+
+     .. doctest:: schema modification
+
+        >>> openapi_unmodified = _get_unmodified_openapi(app)
+        >>> openapi_modified   = _extend_openapi(app)
+        >>> assert openapi_modified != openapi_unmodified
+    """
+    if app.openapi_schema is not None:
+        return app.openapi_schema
+    openapi = _get_unmodified_openapi(app)
+
+    add_openapi_xml_schema(app, openapi, extension_kwargs)
+
+    app.openapi_schema = jsonable_encoder(openapi, by_alias=True, exclude_none=True)
+    return app.openapi_schema
+
+
+def add_openapi_extension(app: FastAPI, **extension_kwargs: Any) -> None:
+    """
+    .. testsetup::
+
+        >>> from fastapi import FastAPI
+        >>> from dataclasses import dataclass
+        >>> from fastapi_xml.xmlbody import XmlBody
+
+     .. doctest::
+
+        >>> app = FastAPI()
+        >>> add_openapi_extension(app)
+        >>> assert isinstance(app.openapi, MethodType)
+        >>> assert app.openapi.__func__ == _extend_openapi
+        >>> assert isinstance(app.openapi(), dict)
+    """
+    app.openapi = MethodType(_extend_openapi, app)  # type: ignore[method-assign]
diff --git a/fastapi_xml/response.py b/fastapi_xml/response.py
new file mode 100644
index 0000000..58b576c
--- /dev/null
+++ b/fastapi_xml/response.py
@@ -0,0 +1,63 @@
+from typing import Any
+from typing import Callable
+from typing import ClassVar
+from typing import Dict
+from typing import Optional
+
+from fastapi.responses import JSONResponse
+from xsdata.formats.dataclass.serializers import XmlSerializer
+
+from .decoder import DEFAULT_XML_CONTEXT
+
+__all__ = [
+    "XmlResponse",
+    "XmlAppResponse",
+    "XmlTextResponse",
+]
+
+NS_MAP: Dict[Optional[str], str] = {}
+
+
+class XmlResponse(JSONResponse):
+    """fastapi.openapi.utils.get_openapi_path does not support any
+    response_schema except for JSONResponse:"""
+
+    media_type: str = "application/xml"
+    xml_serializer_factory: ClassVar[
+        Callable[[], XmlSerializer]
+    ] = lambda: XmlSerializer(context=DEFAULT_XML_CONTEXT)
+    serializer: ClassVar[Optional[XmlSerializer]] = None
+
+    @classmethod
+    def get_serializer(cls) -> XmlSerializer:
+        """
+        The get_serializer function is a class method that returns an instance
+        of the XmlSerializer class. The first time it is called, it creates an
+        instance and stores it in the serializer attribute of the class.
+        Subsequent calls return this same object.
+
+        :return: The serializer for the class
+        """
+        if cls.serializer is None:
+            cls.serializer = cls.xml_serializer_factory()
+        return cls.serializer
+
+    def render(self, content: Any) -> bytes:
+        """
+        The render function is responsible for taking the content and
+        serializing it into a string. The render function should return a bytes
+        object, not a str.
+
+        :param content: Any: Pass the data to be serialized
+        :return: A xml serialized byte string
+        """
+        serializer = self.get_serializer()
+        return serializer.render(content, ns_map=NS_MAP).encode("utf-8")
+
+
+class XmlTextResponse(XmlResponse):
+    media_type: str = "text/xml"
+
+
+class XmlAppResponse(XmlResponse):
+    media_type: str = "application/xml"
diff --git a/fastapi_xml/route.py b/fastapi_xml/route.py
new file mode 100644
index 0000000..2cf42c7
--- /dev/null
+++ b/fastapi_xml/route.py
@@ -0,0 +1,361 @@
+import asyncio
+from contextlib import AsyncExitStack
+from typing import Any
+from typing import Callable
+from typing import Coroutine
+from typing import Dict
+from typing import Optional
+from typing import Tuple
+from typing import Type
+from typing import Union
+
+from fastapi import params
+from fastapi._compat import _normalize_errors
+from fastapi._compat import ModelField
+from fastapi.datastructures import Default
+from fastapi.datastructures import DefaultPlaceholder
+from fastapi.datastructures import FormData
+from fastapi.dependencies.models import Dependant
+from fastapi.dependencies.utils import solve_dependencies
+from fastapi.exceptions import FastAPIError
+from fastapi.exceptions import RequestValidationError
+from fastapi.routing import APIRoute
+from fastapi.routing import run_endpoint_function
+from fastapi.routing import serialize_response
+from fastapi.types import IncEx
+from fastapi.utils import is_body_allowed_for_status_code
+from starlette.background import BackgroundTasks
+from starlette.exceptions import HTTPException
+from starlette.requests import Request
+from starlette.responses import JSONResponse
+from starlette.responses import Response
+from xsdata.formats.dataclass.context import XmlContext
+
+from .decoder import BodyDecodeError
+from .decoder import XmlDecoder
+from .response import XmlResponse
+
+DEFAULT_XML_CONTEXT: XmlContext = XmlContext()
+
+
+class XmlRoute(APIRoute):
+    def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]:
+        return self.get_request_handler(
+            dependant=self.dependant,
+            body_field=self.body_field,
+            status_code=self.status_code,
+            response_class=self.response_class,
+            response_field=self.secure_cloned_response_field,
+            response_model_include=self.response_model_include,
+            response_model_exclude=self.response_model_exclude,
+            response_model_by_alias=self.response_model_by_alias,
+            response_model_exclude_unset=self.response_model_exclude_unset,
+            response_model_exclude_defaults=self.response_model_exclude_defaults,
+            response_model_exclude_none=self.response_model_exclude_none,
+            dependency_overrides_provider=self.dependency_overrides_provider,
+        )
+
+    @staticmethod
+    async def _mod_fastapi_return_response(
+        raw_response: Any,
+        background_tasks: Optional[BackgroundTasks],
+        sub_response: Response,
+        is_coroutine: bool,
+        actual_response_class: "Type[Response]",
+        status_code: Optional[int] = None,
+        response_field: Optional[ModelField] = None,
+        response_model_include: Optional[IncEx] = None,
+        response_model_exclude: Optional[IncEx] = None,
+        response_model_by_alias: bool = True,
+        response_model_exclude_unset: bool = False,
+        response_model_exclude_defaults: bool = False,
+        response_model_exclude_none: bool = False,
+    ) -> Response:  # pragma: nocover
+        # Repository: https://github.com/tiangolo/fastapi
+        # fastapi's license copy is blow.
+        #
+        # The MIT License (MIT)
+        #
+        # Copyright (c) 2018 Sebastián Ramírez
+        #
+        # Permission is hereby granted, free of charge, to any person obtaining a copy
+        # of this software and associated documentation files (the "Software"), to deal
+        # in the Software without restriction, including without limitation the rights
+        # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+        # copies of the Software, and to permit persons to whom the Software is
+        # furnished to do so, subject to the following conditions:
+        #
+        # The above copyright notice and this permission notice shall be included in
+        # all copies or substantial portions of the Software.
+        #
+        # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+        # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+        # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+        # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+        # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+        # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+        # THE SOFTWARE.
+        if isinstance(raw_response, Response):
+            if raw_response.background is None:
+                raw_response.background = background_tasks
+            response = raw_response
+        else:
+            response_args: Dict[str, Any] = {"background": background_tasks}
+            # If status_code was set, use it, otherwise use the default from the
+            # response class, in the case of redirect it's 307
+            current_status_code = (
+                status_code if status_code else sub_response.status_code
+            )
+            if current_status_code is not None:
+                response_args["status_code"] = current_status_code
+            if sub_response.status_code:
+                response_args["status_code"] = sub_response.status_code
+            content = await serialize_response(
+                field=response_field,
+                response_content=raw_response,
+                include=response_model_include,
+                exclude=response_model_exclude,
+                by_alias=response_model_by_alias,
+                exclude_unset=response_model_exclude_unset,
+                exclude_defaults=response_model_exclude_defaults,
+                exclude_none=response_model_exclude_none,
+                is_coroutine=is_coroutine,
+            )
+            response = actual_response_class(content, **response_args)
+            if not is_body_allowed_for_status_code(response.status_code):
+                response.body = b""
+            response.headers.raw.extend(sub_response.headers.raw)
+
+        if response is None:
+            raise FastAPIError(
+                "No response object was returned. There's a high chance that the "
+                "application code is raising an exception and a dependency with yield "
+                "has a block with a bare except, or a block with except Exception, "
+                "and is not raising the exception again. Read more about it in the "
+                "docs: https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies"
+                "-with-yield-and-except"
+            )
+        return response
+
+    @staticmethod
+    def _original_fastapi_prepare_request_handler(
+        *,
+        dependant: Dependant,
+        body_field: Optional[ModelField],
+        response_class: Union["Type[Response]", DefaultPlaceholder],
+    ) -> Tuple[bool, bool, "Type[Response]"]:
+        # Repository: https://github.com/tiangolo/fastapi
+        #
+        # The MIT License (MIT)
+        #
+        # Copyright (c) 2018 Sebastián Ramírez
+        #
+        # Permission is hereby granted, free of charge, to any person obtaining a copy
+        # of this software and associated documentation files (the "Software"), to deal
+        # in the Software without restriction, including without limitation the rights
+        # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+        # copies of the Software, and to permit persons to whom the Software is
+        # furnished to do so, subject to the following conditions:
+        #
+        # The above copyright notice and this permission notice shall be included in
+        # all copies or substantial portions of the Software.
+        #
+        # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+        # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+        # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+        # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+        # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+        # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+        # THE SOFTWARE.
+        assert dependant.call is not None, "dependant.call must be a function"
+        is_coroutine = asyncio.iscoroutinefunction(dependant.call)
+        is_body_form = body_field is not None and isinstance(
+            body_field.field_info, params.Form
+        )
+        if isinstance(response_class, DefaultPlaceholder):
+            actual_response_class: "Type[Response]" = (
+                response_class.value
+            )  # pragma: no cover
+        else:
+            actual_response_class = response_class
+        return is_coroutine, is_body_form, actual_response_class
+
+    @staticmethod
+    async def _mod_fastapi_call_endpoint(
+        *,
+        request: Request,
+        dependant: Dependant,
+        is_coroutine: bool,
+        body: Optional[Union[Dict[str, Any], FormData]],
+        dependency_overrides_provider: Optional[Any] = None,
+    ) -> Tuple[Any, Optional[BackgroundTasks], Response]:  # pragma: no cover
+
+        # Repository: https://github.com/tiangolo/fastapi
+        #
+        # The MIT License (MIT)
+        #
+        # Copyright (c) 2018 Sebastián Ramírez
+        #
+        # Permission is hereby granted, free of charge, to any person obtaining a copy
+        # of this software and associated documentation files (the "Software"), to deal
+        # in the Software without restriction, including without limitation the rights
+        # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+        # copies of the Software, and to permit persons to whom the Software is
+        # furnished to do so, subject to the following conditions:
+        #
+        # The above copyright notice and this permission notice shall be included in
+        # all copies or substantial portions of the Software.
+        #
+        # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+        # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+        # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+        # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+        # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+        # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+        # THE SOFTWARE.
+        async with AsyncExitStack() as async_exit_stack:
+            solved_result = await solve_dependencies(
+                request=request,
+                dependant=dependant,
+                body=body,
+                dependency_overrides_provider=dependency_overrides_provider,
+                async_exit_stack=async_exit_stack,
+            )
+
+        values, errors, background_tasks, sub_response, _ = solved_result
+        if errors:
+            validation_error = RequestValidationError(
+                _normalize_errors(errors), body=body
+            )
+            raise validation_error
+        else:
+            raw_response = await run_endpoint_function(
+                dependant=dependant, values=values, is_coroutine=is_coroutine
+            )
+            return raw_response, background_tasks, sub_response
+
+    @staticmethod
+    async def _request_handler(
+        *,
+        request: Request,
+        dependant: Dependant,
+        is_body_form: bool,
+        body_field: Optional[ModelField],
+        is_coroutine: bool,
+        actual_response_class: "Type[Response]",
+        status_code: Optional[int] = None,
+        response_field: Optional[ModelField] = None,
+        response_model_include: Optional[IncEx] = None,
+        response_model_exclude: Optional[IncEx] = None,
+        response_model_by_alias: bool = True,
+        response_model_exclude_unset: bool = False,
+        response_model_exclude_defaults: bool = False,
+        response_model_exclude_none: bool = False,
+        dependency_overrides_provider: Optional[Any] = None,
+    ) -> Response:
+        body: Any = None
+        if body_field:
+            body_bytes = await request.body()
+            try:
+                body = XmlDecoder.decode(request, body_field, body_bytes)
+            except BodyDecodeError as e:  # pragma: nocover
+                raise HTTPException(status_code=400, detail=str(e)) from e
+            except Exception:  # pragma: nocover
+                # run_decoder cannot trigger this exception handler since run_decoder
+                # raises BodyDecodeError always. However, better safe than sorry
+                raise HTTPException(
+                    status_code=400, detail="There was an error parsing the body"
+                )
+
+        (
+            raw_response,
+            background_tasks,
+            sub_response,
+        ) = await XmlRoute._mod_fastapi_call_endpoint(
+            request=request,
+            dependant=dependant,
+            is_coroutine=is_coroutine,
+            body=body,
+            dependency_overrides_provider=dependency_overrides_provider,
+        )
+
+        if (
+            not isinstance(raw_response, Response)
+            and isinstance(actual_response_class, type)
+            and issubclass(actual_response_class, XmlResponse)
+        ):
+            raw_response = actual_response_class(content=raw_response)
+
+        return await XmlRoute._mod_fastapi_return_response(
+            raw_response=raw_response,
+            background_tasks=background_tasks,
+            sub_response=sub_response,
+            is_coroutine=is_coroutine,
+            actual_response_class=actual_response_class,
+            status_code=status_code,
+            response_field=response_field,
+            response_model_include=response_model_include,
+            response_model_exclude=response_model_exclude,
+            response_model_by_alias=response_model_by_alias,
+            response_model_exclude_unset=response_model_exclude_unset,
+            response_model_exclude_defaults=response_model_exclude_defaults,
+            response_model_exclude_none=response_model_exclude_none,
+        )
+
+    @staticmethod
+    def get_request_handler(
+        dependant: Dependant,
+        body_field: Optional[ModelField] = None,
+        status_code: Optional[int] = None,
+        response_class: Union["Type[Response]", DefaultPlaceholder] = Default(
+            JSONResponse
+        ),
+        response_field: Optional[ModelField] = None,
+        response_model_include: Optional[IncEx] = None,
+        response_model_exclude: Optional[IncEx] = None,
+        response_model_by_alias: bool = True,
+        response_model_exclude_unset: bool = False,
+        response_model_exclude_defaults: bool = False,
+        response_model_exclude_none: bool = False,
+        dependency_overrides_provider: Optional[Any] = None,
+    ) -> Callable[[Request], Coroutine[Any, Any, Response]]:
+        """
+        If fastapi fails to decode the body, this request handler will use
+        :class:`XmlDecoder` to decode the body.
+
+        Furthermore, any API endpoint may use :class:`XmlResponse`
+        to serialize any data into a non-json format.
+        """
+
+        (
+            is_coroutine,
+            is_body_form,
+            actual_response_class,
+        ) = XmlRoute._original_fastapi_prepare_request_handler(
+            dependant=dependant, body_field=body_field, response_class=response_class
+        )
+
+        wrapped_func = XmlRoute._request_handler
+
+        async def wrapper(request: Request) -> Any:
+            return await wrapped_func(
+                request=request,
+                dependant=dependant,
+                is_coroutine=is_coroutine,
+                is_body_form=is_body_form,
+                body_field=body_field,
+                actual_response_class=actual_response_class,
+                status_code=status_code,
+                response_field=response_field,
+                response_model_include=response_model_include,
+                response_model_exclude=response_model_exclude,
+                response_model_by_alias=response_model_by_alias,
+                response_model_exclude_unset=response_model_exclude_unset,
+                response_model_exclude_defaults=response_model_exclude_defaults,
+                response_model_exclude_none=response_model_exclude_none,
+                dependency_overrides_provider=dependency_overrides_provider,
+            )
+
+        wrapper.__wrapped_func__ = wrapped_func  # type: ignore[attr-defined]
+
+        return wrapper
diff --git a/fastapi_xml/xmlbody.py b/fastapi_xml/xmlbody.py
index e98a3f2..29261ac 100644
--- a/fastapi_xml/xmlbody.py
+++ b/fastapi_xml/xmlbody.py
@@ -1,241 +1,9 @@
-from dataclasses import asdict
-from dataclasses import Field
-from dataclasses import fields
-from dataclasses import is_dataclass
 from typing import Any
-from typing import Callable
-from typing import ClassVar
-from typing import Dict
-from typing import Iterable
 from typing import List
-from typing import Mapping
 from typing import Optional
-from typing import Type
-from typing import TYPE_CHECKING
 
 from fastapi import Body
-from fastapi import FastAPI
-from fastapi.openapi.models import Components
-from fastapi.openapi.models import OpenAPI
-from fastapi.openapi.models import Schema
-from fastapi.openapi.models import XML
-from fastapi.openapi.utils import get_flat_models_from_routes
-from pydantic import BaseModel
-from pydantic.config import BaseConfig
-from pydantic.dataclasses import create_model
-from pydantic.dataclasses import gather_all_validators
-from pydantic.fields import Field as PydanticField
-from pydantic.fields import FieldInfo
-from pydantic.fields import ModelField
-from pydantic.fields import Required
-from pydantic.fields import Undefined
-from pydantic.typing import NoArgAnyCallable
-from starlette.requests import Request
-from xsdata.exceptions import ParserError
-from xsdata.formats.dataclass.context import XmlContext
-from xsdata.formats.dataclass.models.elements import XmlType
-from xsdata.formats.dataclass.parsers import XmlParser
-from xsdata.formats.dataclass.serializers import XmlSerializer
-
-from .nonjson import BodyDecodeError
-from .nonjson import BodyDecoder
-from .nonjson import NonJsonResponse
-
-if TYPE_CHECKING:  # pragma: nocover
-    from pydantic.dataclasses import Dataclass
-
-DEFAULT_XML_CONTEXT: XmlContext = XmlContext()
-NS_MAP: Dict[Optional[str], str] = {}
-
-
-class XmlDecoder(BodyDecoder):
-    xml_parser_factory: ClassVar[Callable[[], XmlParser]] = lambda: XmlParser(
-        context=DEFAULT_XML_CONTEXT
-    )
-    xml_parser: ClassVar[Optional[XmlParser]] = None
-    supported_content_type: ClassVar[Iterable[str]] = ["application/xml", "text/xml"]
-
-    @classmethod
-    def get_parser(cls) -> XmlParser:
-        if cls.xml_parser is None:
-            cls.xml_parser = cls.xml_parser_factory()
-        return cls.xml_parser
-
-    @classmethod
-    def decode(
-        cls, request: Request, model_field: ModelField, body: bytes
-    ) -> Optional[Dict[str, Any]]:
-        """
-        This method decodes the body. Any Implementation must review if the
-        body has the correct format. If not, this method MUST return None. For
-        instance, an xml decoder is not capable to decode binary data. Hence,
-        the xml decoder validates if the body is valid xml first, and proceeds
-        decoding afterwards.
-
-        :param request: the original request
-        :param field:   the model field to deal with
-        :param body:    the original http body
-
-        :raises BodyDecodeError: if this is the correct decoder but the body is
-                                 invalid for some reason. The error message should
-                                 not contain sensible data since :meth:`run_decoder`
-                                 will forward it.
-
-        :return: The Decoder MUST return None, if the decoding failed for any reason.
-                Else, it MUST return a mapping for pydantic's constructor
-
-        .. testsetup::
-
-            >>> from dataclasses import dataclass
-            >>> from dataclasses import field
-            >>> from fastapi.routing import APIRoute
-            >>> from pydantic import BaseModel
-
-            >>> class NotADataclazz(BaseModel):
-            ...     x: str
-
-            >>> @dataclass
-            ... class Model:
-            ...     x: str = field(metadata={"type": "Element"})
-
-            >>> app = FastAPI()
-            >>> @app.router.get("/model")
-            ... def endpoint_model(x: Model = XmlBody()) -> None:  # pragma: no cover
-            ...     pass
-
-            >>> @app.router.get("/dclazz")
-            ... def endpoint_dclazz(
-            ...     x: NotADataclazz = XmlBody()
-            ... ) -> None:  # pragma: no cover
-            ...     pass
-
-            >>> test_scope: Dict[str, Any] = {"type": "http", "query_string": ""}
-            >>> api_routes = [r for r in app.routes if isinstance(r, APIRoute)]
-            >>> route_dclazz = [r for r in api_routes if r.path == "/dclazz"][0]
-            >>> route_model = [r for r in api_routes if r.path == "/model"][0]
-
-        .. doctest:: decode body
-
-            >>> test_scope["headers"] = [(b"content-type", b"text/xml")]
-            >>> test_request = Request(scope=test_scope)
-            >>> test_field = route_model.body_field
-            >>> test_body = b"<Model><x>test</x></Model>"
-            >>> test_result = XmlDecoder.decode(test_request, test_field, test_body)
-            >>> assert isinstance(test_result, dict)
-            >>> assert "x" in test_result
-            >>> assert test_result["x"] == "test"
-
-        .. doctest:: return None if body model is not a dataclazz
-
-            >>> route = route_dclazz
-            >>> test_scope["headers"] = []
-            >>> test_request = Request(scope=test_scope)
-            >>> test_field = route_dclazz.body_field
-            >>> request = Request(scope=test_scope)
-            >>> assert XmlDecoder.decode(request, test_field, b"") is None
-
-        .. doctest:: raise BodyDecodeError on ParserError
-
-            >>> test_field = route_model.body_field
-            >>> test_scope["headers"] = [(b"content-type", b"text/xml")]
-            >>> test_request = Request(scope=test_scope)
-            >>> XmlDecoder.decode(test_request, test_field, b"invalid")
-            Traceback (most recent call last):
-            fastapi_xml.nonjson.BodyDecodeError: syntax error: line 1, column 0
-
-        .. doctest:: Do not raise an BodyDecodeErrpr if HTTP content-type does not match
-
-            >>> test_field = route_model.body_field
-            >>> test_scope["headers"] = [(b"content-type", b"text/something-else")]
-            >>> test_request = Request(scope=test_scope)
-            >>> assert XmlDecoder.decode(test_request, test_field, b"invalid") is None
-
-        .. doctest:: Do not raise an BodyDecodeErrpr if HTTP content-type is empty
-
-            >>> test_field = route_model.body_field
-            >>> test_scope["headers"] = []
-            >>> test_request = Request(scope=test_scope)
-            >>> assert XmlDecoder.decode(test_request, test_field, b"invalid") is None
-        """
-        xml_parser = cls.get_parser()
-        clazz = model_field.type_
-        if not is_dataclass(clazz):
-            return None
-
-        try:
-            o: object = xml_parser.from_bytes(body, clazz=clazz)
-        except ParserError as e:
-            http_content_type: str = request.headers.get("content-type", "")
-            if http_content_type.endswith("/xml"):
-                raise BodyDecodeError(str(e)) from e
-            else:
-                return None
-        else:
-            return asdict(o)
-
-
-BodyDecoder.register(XmlDecoder)
-
-
-class XmlResponse(NonJsonResponse):
-    media_type: str = "application/xml"
-    xml_serializer_factory: ClassVar[
-        Callable[[], XmlSerializer]
-    ] = lambda: XmlSerializer(context=DEFAULT_XML_CONTEXT)
-    serializer: ClassVar[Optional[XmlSerializer]] = None
-
-    @classmethod
-    def get_serializer(cls) -> XmlSerializer:
-        """
-        .. testsetup::
-
-           >>> current_serializer = XmlResponse.serializer
-           >>> XmlResponse.serializer = None
-
-        .. doctest::
-
-            >>> serializer = XmlResponse.get_serializer()
-            >>> assert isinstance(serializer, XmlSerializer)
-
-        .. testcleanup::
-
-            >>> XmlResponse.serializer = current_serializer
-        """
-        if cls.serializer is None:
-            cls.serializer = cls.xml_serializer_factory()
-        return cls.serializer
-
-    def render(self, content: Any) -> bytes:
-        """
-        .. testsetup::
-
-            >>> from dataclasses import dataclass
-            >>> from dataclasses import field
-
-            >>> @dataclass
-            ... class Dummy:
-            ...     x: str = field(metadata={"type": "Element"})
-
-        .. doctest::
-            >>> serializer = XmlSerializer()
-            >>> test_obj = Dummy(x="test")
-            >>> test_response = XmlResponse(content=test_obj)
-            >>> test_body = test_response.render(test_obj)
-            >>> assert isinstance(test_body, bytes)
-            >>> parsed_obj = XmlParser().from_bytes(test_body, clazz=Dummy)
-            >>> assert isinstance(parsed_obj, Dummy)
-            >>> assert parsed_obj.x == test_obj.x
-        """
-        serializer = self.get_serializer()
-        return serializer.render(content, ns_map=NS_MAP).encode("utf-8")
-
-
-class XmlTextResponse(XmlResponse):
-    media_type: str = "text/xml"
-
-
-class XmlAppResponse(XmlResponse):
-    media_type: str = "application/xml"
+from fastapi._compat import Undefined
 
 
 def XmlBody(
@@ -254,9 +22,31 @@ def XmlBody(
     max_length: Optional[int] = None,
     regex: Optional[str] = None,
     example: Any = Undefined,
-    examples: Optional[Dict[str, Any]] = None,
+    examples: Optional[List[Any]] = None,
     **extra: Any,
 ) -> Any:
+    """
+    The XmlBody function is a shortcut for the Body function with media_type
+    set to **application/xml**.
+
+    :param default: Set a default value for the body
+    :param embed: Indicate whether the body should be embedded
+    :param media_type: Specify the media type of the body
+    :param alias: Specify the name of the parameter in a query string
+    :param title: Set the title of the parameter
+    :param description: Describe the parameter
+    :param gt: Specify a minimum value
+    :param ge: Specify the minimum value of a numeric instance
+    :param lt: Specify the maximum value of a number
+    :param le: Specify the maximum value of a number
+    :param min_length: Specify the minimum length of a string
+    :param max_length: Specify the maximum length of a string
+    :param regex: Validate the string against a regex pattern
+    :param example: Define an example of the body
+    :param examples Define a dictionary of examples
+    :param extra: Allows for any other parameter that is not defined in the function
+    :return: A body object
+    """
     return Body(
         default,
         embed=embed,
@@ -275,489 +65,3 @@ def XmlBody(
         examples=examples,
         **extra,
     )
-
-
-def _get_all_dataclasses(type_: "Type[Any]" = object) -> List["Type[Any]"]:
-    """
-    .. testsetup::
-
-        >>> from dataclasses import dataclass
-
-    .. doctest::
-
-        >>> @dataclass
-        ... class TestModel:
-        ...     x: str
-        >>>
-        >>> @dataclass
-        ... class TestModelChild(TestModel):
-        ...     x: str
-        ...
-        ...     @dataclass
-        ...     class Embedded:
-        ...         y: str
-        >>>
-        >>> all_dataclasses = set(_get_all_dataclasses())
-        >>> assert len(all_dataclasses) > 2
-        >>> assert TestModel in all_dataclasses
-        >>> assert TestModelChild in all_dataclasses
-        >>> assert TestModelChild.Embedded in all_dataclasses
-    """
-    result = [t for t in type_.__subclasses__() if is_dataclass(t)]
-    for i in range(len(result)):
-        result += _get_all_dataclasses(result[i])
-    return result
-
-
-def _get_dataclass(
-    model: "Type[BaseModel]", all_dataclasses: List["Type[Any]"]
-) -> Optional["Type[Any]"]:
-    """
-    .. testsetup::
-
-        >>> from dataclasses import dataclass
-
-    .. doctest::
-        >>>
-        ... class NoDataclass(BaseModel):
-        ...     x: str
-
-        >>> @dataclass
-        ... class UniqueModelName:
-        ...     x: str
-
-        >>> pydantic_model = _create_pydantic_model_from_dataclass(
-        ...     UniqueModelName
-        ... )  # ignore: type
-        >>> all_dataclazzes = _get_all_dataclasses()
-        >>> dclazz = _get_dataclass(pydantic_model, all_dataclazzes)
-        >>> assert dclazz == UniqueModelName
-        >>> assert _get_dataclass(NoDataclass, all_dataclazzes) is None
-    """
-    result = getattr(model, "__dataclass__", None)
-    if result is None:
-        # addressing https://github.com/pydantic/pydantic/issues/4353
-        choices = [
-            clazz
-            for clazz in all_dataclasses
-            if model.__name__ == clazz.__name__ and model.__module__ == clazz.__module__
-        ]
-        choices = list(dict.fromkeys(choices))
-        if len(choices) > 0:
-            result = choices[0]
-            setattr(model, "__dataclass__", result)  # noqa: B010
-    return result
-
-
-def _get_element_name_generator(meta: "Type[object]") -> Callable[[str], str]:
-    """
-    .. doctest::
-
-        >>> class meta1:
-        ...     element_name_generator = lambda x: "test"
-        >>> class meta2:
-        ...    pass
-
-        >>> g = _get_element_name_generator(meta1)
-        >>> assert g("x") == "test"
-        >>> g = _get_element_name_generator(meta2)
-        >>> assert g == DEFAULT_XML_CONTEXT.element_name_generator
-    """
-    return getattr(
-        meta, "element_name_generator", DEFAULT_XML_CONTEXT.element_name_generator
-    )
-
-
-def _get_attribute_name_generator(meta: "Type[object]") -> Callable[[str], str]:
-    """
-    .. doctest::
-
-        >>> class meta1:
-        ...     attribute_name_generator = lambda x: "test"
-        >>> class meta2:
-        ...    pass
-
-        >>> g = _get_attribute_name_generator(meta1)
-        >>> assert g("x") == "test"
-        >>> g = _get_attribute_name_generator(meta2)
-        >>> assert g == DEFAULT_XML_CONTEXT.attribute_name_generator
-    """
-    return getattr(
-        meta, "attribute_name_generator", DEFAULT_XML_CONTEXT.attribute_name_generator
-    )
-
-
-def _add_model_schema(
-    dclazz: "Type[object]", model_schema: Schema, ns_map: Mapping[str, str]
-) -> None:
-    """
-    .. testsetup::
-
-        >>> from dataclasses import dataclass
-
-    .. doctest::
-
-        >>> @dataclass
-        ... class Dummy:
-        ...     class Meta:
-        ...         name = "Foo"
-        ...         namespace = "http://testns"
-        ...     x: str
-
-        >>> test_schema = Schema()
-        >>> test_ns_map = {"http://testns": "bla"}
-        >>> _add_model_schema(Dummy, test_schema, test_ns_map)
-        >>> assert isinstance(test_schema.xml, XML)
-        >>> assert test_schema.xml.name == Dummy.Meta.name
-        >>> assert test_schema.xml.prefix == "bla"
-        >>> assert test_schema.xml.namespace == "http://testns"
-        >>> assert test_schema.xml.attribute is None
-        >>> assert test_schema.xml.wrapped is None
-    """
-    model_meta = getattr(dclazz, "Meta", type)
-    namespace = getattr(model_meta, "namespace", None)
-    prefix = None if not isinstance(namespace, str) else ns_map.get(namespace)
-    xml_schema = XML(
-        name=getattr(model_meta, "name", dclazz.__name__),
-        namespace=namespace,
-        prefix=prefix,
-        attribute=None,
-        wrapped=None,
-    )
-    model_schema.xml = xml_schema
-
-
-def _is_xml_schema_empty(xml_schema: XML) -> bool:
-    """
-    .. doctest::
-
-        >>> xml = XML()
-        >>> assert _is_xml_schema_empty(XML()) is True
-        >>> assert _is_xml_schema_empty(XML(name="")) is False
-        >>> assert _is_xml_schema_empty(XML(prefix="")) is False
-        >>> assert _is_xml_schema_empty(XML(attribute=False)) is False
-        >>> assert _is_xml_schema_empty(XML(wrapped=False)) is False
-    """
-    return (
-        xml_schema.name is None
-        and xml_schema.namespace is None
-        and xml_schema.prefix is None
-        and xml_schema.attribute is None
-        and xml_schema.wrapped is None
-    )
-
-
-# TODO: doctest
-# def _switch_ref_to_one_of(prop: Schema, xml_schema: XML) -> None:
-#    if not _is_xml_schema_empty(xml_schema):
-#        prop.xml = xml_schema
-#        if prop.ref is not None:
-#            prop.oneOf = [Schema(**{"$ref": prop.ref})]
-#            prop.ref = None
-
-
-def _switch_ref_to_all_of(prop: Schema, xml_schema: XML) -> None:
-    """
-    .. doctest:: empty xml.
-
-        >>> test_prop = Schema()
-        >>> test_xml = XML()
-        >>> assert test_prop.xml is None
-        >>> _switch_ref_to_all_of(test_prop, test_xml)
-        >>> assert test_prop.xml is None
-        >>> assert test_prop.allOf is None
-        >>> assert test_prop.ref is None
-
-    .. doctest:: non empty xml
-
-        >>> test_prop = Schema()
-        >>> test_xml = XML(name="x")
-        >>> assert test_prop.xml is None
-        >>> _switch_ref_to_all_of(test_prop, test_xml)
-        >>> assert test_prop.xml is not None
-        >>> assert id(test_prop.xml) == id(test_xml)
-        >>> assert test_prop.allOf is None
-        >>> assert test_prop.ref is None
-
-    .. doctest:: test ref
-
-        >>> test_prop = Schema(ref="test_ref")
-        >>> test_xml = XML(name="x")
-        >>> assert test_prop.xml is None
-        >>> _switch_ref_to_all_of(test_prop, test_xml)
-        >>> assert test_prop.xml is not None
-        >>> assert id(test_prop.xml) == id(test_xml)
-        >>> assert isinstance(test_prop.allOf, list)
-        >>> assert len(test_prop.allOf) == 1
-        >>> assert isinstance(test_prop.allOf[0], Schema)
-        >>> assert test_prop.allOf[0].ref == "test_ref"
-        >>> assert test_prop.ref is None
-    """
-    if not _is_xml_schema_empty(xml_schema):
-        prop.xml = xml_schema
-        if prop.ref is not None:
-            prop.allOf = [Schema(**{"$ref": prop.ref})]
-            prop.ref = None
-
-
-_FILTER_XSDATA_METADATA = {"name", "type"}
-
-
-def _filter_xsdata_metadata(metadata: Mapping[str, Any]) -> Dict[str, Any]:
-    """
-    .. doctest::
-
-        >>> test_value = {**{"key": "value"}, **{k: k for k in _FILTER_XSDATA_METADATA}}
-        >>> test_result = _filter_xsdata_metadata(test_value)
-        >>> assert all(k not in test_result for k in _FILTER_XSDATA_METADATA)
-    """
-    return {k: v for k, v in metadata.items() if k not in _FILTER_XSDATA_METADATA}
-
-
-def _create_pydantic_model_from_dataclass(
-    dc_cls: "Type[Dataclass]",
-    config: "Type[Any]" = BaseConfig,
-    dc_cls_doc: Optional[str] = None,
-) -> "Type[BaseModel]":  # pragma: nocover
-    # Repository: https://github.com/pydantic/pydantic
-    # pydanics's license copy is blow.
-    #
-    # The MIT License (MIT)
-    #
-    # Copyright (c) 2017 - 2022 Samuel Colvin and other contributors
-    #
-    # Permission is hereby granted, free of charge, to any person obtaining a copy
-    # of this software and associated documentation files (the "Software"), to deal
-    # in the Software without restriction, including without limitation the rights
-    # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-    # copies of the Software, and to permit persons to whom the Software is
-    # furnished to do so, subject to the following conditions:
-    #
-    # The above copyright notice and this permission notice shall be included in all
-    # copies or substantial portions of the Software.
-    #
-    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-    # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-    # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-    # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-    # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-    # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-    # SOFTWARE.
-    import dataclasses
-
-    field_definitions: Dict[str, Any] = {}
-    for field in dataclasses.fields(dc_cls):
-        default: Any = Undefined
-        default_factory: Optional[NoArgAnyCallable] = None
-        field_info: FieldInfo
-
-        if field.default is not dataclasses.MISSING:
-            default = field.default
-        elif field.default_factory is not dataclasses.MISSING:
-            default_factory = field.default_factory
-        else:
-            default = Required
-
-        if isinstance(default, FieldInfo):
-            field_info = default
-            dc_cls.__pydantic_has_field_info_default__ = True
-        else:
-            # BEGIN EDIT
-            field_info = PydanticField(
-                default=default,
-                default_factory=default_factory,
-                **_filter_xsdata_metadata(field.metadata),
-            )
-            # END EDIT
-        field_definitions[field.name] = (field.type, field_info)
-
-    validators = gather_all_validators(dc_cls)
-    model: "Type[BaseModel]" = create_model(
-        dc_cls.__name__,
-        __config__=config,
-        __module__=dc_cls.__module__,
-        __validators__=validators,
-        __cls_kwargs__={"__resolve_forward_refs__": False},
-        **field_definitions,
-    )
-    model.__doc__ = dc_cls_doc if dc_cls_doc is not None else dc_cls.__doc__ or ""
-    return model
-
-
-def _add_field_schema(
-    dclazz: "Type[object]",
-    model_field: "Field[Any]",
-    model_schema: Schema,
-    ns_map: Mapping[str, str],
-) -> None:
-    """
-    .. testsetup::
-
-        >>> from dataclasses import field
-
-    .. doctest:: empty properties
-
-        >>> test_schema = Schema()
-        >>> _add_field_schema(object, field(), test_schema, {})
-        >>> assert len(test_schema.dict(exclude_none=True)) == 0
-    """
-    # TODO: complete doctest
-    if model_schema.properties is None:
-        return
-
-    model_meta = getattr(dclazz, "Meta", type)
-    prop = model_schema.properties[model_field.name]
-    namespace = model_field.metadata.get("namespace")
-    is_attribute = model_field.metadata.get("type") == XmlType.ATTRIBUTE
-    meta_name = model_field.metadata.get("name")
-    wrapper_name = model_field.metadata.get("wrapper")
-    prefix = None if not isinstance(namespace, str) else ns_map.get(namespace)
-    name_gen = (
-        _get_attribute_name_generator(model_meta)
-        if is_attribute
-        else _get_element_name_generator(model_meta)
-    )
-
-    if wrapper_name is None:
-        field_name = name_gen(meta_name) if meta_name is not None else None
-        array_name = None
-    else:
-        field_name = name_gen(wrapper_name)
-        array_name = name_gen(meta_name) if meta_name is not None else None
-
-    if wrapper_name is not None and prop.type != "array":
-        raise TypeError(
-            f"invalid wrapping type on {dclazz.__name__}.{model_field.name}: "
-            f"{prop.type}"
-        )
-
-    xml_schema = XML(
-        name=field_name,
-        namespace=namespace,
-        prefix=prefix,
-        attribute=is_attribute if is_attribute is True else None,
-        wrapped=True if wrapper_name is not None else None,
-    )
-    if not _is_xml_schema_empty(xml_schema):
-        prop.xml = xml_schema
-    # TODO: oneof allOf pydantic referenced
-    # _switch_ref_to_one_of(
-    #    prop,
-    #    XML(
-    #        name=field_name,
-    #        namespace=namespace,
-    #        prefix=prefix,
-    #        attribute=is_attribute if is_attribute is True else None,
-    #        wrapped=True if wrapper_name is not None else None,
-    #    ),
-    # )
-
-    if prop.type == "array":
-        if not isinstance(prop.items, Schema):
-            raise TypeError(
-                f"missing property items on {model_schema.title}.{model_field.name}"
-            )
-        _switch_ref_to_all_of(
-            prop.items,
-            XML(
-                name=array_name,
-                namespace=None,
-                prefix=None,
-                attribute=None,
-                wrapped=None,
-            ),
-        )
-
-
-def _get_route_models(app: FastAPI, openapi: OpenAPI) -> List["Type[BaseModel]"]:
-    """
-    .. testsetup:
-
-        >>> from dataclasses import dataclass
-
-    .. doctest::
-
-        >>> @dataclass
-        ... class TestModel:
-        ...     x: str
-
-        >>> app = FastAPI()
-        >>> @app.router.get("/", response_model=TestModel)
-        ... def dummy_endpoint() -> None:  # pragma: no cover
-        ...     pass
-
-        >>> openapi = OpenAPI(**app.openapi())
-        >>> models = _get_route_models(app, openapi)
-        >>> assert len(models) == 1
-        >>> assert issubclass(models[0], BaseModel)
-        >>> assert models[0].__name__ == TestModel.__name__
-    """
-    return [
-        model
-        for model in get_flat_models_from_routes(app.routes)
-        if isinstance(openapi.components, Components)
-        and isinstance(openapi.components.schemas, dict)
-        and model.__name__ in openapi.components.schemas
-        and issubclass(model, BaseModel)
-    ]
-
-
-def add_openapi_xml_schema(
-    app: FastAPI, openapi: OpenAPI, ns_map: Optional[Mapping[str, str]] = None
-) -> bool:
-    """
-    .. testsetup::
-
-        >>> from fastapi.openapi.models import Components
-        >>> from dataclasses import dataclass
-
-        >>> @dataclass
-        ... class TestModel:
-        ...     x: str
-
-        >>> test_app = FastAPI()
-        >>> @test_app.router.get("/", response_model=TestModel)
-        ... def dummy_endpoint() -> None:  # pragma: no cover
-        ...     pass
-
-    .. doctest:: test schema modified
-
-        >>> test_app.openapi_schema = None
-        >>> test_openapi = OpenAPI(**test_app.openapi())
-        >>> assert add_openapi_xml_schema(test_app, test_openapi) is True
-
-    .. doctest:: Return None if components or its schemas are missing
-
-        >>> test_openapi = OpenAPI(**test_app.openapi())
-        >>> test_openapi.components = None
-        >>> assert add_openapi_xml_schema(test_app, test_openapi) is False
-        >>> test_openapi.components = Components()
-        >>> assert add_openapi_xml_schema(test_app, test_openapi) is False
-    """
-    if openapi.components is None or openapi.components.schemas is None:
-        return False
-
-    ns_map = ns_map or {}
-    flat_models = _get_route_models(app, openapi)
-    all_dataclazzes = _get_all_dataclasses()
-
-    model_counter = 0
-    field_counter = 0
-    for model in flat_models:
-        dclazz = _get_dataclass(model, all_dataclazzes)
-        if dclazz is not None:
-            rewrite_model = _create_pydantic_model_from_dataclass(dclazz)
-            model_schema = Schema(
-                **rewrite_model.schema(
-                    by_alias=True, ref_template="#/components/schemas/{model}"
-                )
-            )
-            openapi.components.schemas[model.__name__] = model_schema
-            if isinstance(model_schema, Schema):
-                _add_model_schema(dclazz, model_schema, ns_map)
-                model_counter += 1
-
-                for field in fields(dclazz):
-                    _add_field_schema(dclazz, field, model_schema, ns_map)
-                    field_counter += 1
-    return model_counter > 0 and field_counter > 0
diff --git a/poetry.lock b/poetry.lock
index 57cdfed..5407a85 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,1120 +1,254 @@
+# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
+
 [[package]]
-name = "alabaster"
-version = "0.7.12"
-description = "A configurable sidebar-enabled Sphinx theme"
-category = "main"
+name = "annotated-types"
+version = "0.6.0"
+description = "Reusable constraint types to use with typing.Annotated"
 optional = false
-python-versions = "*"
+python-versions = ">=3.8"
+files = [
+    {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"},
+    {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"},
+]
 
 [[package]]
 name = "anyio"
-version = "3.6.2"
+version = "4.3.0"
 description = "High level compatibility layer for multiple asynchronous event loop implementations"
-category = "main"
 optional = false
-python-versions = ">=3.6.2"
+python-versions = ">=3.8"
+files = [
+    {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"},
+    {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"},
+]
 
 [package.dependencies]
+exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
 idna = ">=2.8"
 sniffio = ">=1.1"
-typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
-
-[package.extras]
-doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
-test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
-trio = ["trio (>=0.16,<0.22)"]
-
-[[package]]
-name = "attrs"
-version = "22.1.0"
-description = "Classes Without Boilerplate"
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.extras]
-dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
-docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
-tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
-tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
-
-[[package]]
-name = "babel"
-version = "2.10.3"
-description = "Internationalization utilities"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-pytz = ">=2015.7"
-
-[[package]]
-name = "certifi"
-version = "2022.9.24"
-description = "Python package for providing Mozilla's CA Bundle."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[[package]]
-name = "cfgv"
-version = "3.3.1"
-description = "Validate configuration and produce human readable error messages."
-category = "main"
-optional = false
-python-versions = ">=3.6.1"
-
-[[package]]
-name = "charset-normalizer"
-version = "2.1.1"
-description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-category = "main"
-optional = false
-python-versions = ">=3.6.0"
+typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""}
 
 [package.extras]
-unicode-backport = ["unicodedata2"]
+doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
+test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
+trio = ["trio (>=0.23)"]
 
 [[package]]
-name = "click"
-version = "8.1.3"
-description = "Composable command line interface toolkit"
-category = "main"
+name = "exceptiongroup"
+version = "1.2.0"
+description = "Backport of PEP 654 (exception groups)"
 optional = false
 python-versions = ">=3.7"
-
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
-
-[[package]]
-name = "colorama"
-version = "0.4.5"
-description = "Cross-platform colored terminal text."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[[package]]
-name = "coverage"
-version = "6.5.0"
-description = "Code coverage measurement for Python"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
+files = [
+    {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
+    {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
+]
 
 [package.extras]
-toml = ["tomli"]
-
-[[package]]
-name = "distlib"
-version = "0.3.6"
-description = "Distribution utilities"
-category = "main"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "docutils"
-version = "0.19"
-description = "Docutils -- Python Documentation Utilities"
-category = "main"
-optional = false
-python-versions = ">=3.7"
+test = ["pytest (>=6)"]
 
 [[package]]
 name = "fastapi"
-version = "0.85.1"
+version = "0.110.0"
 description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
-category = "main"
 optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
-starlette = "0.20.4"
-
-[package.extras]
-all = ["email-validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "uvicorn[standard] (>=0.12.0,<0.19.0)"]
-dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "uvicorn[standard] (>=0.12.0,<0.19.0)"]
-doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.7.0)"]
-test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.971)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-orjson (==3.6.2)", "types-ujson (==5.4.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]
-
-[[package]]
-name = "filelock"
-version = "3.8.0"
-description = "A platform independent file lock."
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.extras]
-docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"]
-testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"]
-
-[[package]]
-name = "h11"
-version = "0.14.0"
-description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
-category = "main"
-optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+files = [
+    {file = "fastapi-0.110.0-py3-none-any.whl", hash = "sha256:87a1f6fb632a218222c5984be540055346a8f5d8a68e8f6fb647b1dc9934de4b"},
+    {file = "fastapi-0.110.0.tar.gz", hash = "sha256:266775f0dcc95af9d3ef39bad55cff525329a931d5fd51930aadd4f428bf7ff3"},
+]
 
 [package.dependencies]
-typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
-
-[[package]]
-name = "identify"
-version = "2.5.6"
-description = "File identification library for Python"
-category = "main"
-optional = false
-python-versions = ">=3.7"
+pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
+starlette = ">=0.36.3,<0.37.0"
+typing-extensions = ">=4.8.0"
 
 [package.extras]
-license = ["ukkonen"]
+all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
 
 [[package]]
 name = "idna"
-version = "3.4"
+version = "3.6"
 description = "Internationalized Domain Names in Applications (IDNA)"
-category = "main"
 optional = false
 python-versions = ">=3.5"
-
-[[package]]
-name = "imagesize"
-version = "1.4.1"
-description = "Getting image size from png/jpeg/jpeg2000/gif file"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[[package]]
-name = "importlib-metadata"
-version = "5.0.0"
-description = "Read metadata from Python packages"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
-zipp = ">=0.5"
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
-perf = ["ipython"]
-testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
-
-[[package]]
-name = "iniconfig"
-version = "1.1.1"
-description = "iniconfig: brain-dead simple config-ini parsing"
-category = "main"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "jinja2"
-version = "3.1.2"
-description = "A very fast and expressive template engine."
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-MarkupSafe = ">=2.0"
-
-[package.extras]
-i18n = ["Babel (>=2.7)"]
-
-[[package]]
-name = "markupsafe"
-version = "2.1.1"
-description = "Safely add untrusted strings to HTML/XML markup."
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[[package]]
-name = "nodeenv"
-version = "1.7.0"
-description = "Node.js virtual environment builder"
-category = "main"
-optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
-
-[package.dependencies]
-setuptools = "*"
-
-[[package]]
-name = "packaging"
-version = "21.3"
-description = "Core utilities for Python packages"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
-
-[[package]]
-name = "platformdirs"
-version = "2.5.2"
-description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.extras]
-docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"]
-test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
-
-[[package]]
-name = "pluggy"
-version = "1.0.0"
-description = "plugin and hook calling mechanisms for python"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
-
-[package.extras]
-dev = ["pre-commit", "tox"]
-testing = ["pytest", "pytest-benchmark"]
-
-[[package]]
-name = "pre-commit"
-version = "2.20.0"
-description = "A framework for managing and maintaining multi-language pre-commit hooks."
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-cfgv = ">=2.0.0"
-identify = ">=1.0.0"
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
-nodeenv = ">=0.11.1"
-pyyaml = ">=5.1"
-toml = "*"
-virtualenv = ">=20.0.8"
-
-[[package]]
-name = "py"
-version = "1.11.0"
-description = "library with cross-python path, ini-parsing, io, code, log facilities"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+    {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
+    {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
+]
 
 [[package]]
 name = "pydantic"
-version = "1.10.2"
-description = "Data validation and settings management using python type hints"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-typing-extensions = ">=4.1.0"
-
-[package.extras]
-dotenv = ["python-dotenv (>=0.10.4)"]
-email = ["email-validator (>=1.0.3)"]
-
-[[package]]
-name = "pygments"
-version = "2.13.0"
-description = "Pygments is a syntax highlighting package written in Python."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-plugins = ["importlib-metadata"]
-
-[[package]]
-name = "pyparsing"
-version = "3.0.9"
-description = "pyparsing module - Classes and methods to define and execute parsing grammars"
-category = "main"
-optional = false
-python-versions = ">=3.6.8"
-
-[package.extras]
-diagrams = ["jinja2", "railroad-diagrams"]
-
-[[package]]
-name = "pytest"
-version = "7.1.3"
-description = "pytest: simple powerful testing with Python"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-attrs = ">=19.2.0"
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
-iniconfig = "*"
-packaging = "*"
-pluggy = ">=0.12,<2.0"
-py = ">=1.8.2"
-tomli = ">=1.0.0"
-
-[package.extras]
-testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
-
-[[package]]
-name = "pytest-cov"
-version = "4.0.0"
-description = "Pytest plugin for measuring coverage."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-coverage = {version = ">=5.2.1", extras = ["toml"]}
-pytest = ">=4.6"
-
-[package.extras]
-testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
-
-[[package]]
-name = "pytz"
-version = "2022.5"
-description = "World timezone definitions, modern and historical"
-category = "main"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "pyyaml"
-version = "6.0"
-description = "YAML parser and emitter for Python"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[[package]]
-name = "requests"
-version = "2.28.1"
-description = "Python HTTP for Humans."
-category = "main"
-optional = false
-python-versions = ">=3.7, <4"
-
-[package.dependencies]
-certifi = ">=2017.4.17"
-charset-normalizer = ">=2,<3"
-idna = ">=2.5,<4"
-urllib3 = ">=1.21.1,<1.27"
-
-[package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)"]
-use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
-
-[[package]]
-name = "setuptools"
-version = "65.5.0"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
-
-[[package]]
-name = "six"
-version = "1.16.0"
-description = "Python 2 and 3 compatibility utilities"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+version = "2.6.3"
+description = "Data validation using Python type hints"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"},
+    {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"},
+]
+
+[package.dependencies]
+annotated-types = ">=0.4.0"
+pydantic-core = "2.16.3"
+typing-extensions = ">=4.6.1"
+
+[package.extras]
+email = ["email-validator (>=2.0.0)"]
+
+[[package]]
+name = "pydantic-core"
+version = "2.16.3"
+description = ""
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"},
+    {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"},
+    {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"},
+    {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"},
+    {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"},
+    {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"},
+    {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"},
+    {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"},
+    {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"},
+    {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"},
+    {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"},
+    {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"},
+    {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"},
+    {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"},
+    {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"},
+    {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"},
+    {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"},
+    {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"},
+    {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"},
+    {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"},
+    {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"},
+    {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"},
+    {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"},
+    {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"},
+    {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"},
+    {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"},
+    {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"},
+    {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"},
+    {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"},
+    {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"},
+    {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"},
+    {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"},
+    {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"},
+    {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"},
+    {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"},
+    {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"},
+    {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"},
+    {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"},
+    {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"},
+    {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"},
+    {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"},
+    {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"},
+    {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"},
+    {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"},
+    {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"},
+    {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"},
+    {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"},
+    {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"},
+    {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"},
+    {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"},
+    {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"},
+    {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"},
+    {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"},
+    {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"},
+    {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"},
+    {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"},
+    {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"},
+    {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"},
+    {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"},
+    {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"},
+    {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"},
+    {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"},
+    {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"},
+    {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"},
+    {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"},
+    {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"},
+    {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"},
+    {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"},
+    {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"},
+    {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"},
+    {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"},
+    {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"},
+    {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"},
+    {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"},
+    {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"},
+    {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"},
+    {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"},
+    {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"},
+    {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"},
+]
+
+[package.dependencies]
+typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
 
 [[package]]
 name = "sniffio"
-version = "1.3.0"
+version = "1.3.1"
 description = "Sniff out which async library your code is running under"
-category = "main"
 optional = false
 python-versions = ">=3.7"
-
-[[package]]
-name = "snowballstemmer"
-version = "2.2.0"
-description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
-category = "main"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "sphinx"
-version = "5.3.0"
-description = "Python documentation generator"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-alabaster = ">=0.7,<0.8"
-babel = ">=2.9"
-colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
-docutils = ">=0.14,<0.20"
-imagesize = ">=1.3"
-importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""}
-Jinja2 = ">=3.0"
-packaging = ">=21.0"
-Pygments = ">=2.12"
-requests = ">=2.5.0"
-snowballstemmer = ">=2.0"
-sphinxcontrib-applehelp = "*"
-sphinxcontrib-devhelp = "*"
-sphinxcontrib-htmlhelp = ">=2.0.0"
-sphinxcontrib-jsmath = "*"
-sphinxcontrib-qthelp = "*"
-sphinxcontrib-serializinghtml = ">=1.1.5"
-
-[package.extras]
-docs = ["sphinxcontrib-websupport"]
-lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"]
-test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"]
-
-[[package]]
-name = "sphinxcontrib-applehelp"
-version = "1.0.2"
-description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books"
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.extras]
-lint = ["docutils-stubs", "flake8", "mypy"]
-test = ["pytest"]
-
-[[package]]
-name = "sphinxcontrib-devhelp"
-version = "1.0.2"
-description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.extras]
-lint = ["docutils-stubs", "flake8", "mypy"]
-test = ["pytest"]
-
-[[package]]
-name = "sphinxcontrib-htmlhelp"
-version = "2.0.0"
-description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-lint = ["docutils-stubs", "flake8", "mypy"]
-test = ["html5lib", "pytest"]
-
-[[package]]
-name = "sphinxcontrib-jsmath"
-version = "1.0.1"
-description = "A sphinx extension which renders display math in HTML via JavaScript"
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.extras]
-test = ["flake8", "mypy", "pytest"]
-
-[[package]]
-name = "sphinxcontrib-qthelp"
-version = "1.0.3"
-description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.extras]
-lint = ["docutils-stubs", "flake8", "mypy"]
-test = ["pytest"]
-
-[[package]]
-name = "sphinxcontrib-serializinghtml"
-version = "1.1.5"
-description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.extras]
-lint = ["docutils-stubs", "flake8", "mypy"]
-test = ["pytest"]
+files = [
+    {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
+    {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
+]
 
 [[package]]
 name = "starlette"
-version = "0.20.4"
+version = "0.36.3"
 description = "The little ASGI library that shines."
-category = "main"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+files = [
+    {file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"},
+    {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"},
+]
 
 [package.dependencies]
 anyio = ">=3.4.0,<5"
 typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
 
 [package.extras]
-full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
-
-[[package]]
-name = "toml"
-version = "0.10.2"
-description = "Python Library for Tom's Obvious, Minimal Language"
-category = "main"
-optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-
-[[package]]
-name = "tomli"
-version = "2.0.1"
-description = "A lil' TOML parser"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[[package]]
-name = "tox"
-version = "3.26.0"
-description = "tox is a generic virtualenv management and test command line tool"
-category = "main"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
-
-[package.dependencies]
-colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""}
-filelock = ">=3.0.0"
-importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
-packaging = ">=14"
-pluggy = ">=0.12.0"
-py = ">=1.4.17"
-six = ">=1.14.0"
-tomli = {version = ">=2.0.1", markers = "python_version >= \"3.7\" and python_version < \"3.11\""}
-virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7"
-
-[package.extras]
-docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"]
-testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"]
-
-[[package]]
-name = "tox-poetry"
-version = "0.4.1"
-description = "Tox poetry plugin"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-pluggy = "*"
-toml = "*"
-tox = {version = ">=3.7.0", markers = "python_version >= \"3\""}
-
-[package.extras]
-test = ["coverage", "pycodestyle", "pylint", "pytest"]
+full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
 
 [[package]]
 name = "typing-extensions"
-version = "4.4.0"
-description = "Backported and Experimental Type Hints for Python 3.7+"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[[package]]
-name = "urllib3"
-version = "1.26.12"
-description = "HTTP library with thread-safe connection pooling, file post, and more."
-category = "main"
+version = "4.10.0"
+description = "Backported and Experimental Type Hints for Python 3.8+"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
-
-[package.extras]
-brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
-secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
-socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
-
-[[package]]
-name = "uvicorn"
-version = "0.18.3"
-description = "The lightning-fast ASGI server."
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-click = ">=7.0"
-h11 = ">=0.8"
-typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
-
-[package.extras]
-standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"]
-
-[[package]]
-name = "virtualenv"
-version = "20.16.5"
-description = "Virtual Python Environment builder"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-distlib = ">=0.3.5,<1"
-filelock = ">=3.4.1,<4"
-importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""}
-platformdirs = ">=2.4,<3"
-
-[package.extras]
-docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"]
-testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"]
+python-versions = ">=3.8"
+files = [
+    {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
+    {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
+]
 
 [[package]]
 name = "xsdata"
-version = "22.9"
+version = "24.3.1"
 description = "Python XML Binding"
-category = "main"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+files = [
+    {file = "xsdata-24.3.1-py3-none-any.whl", hash = "sha256:2f4a00ec33fb6ff41fca95f3fac826b83fc34d8644e2f3da830ad393defb5705"},
+    {file = "xsdata-24.3.1.tar.gz", hash = "sha256:cf6c6895616260cbe2a4eff6f8f906cd16f9c1dba8fd8561e500b91269b86616"},
+]
 
 [package.dependencies]
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+typing-extensions = "*"
 
 [package.extras]
-cli = ["click (>=5.0)", "click-default-group (>=1.2)", "docformatter", "jinja2 (>=2.10)", "toposort (>=1.5)"]
-docs = ["furo", "sphinx", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx-copybutton", "sphinx-inline-tabs"]
-lxml = ["lxml (>=4.4.1)"]
+cli = ["click (>=5.0)", "click-default-group (>=1.2)", "docformatter (>=1.7.2)", "jinja2 (>=2.10)", "ruff (>=0.3.0)", "toposort (>=1.5)"]
+docs = ["markdown-exec[ansi]", "mkdocs", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material", "mkdocs-minify-plugin", "mkdocstrings[python]", "pymdownx-superfence-filter-lines"]
+lxml = ["lxml (>=4.5.0)"]
 soap = ["requests"]
-test = ["codecov", "pre-commit", "pytest", "pytest-benchmark", "pytest-cov", "tox"]
-
-[[package]]
-name = "zipp"
-version = "3.9.0"
-description = "Backport of pathlib-compatible object wrapper for zip files"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
-testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
-
-[extras]
-docs = ["Sphinx"]
-local = ["uvicorn"]
-testing = ["pytest", "pytest-cov", "pre-commit", "tox-poetry"]
+test = ["pre-commit", "pytest", "pytest-benchmark", "pytest-cov"]
 
 [metadata]
-lock-version = "1.1"
-python-versions = ">= 3.7,<4.0"
-content-hash = "14cd8034536625222bbdb33ebe9472ec201da3bca256da0b88798a230c25f5dc"
-
-[metadata.files]
-alabaster = [
-    {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
-    {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
-]
-anyio = [
-    {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"},
-    {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"},
-]
-attrs = [
-    {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
-    {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
-]
-babel = [
-    {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"},
-    {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"},
-]
-certifi = [
-    {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
-    {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
-]
-cfgv = [
-    {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
-    {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
-]
-charset-normalizer = [
-    {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
-    {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
-]
-click = [
-    {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
-    {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
-]
-colorama = [
-    {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
-    {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
-]
-coverage = [
-    {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"},
-    {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"},
-    {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"},
-    {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"},
-    {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"},
-    {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"},
-    {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"},
-    {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"},
-    {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"},
-    {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"},
-    {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"},
-    {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"},
-    {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"},
-    {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"},
-    {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"},
-    {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"},
-    {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"},
-    {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"},
-    {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"},
-    {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"},
-    {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"},
-    {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"},
-    {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"},
-    {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"},
-    {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"},
-    {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"},
-    {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"},
-    {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"},
-    {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"},
-    {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"},
-    {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"},
-    {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"},
-    {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"},
-    {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"},
-    {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"},
-    {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"},
-    {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"},
-    {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"},
-    {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"},
-    {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"},
-    {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"},
-    {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"},
-    {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"},
-    {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"},
-    {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"},
-    {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"},
-    {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"},
-    {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"},
-    {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"},
-    {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"},
-]
-distlib = [
-    {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"},
-    {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"},
-]
-docutils = [
-    {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"},
-    {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"},
-]
-fastapi = [
-    {file = "fastapi-0.85.1-py3-none-any.whl", hash = "sha256:de3166b6b1163dc22da4dc4ebdc3192fcbac7700dd1870a1afa44de636a636b5"},
-    {file = "fastapi-0.85.1.tar.gz", hash = "sha256:1facd097189682a4ff11cbd01334a992e51b56be663b2bd50c2c09523624f144"},
-]
-filelock = [
-    {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"},
-    {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"},
-]
-h11 = [
-    {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
-    {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
-]
-identify = [
-    {file = "identify-2.5.6-py2.py3-none-any.whl", hash = "sha256:b276db7ec52d7e89f5bc4653380e33054ddc803d25875952ad90b0f012cbcdaa"},
-    {file = "identify-2.5.6.tar.gz", hash = "sha256:6c32dbd747aa4ceee1df33f25fed0b0f6e0d65721b15bd151307ff7056d50245"},
-]
-idna = [
-    {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
-    {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
-]
-imagesize = [
-    {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"},
-    {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"},
-]
-importlib-metadata = [
-    {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"},
-    {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"},
-]
-iniconfig = [
-    {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
-    {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
-]
-jinja2 = [
-    {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
-    {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
-]
-markupsafe = [
-    {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
-    {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
-    {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
-    {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
-    {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
-    {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
-    {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
-    {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
-    {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
-    {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
-    {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
-    {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
-    {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
-    {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
-    {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
-    {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
-    {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
-    {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
-    {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
-    {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
-    {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
-    {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
-    {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
-    {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
-    {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
-    {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
-    {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
-    {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
-    {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
-    {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
-    {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
-    {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
-    {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
-    {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
-    {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
-    {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
-    {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
-    {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
-    {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
-    {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
-]
-nodeenv = [
-    {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"},
-    {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"},
-]
-packaging = [
-    {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
-    {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
-]
-platformdirs = [
-    {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
-    {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
-]
-pluggy = [
-    {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
-    {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
-]
-pre-commit = [
-    {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"},
-    {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"},
-]
-py = [
-    {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
-    {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
-]
-pydantic = [
-    {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
-    {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"},
-    {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"},
-    {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"},
-    {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"},
-    {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"},
-    {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"},
-    {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"},
-    {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"},
-    {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"},
-    {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"},
-    {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"},
-    {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"},
-    {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"},
-    {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"},
-    {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"},
-    {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"},
-    {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"},
-    {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"},
-    {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"},
-    {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"},
-    {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"},
-    {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"},
-    {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"},
-    {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"},
-    {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"},
-    {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"},
-    {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"},
-    {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"},
-    {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"},
-    {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"},
-    {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"},
-    {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"},
-    {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"},
-    {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
-    {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
-]
-pygments = [
-    {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"},
-    {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"},
-]
-pyparsing = [
-    {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
-    {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
-]
-pytest = [
-    {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"},
-    {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
-]
-pytest-cov = [
-    {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"},
-    {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"},
-]
-pytz = [
-    {file = "pytz-2022.5-py2.py3-none-any.whl", hash = "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22"},
-    {file = "pytz-2022.5.tar.gz", hash = "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914"},
-]
-pyyaml = [
-    {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
-    {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
-    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
-    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
-    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
-    {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
-    {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
-    {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
-    {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
-    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
-    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
-    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
-    {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
-    {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
-    {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
-    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
-    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
-    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
-    {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
-    {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
-    {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
-    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
-    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
-    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
-    {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
-    {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
-    {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
-    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
-    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
-    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
-    {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
-    {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
-    {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
-    {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
-    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
-    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
-    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
-    {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
-    {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
-    {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
-]
-requests = [
-    {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
-    {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
-]
-setuptools = [
-    {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"},
-    {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"},
-]
-six = [
-    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
-    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
-]
-sniffio = [
-    {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
-    {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
-]
-snowballstemmer = [
-    {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
-    {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
-]
-sphinx = [
-    {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"},
-    {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"},
-]
-sphinxcontrib-applehelp = [
-    {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
-    {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"},
-]
-sphinxcontrib-devhelp = [
-    {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"},
-    {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"},
-]
-sphinxcontrib-htmlhelp = [
-    {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"},
-    {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"},
-]
-sphinxcontrib-jsmath = [
-    {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
-    {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
-]
-sphinxcontrib-qthelp = [
-    {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
-    {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},
-]
-sphinxcontrib-serializinghtml = [
-    {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"},
-    {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"},
-]
-starlette = [
-    {file = "starlette-0.20.4-py3-none-any.whl", hash = "sha256:c0414d5a56297d37f3db96a84034d61ce29889b9eaccf65eb98a0b39441fcaa3"},
-    {file = "starlette-0.20.4.tar.gz", hash = "sha256:42fcf3122f998fefce3e2c5ad7e5edbf0f02cf685d646a83a08d404726af5084"},
-]
-toml = [
-    {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
-    {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
-]
-tomli = [
-    {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
-    {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
-]
-tox = [
-    {file = "tox-3.26.0-py2.py3-none-any.whl", hash = "sha256:bf037662d7c740d15c9924ba23bb3e587df20598697bb985ac2b49bdc2d847f6"},
-    {file = "tox-3.26.0.tar.gz", hash = "sha256:44f3c347c68c2c68799d7d44f1808f9d396fc8a1a500cbc624253375c7ae107e"},
-]
-tox-poetry = [
-    {file = "tox-poetry-0.4.1.tar.gz", hash = "sha256:2395808e1ce487b5894c10f2202e14702bfa6d6909c0d1e525170d14809ac7ef"},
-    {file = "tox_poetry-0.4.1-py2.py3-none-any.whl", hash = "sha256:11d9cd4e51d4cd9484b3ba63f2650ab4cfb4096e5f0682ecf561ddfc3c8e8c92"},
-]
-typing-extensions = [
-    {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
-    {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
-]
-urllib3 = [
-    {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
-    {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
-]
-uvicorn = [
-    {file = "uvicorn-0.18.3-py3-none-any.whl", hash = "sha256:0abd429ebb41e604ed8d2be6c60530de3408f250e8d2d84967d85ba9e86fe3af"},
-    {file = "uvicorn-0.18.3.tar.gz", hash = "sha256:9a66e7c42a2a95222f76ec24a4b754c158261c4696e683b9dadc72b590e0311b"},
-]
-virtualenv = [
-    {file = "virtualenv-20.16.5-py3-none-any.whl", hash = "sha256:d07dfc5df5e4e0dbc92862350ad87a36ed505b978f6c39609dc489eadd5b0d27"},
-    {file = "virtualenv-20.16.5.tar.gz", hash = "sha256:227ea1b9994fdc5ea31977ba3383ef296d7472ea85be9d6732e42a91c04e80da"},
-]
-xsdata = [
-    {file = "xsdata-22.9-py3-none-any.whl", hash = "sha256:090cc780861dbf2bb7ae7cdac08cddba1c8000fba08b9915fdb45be8f8807b78"},
-    {file = "xsdata-22.9.tar.gz", hash = "sha256:c62d5002b4de59baca4c4ea9edfdc08fb7759713ec21139aaeebff20cc3e7cfc"},
-]
-zipp = [
-    {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"},
-    {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"},
-]
+lock-version = "2.0"
+python-versions = ">= 3.9, < 4.0"
+content-hash = "31721cf8d59df038fc894fcc48cd429f3f51ffd0089bb5edd4a148fb292bc139"
diff --git a/pyproject.toml b/pyproject.toml
index b800105..2144d09 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,29 +17,14 @@ packages = [
 ]
 
 [tool.poetry.dependencies]
-python = ">= 3.7,<4.0"
+python = ">= 3.9, < 4.0"
 fastapi = ">= 0.70.0"
-pydantic = ">= 1.10.2,<1.11 "
+pydantic = ">= 2.0"
 xsdata = ">= 22.9"
 
-pre-commit = {version = "^2.20.0"}
-Sphinx = {version = "^5.2.3"}
-
-uvicorn = {version = "^0.18.2"}
-
-tox-poetry = {version = "^0.4.1"}
-tox = {version = "^3.26.0"}
-pytest = {version = "^7.1.3"}
-pytest-cov = {version = "^4.0.0"}
-
 [tool.poetry-version-plugin]
 source = "git-tag"
 
-[tool.poetry.extras]
-docs = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-napoleon"]
-testing = ["pytest", "pytest-cov", "pre-commit", "tox-poetry"]
-local = ["uvicorn"]
-
 [build-system]
 requires = ["poetry-core>=1.0.0"]
 build-backend = "poetry.core.masonry.api"
diff --git a/tox.ini b/tox.ini
index a9ae881..0f6a4ba 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,16 +6,15 @@ requires =
     pytest
 envlist =
     docs
-    py37
     py38
     py39
     py310
     py311
-    pypy3
+    py312
 
 [flake8]
 exclude = tests/*
-max-line-length = 88
+max-line-length = 120
 ignore = ANN101,ANN401,ANN102,B008,D004
 extend-ignore = W503,E203
 
@@ -31,15 +30,13 @@ pythonpath =
     fastapi_xml
 
 [doc8]
-max-line-length = 88
+max-line-length = 120
 
 [testenv]
 deps =
     pytest
+    pytest-cov
     poetry
-extras =
-    docs
-    testing
 commands =
     poetry install
     poetry run pytest --basetemp="{envtmpdir}" {posargs}
@@ -48,8 +45,9 @@ commands =
 deps =
     poetry
     sphinx
+    sphinx-rtd-theme
+    sphinxcontrib-napoleon
 basepython = python3.9
-extras = docs
 changedir = docs
 commands =
     poetry run sphinx-build -b html . _build


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 6438c94..ea91d10 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -14,14 +14,10 @@ jobs:
       fail-fast: false
       matrix:
         include:
-          - {name: PyPy 3.7, python: 'pypy-3.7', os: ubuntu}
-          - {name: Python 3.7, python: '3.7', os: ubuntu}
-          - {name: Python 3.8, python: '3.8', os: ubuntu}
           - {name: Python 3.9, python: '3.9', os: ubuntu}
           - {name: Python 3.10, python: '3.10', os: ubuntu}
-          - {name: Python 3.11-dev, python: '3.11-dev', os: ubuntu}
-          - {name: Windows py37, python: '3.7', os: windows}
-          #- {name: MacOS py37, python: '3.7', os: macos}
+          - {name: Python 3.11, python: '3.11', os: ubuntu}
+          - {name: Python 3.12, python: '3.12', os: ubuntu}
     steps:
     - uses: actions/checkout@v3
     - name: Set up Python ${{ matrix.python }}
@@ -30,7 +26,7 @@ jobs:
         python-version: ${{ matrix.python }}
     - name: Install dependencies
       run: |
-        python -m pip install --upgrade pip tox tox-poetry codecov
+        python -m pip install --upgrade pip tox pytest pytest-cov
     - name: Tox
       run: |
         python3 -m tox -e py
diff --git a/tests/test_decoder.py b/tests/test_decoder.py
new file mode 100644
index 0000000..e886bf3
--- /dev/null
+++ b/tests/test_decoder.py
@@ -0,0 +1,123 @@
+#  type: ignore
+import unittest
+from dataclasses import dataclass
+from dataclasses import field
+from typing import Any
+from typing import Dict
+
+from fastapi import APIRouter
+from fastapi import FastAPI
+from fastapi import Request
+from fastapi.routing import APIRoute
+from pydantic import BaseModel
+
+from fastapi_xml.decoder import BodyDecodeError
+from fastapi_xml.decoder import XmlDecoder
+from fastapi_xml.decoder import XmlParser
+from fastapi_xml.xmlbody import XmlBody
+
+
+class TestXmlDecoder(unittest.TestCase):
+    def setUp(self) -> None:
+        """
+        The setUp function is called before each test function.
+
+        It creates a FastAPI app with two endpoints, one using a
+        dataclass and the other not.
+        """
+
+        class NotADataClazz(BaseModel):
+            x: str
+
+        @dataclass
+        class Model:
+            x: str = field(metadata={"type": "Element"})
+
+        router = APIRouter()
+
+        @router.get("/model")
+        def endpoint_model(x: Model = XmlBody()) -> None:  # pragma: no cover
+            """dummy endpoint."""
+            pass
+
+        @router.get("/dclazz")
+        def endpoint_dclazz(x: NotADataClazz = XmlBody()) -> None:  # pragma: no cover
+            """dummy endpoint."""
+            pass
+
+        self.app = FastAPI()
+        self.app.include_router(router)
+        self.api_routes = [r for r in self.app.routes if isinstance(r, APIRoute)]
+
+    def test_get_parser(self) -> None:
+        """
+        The test_get_parser function tests the get_parser function in the
+        XmlDecoder class.
+
+        It asserts that an instance of XmlParser is returned.
+        """
+        result = XmlDecoder.get_parser()
+        self.assertIsInstance(result, XmlParser)
+
+    def test_decode__decode_body(self) -> None:
+        """
+        The function is responsible for evaluating the
+        :meth:`fastapi_xml.XmlDecoder.decode` function. This function
+        establishes a test environment, represented as a dictionary containing
+        details related to an HTTP request.
+
+        Within this test scope, it specifies the nature of the request (HTTP) and
+        indicates the absence of query parameters in this specific instance.
+        Furthermore, the scope includes headers, specifically one header titled "content-type"
+        with the value "text/xml." This signifies the intention to transmit XML data to the
+        API endpoint located at "/model/."
+
+        Subsequently, the test proceeds to craft a Request object, utilizing the test scope
+        as the input for its constructor method.
+        """
+        test_scope: Dict[str, Any] = {"type": "http", "query_string": ""}
+        route_model = [r for r in self.api_routes if r.path == "/model"][0]
+        test_scope["headers"] = [(b"content-type", b"text/xml")]
+        test_request = Request(scope=test_scope)
+        test_body = b"<Model><x>test</x></Model>"
+        test_field = route_model.body_field
+        test_result = XmlDecoder.decode(test_request, test_field, test_body)
+        self.assertIsInstance(test_result, dict)
+        self.assertTrue("x" in test_result)
+        self.assertEqual(test_result["x"], "test")
+
+    def test_decode__return_non_if_model_is_not_a_dataclass(self) -> None:
+        """
+        This function tests the :meth:`fastapi_xml.XmlDecoder.decode` method.
+
+        It validates that the function returns `None` if the body is
+        empty.
+        """
+        route_dclazz = [r for r in self.api_routes if r.path == "/dclazz"][0]
+        test_scope = {"type": "http", "query_string": "", "headers": []}
+        test_field = route_dclazz.body_field
+        request = Request(scope=test_scope)
+        self.assertIsNone(XmlDecoder.decode(request, test_field, b""))
+
+    def test_decode__BodyDecodeError(self) -> None:
+        """
+        This function tests the :meth:`XmlDecoder.decode` function to ensure
+        that it raises a BodyDecodeError if the content type is xml and an
+        error occurs during decoding.
+
+        Likewise, the test assures that no exception is thrown when the
+        content type is not xml.
+        """
+        route_model = [r for r in self.api_routes if r.path == "/model"][0]
+        test_field = route_model.body_field
+
+        # raise error if content type is xml
+        test_scope = {
+            "type": "http",
+            "query_string": "",
+            "headers": [(b"content-type", b"text/xml")],
+        }
+        test_request = Request(scope=test_scope)
+        self.assertRaises(
+            BodyDecodeError, XmlDecoder.decode, test_request, test_field, b"invalid"
+        )
diff --git a/tests/test_openapi_xml_estension.py b/tests/test_openapi.py
similarity index 55%
rename from tests/test_openapi_xml_estension.py
rename to tests/test_openapi.py
index 15b2ce4..6f796b3 100644
--- a/tests/test_openapi_xml_estension.py
+++ b/tests/test_openapi.py
@@ -1,3 +1,4 @@
+#  type: ignore
 import unittest
 from dataclasses import dataclass
 from dataclasses import Field
@@ -11,18 +12,22 @@
 from typing import TYPE_CHECKING
 from typing import Union
 
+import pydantic
+from fastapi import APIRouter
+from fastapi import FastAPI
+from fastapi.openapi.models import Components
+from fastapi.openapi.models import OpenAPI
 from fastapi.openapi.models import Schema
 from fastapi.openapi.models import XML
 from pydantic import BaseModel
+from pydantic import TypeAdapter
 from pydantic.dataclasses import dataclass as pydantic_dataclass
-from pydantic.dataclasses import DataclassProxy
-from pydantic.schema import default_ref_template
-from pydantic.schema import model_schema
 
-from fastapi_xml import xmlbody
+from fastapi_xml import openapi
+
 
 if TYPE_CHECKING:  # pragma: nocover
-    from pydantic.dataclasses import Dataclass
+    from pydantic.dataclasses import PydanticDataclass
 
 
 class OpenAPIXmlExtensionTests(unittest.TestCase):
@@ -38,10 +43,10 @@ def assertAllNoneExcept(
         self, obj: BaseModel, *exceptions: str, ignore: Optional[Set[str]] = None
     ) -> None:
         ignore = ignore or set()
-        all_fields = set(obj.__fields__.keys())
+        all_fields = set(obj.model_fields.keys())
         should_not_none = set(exceptions)
         should_be_none = all_fields - should_not_none
-        fields = obj.__fields__
+        fields = obj.model_fields
 
         check_valid_fields = (should_not_none | ignore | should_be_none) - all_fields
         self.assertEmpty(check_valid_fields, f"invalid fields: {check_valid_fields}")
@@ -67,12 +72,8 @@ def _get_dataclass_field(dclazz: "Type[object]", field_name: str) -> "Field[Any]
         return f
 
     @staticmethod
-    def _get_schema(model: Union["Type[Dataclass]", DataclassProxy]) -> Schema:
-        if isinstance(model, DataclassProxy):  # pragma: nocover
-            dclazz = model.__dataclass__
-        else:
-            dclazz = model
-        schema = model_schema(dclazz, by_alias=True, ref_template=default_ref_template)
+    def _get_schema(model: Union["PydanticDataclass"]) -> Schema:
+        schema = TypeAdapter(model).json_schema(by_alias=True)
         return Schema(**schema)
 
     def test_named_attribute(self) -> None:
@@ -85,8 +86,8 @@ class NamedAttribute:
         schema = self._get_schema(model)
         mfield = self._get_dataclass_field(dclazz, "x")
 
-        xmlbody._add_model_schema(dclazz, schema, {})
-        xmlbody._add_field_schema(dclazz, mfield, schema, {})
+        openapi._add_model_schema(dclazz, schema, {})
+        openapi._add_field_schema(dclazz, mfield, schema, {})
 
         self.assertAllNoneExcept(
             schema,
@@ -120,8 +121,8 @@ class WithoutMetaName:
         schema = self._get_schema(model)
         mfield = self._get_dataclass_field(dclazz, "x")
 
-        xmlbody._add_model_schema(dclazz, schema, {})
-        xmlbody._add_field_schema(dclazz, mfield, schema, {})
+        openapi._add_model_schema(dclazz, schema, {})
+        openapi._add_field_schema(dclazz, mfield, schema, {})
 
         self.assertAllNoneExcept(
             schema,
@@ -154,7 +155,7 @@ class WithoutMetaName:
         model = pydantic_dataclass(dclazz)
         schema = self._get_schema(model)
 
-        xmlbody._add_model_schema(dclazz, schema, {})
+        openapi._add_model_schema(dclazz, schema, {})
 
         self.assertAllNoneExcept(
             schema,
@@ -179,7 +180,7 @@ class Meta:
         model = pydantic_dataclass(dclazz)
         schema = self._get_schema(model)
 
-        xmlbody._add_model_schema(dclazz, schema, {})
+        openapi._add_model_schema(dclazz, schema, {})
 
         self.assertAllNoneExcept(
             schema,
@@ -206,13 +207,20 @@ class PlainRefWithoutName:
         schema = self._get_schema(model)
         mfield = self._get_dataclass_field(dclazz, "x")
 
-        xmlbody._add_field_schema(dclazz, mfield, schema, {})
+        openapi._add_field_schema(dclazz, mfield, schema, {})
 
         self.assertAllNoneExcept(
             schema,
             "type",
             "properties",
-            ignore={"title", "required", "description", "required", "properties"},
+            ignore={
+                "title",
+                "required",
+                "description",
+                "required",
+                "properties",
+                "defs",
+            },
         )
         assert schema.properties is not None
         prop = schema.properties["x"]
@@ -234,26 +242,22 @@ class PlainRefHavingName:
         schema = self._get_schema(model)
         mfield = self._get_dataclass_field(dclazz, "x")
 
-        xmlbody._add_field_schema(dclazz, mfield, schema, {})
+        openapi._add_field_schema(dclazz, mfield, schema, {})
 
         self.assertAllNoneExcept(
             schema,
             "type",
             "properties",
-            ignore={"title", "required", "description"},
+            ignore={"title", "required", "description", "defs"},
         )
         assert schema.properties is not None
         prop = schema.properties["x"]
+        print(prop.model_dump_json(indent=4))
         self.assertEqual(schema.type, "object")
-
-        self.assertAllNoneExcept(prop, "xml", "allOf", ignore={"title", "description"})
-        assert isinstance(prop.xml, XML)
-        assert isinstance(prop.allOf, list)
+        self.assertAllNoneExcept(prop, "xml", "ref", ignore={"title", "description"})
+        self.assertIsInstance(prop.xml, XML)
         self.assertAllNoneExcept(prop.xml, "name")
-
         self.assertEqual(prop.xml.name, mfield.metadata["name"])
-        self.assertLen(prop.allOf, 1)
-        self.assertAllNoneExcept(prop.allOf[0], "ref")
 
     def test_unnamed_obj_list(self) -> None:
         @dataclass
@@ -269,10 +273,13 @@ class PlainListRefWithoutName:
         schema = self._get_schema(model)
         mfield = self._get_dataclass_field(dclazz, "x")
 
-        xmlbody._add_field_schema(dclazz, mfield, schema, {})
+        openapi._add_field_schema(dclazz, mfield, schema, {})
 
         self.assertAllNoneExcept(
-            schema, "type", "properties", ignore={"title", "description", "required"}
+            schema,
+            "type",
+            "properties",
+            ignore={"title", "description", "required", "defs"},
         )
         self.assertEqual(schema.type, "object")
         assert isinstance(schema.properties, dict)
@@ -297,10 +304,13 @@ class PlainListRefWithoutName:
         schema = self._get_schema(model)
         mfield = self._get_dataclass_field(dclazz, "x")
 
-        xmlbody._add_field_schema(dclazz, mfield, schema, {})
+        openapi._add_field_schema(dclazz, mfield, schema, {})
 
         self.assertAllNoneExcept(
-            schema, "type", "properties", ignore={"title", "description", "required"}
+            schema,
+            "type",
+            "properties",
+            ignore={"title", "description", "required", "defs"},
         )
         assert isinstance(schema.properties, dict)
         prop = schema.properties["x"]
@@ -332,10 +342,13 @@ class PlainListRefWithoutName:
         schema = self._get_schema(model)
         mfield = self._get_dataclass_field(dclazz, "x")
 
-        xmlbody._add_field_schema(dclazz, mfield, schema, {})
+        openapi._add_field_schema(dclazz, mfield, schema, {})
 
         self.assertAllNoneExcept(
-            schema, "type", "properties", ignore={"title", "description", "required"}
+            schema,
+            "type",
+            "properties",
+            ignore={"title", "description", "required", "defs"},
         )
         self.assertEqual(schema.type, "object")
         assert schema.properties is not None
@@ -368,7 +381,7 @@ class WrappedTypeList:
         model = pydantic_dataclass(dclazz)
         schema = self._get_schema(model)
         mfield = self._get_dataclass_field(dclazz, "x")
-        xmlbody._add_field_schema(dclazz, mfield, schema, {})
+        openapi._add_field_schema(dclazz, mfield, schema, {})
         assert isinstance(schema.properties, dict)
         prop = schema.properties["x"]
 
@@ -404,7 +417,7 @@ class NonListWrapper:
         mfield = self._get_dataclass_field(dclazz, "x")
 
         self.assertRaises(
-            TypeError, xmlbody._add_field_schema, dclazz, mfield, schema, {}
+            TypeError, openapi._add_field_schema, dclazz, mfield, schema, {}
         )
 
     def test_items_no_schema(self) -> None:
@@ -428,5 +441,211 @@ class WrappedTypeList:
         assert isinstance(prop.items, Schema)
         prop.items = None
         self.assertRaises(
-            TypeError, xmlbody._add_field_schema, dclazz, mfield, schema, {}
+            TypeError, openapi._add_field_schema, dclazz, mfield, schema, {}
         )
+
+    def test_get_element_name_generator(self) -> None:
+        """
+        The test_get_element_name_generator function tests the
+        :func:`fastapi_xml.xmlbody._get_element_name_generator` function.
+        The test checks that if a class has an element name generator, it is returned by the function.
+        If not, then the default element name generator is returned.
+        """
+
+        class Meta1:
+            @staticmethod
+            def element_name_generator(x: str) -> str:
+                """dummy name generator."""
+                return x.upper()
+
+        class Meta2:
+            pass
+
+        g = openapi._get_element_name_generator(Meta1)
+        self.assertEqual(g("test"), "TEST")
+        g = openapi._get_element_name_generator(Meta2)
+        self.assertEqual(g, openapi.DEFAULT_XML_CONTEXT.element_name_generator)
+
+    def test_get_attribute_name_generator(self) -> None:
+        """
+        The test_get_attribute_name_generator function tests the
+        :func:`fastapi_xml.xmlbody._get_attribute_name_generator` function.
+        The test checks that if a class has an attribute name generator, it is returned by the function.
+        If not, then the default attribute name generator is returned.
+        """
+
+        class Meta1:
+            @staticmethod
+            def attribute_name_generator(x: str):
+                """dummy name generator."""
+                return x.upper()
+
+        class Meta2:
+            pass
+
+        g = openapi._get_attribute_name_generator(Meta1)
+        self.assertEqual(g("test"), "TEST")
+        g = openapi._get_attribute_name_generator(Meta2)
+        self.assertEqual(g, openapi.DEFAULT_XML_CONTEXT.attribute_name_generator)
+
+    def test_add_model_schema(self) -> None:
+        """
+        The test_add_model_schema function tests the
+        :func:`fastapi_xml.xmlbody._add_model_schema` function.
+
+        The test checks that the schema is an instance of Schema, and
+        that it has a name, prefix, namespace and attribute.
+        """
+
+        @dataclass
+        class Dummy:
+            class Meta:
+                name = "Foo"
+                namespace = "http://testns"
+
+            x: str
+
+        test_schema = Schema()
+        test_ns_map = {"http://testns": "bla"}
+        openapi._add_model_schema(Dummy, test_schema, test_ns_map)
+        self.assertIsInstance(test_schema.xml, XML)
+        self.assertEqual(test_schema.xml.name, Dummy.Meta.name)
+        self.assertEqual(test_schema.xml.prefix, "bla")
+        self.assertEqual(test_schema.xml.namespace, "http://testns")
+        self.assertIsNone(test_schema.xml.attribute)
+        self.assertIsNone(test_schema.xml.wrapped)
+
+    def test_is_xml_schema_empty(self) -> None:
+        """The test_is_xml_schema_empty function tests the
+        :func:`fastapi_xml.xmlbody._is_xml_schema_empty` function This test
+        check an empty XML schema and each attribute."""
+        self.assertTrue(openapi._is_xml_schema_empty(XML()))
+        self.assertFalse(openapi._is_xml_schema_empty(XML(name="")))
+        self.assertFalse(openapi._is_xml_schema_empty(XML(prefix="")))
+        self.assertFalse(openapi._is_xml_schema_empty(XML(attribute=False)))
+        self.assertFalse(openapi._is_xml_schema_empty(XML(wrapped=False)))
+
+    def test_switch_ref_to_all_of__empty_xml(self) -> None:
+        """
+        The test_switch_ref_to_all_of__empty_xml function tests the
+        :func:`fastapi_xml.xmlbody._switch_ref_to_all_of` function.
+        This test assures that the function does not affect the xml schema object.
+        """
+        test_prop = Schema()
+        test_xml = XML()
+        self.assertIsNone(test_prop.xml)
+        openapi._switch_ref_to_all_of(test_prop, test_xml)
+        self.assertIsNone(test_prop.xml)
+        self.assertIsNone(test_prop.allOf)
+        self.assertIsNone(test_prop.ref)
+
+    def test_switch_ref_to_all_of__non_empty_xml(self) -> None:
+        """
+        The test_switch_ref_to_all_of__non_empty_xml function tests the
+        :func:`fastapi_xml.xmlbody.switch_ref_to_all_of` function.
+        with a non-empty XML object as input. The test asserts that the XML object is not None, and that it has been
+        assigned to the property's xml attribute.
+        """
+        test_prop = Schema()
+        test_xml = XML(name="x")
+        self.assertIsNone(test_prop.xml)
+        openapi._switch_ref_to_all_of(test_prop, test_xml)
+        self.assertIsNotNone(test_prop.xml)
+        self.assertEqual(id(test_prop.xml), id(test_xml))
+        self.assertIsNone(test_prop.allOf)
+        self.assertIsNone(test_prop.ref)
+
+    def test_switch_ref_to_all_of__existing_ref(self) -> None:
+        """
+        The test_switch_ref_to_all_of__existing_ref function tests the
+        :func:`fastapi_xml.xmlbody._switch_ref_to_all_of` function.
+        This test assures that the function places the $ref within the allOf property.
+        """
+        kwargs = {"$ref": "test_ref"}
+        test_prop = Schema(**kwargs)
+        test_xml = XML(name="x")
+        self.assertIsNone(test_prop.xml)
+        self.assertIsNotNone(test_prop.ref)
+        openapi._switch_ref_to_all_of(test_prop, test_xml)
+        self.assertIsNotNone(test_prop.xml)
+        self.assertEqual(id(test_prop.xml), id(test_xml))
+        self.assertIsInstance(test_prop.allOf, list)
+        self.assertEqual(len(test_prop.allOf), 1)
+        self.assertIsInstance(test_prop.allOf[0], Schema)
+        self.assertEqual(test_prop.allOf[0].ref, "test_ref")
+        self.assertIsNone(test_prop.ref)
+
+    def test_add_field_schema(self) -> None:
+        """The test_add_field_schema function tests the
+        :func:`fastapi_xml.xmlbody._add_field_schema function` The test is
+        incomplete, but it does check that a field can be added to an empty
+        schema."""
+        test_schema = Schema()
+        openapi._add_field_schema(object, field(), test_schema, {})
+        self.assertEqual(len(test_schema.model_dump(exclude_none=True)), 0)
+
+    def test_get_route_models(self) -> None:
+        """
+        The test_get_route_models function tests the
+        :func:`fastapi_xml.xmlbody._get_route_models` function.
+
+        It validates that the function successfully returns the correct
+        response Model.
+        """
+
+        @dataclass
+        class TestModel:
+            x: str
+
+        router = APIRouter()
+
+        @router.get("/", response_model=TestModel)
+        def dummy_endpoint() -> None:  # pragma: no cover
+            """a dummy endpoint."""
+            pass
+
+        app = FastAPI()
+        app.include_router(router)
+        schema = OpenAPI(**app.openapi())
+        models = openapi._get_route_models(app, schema)
+        self.assertEqual(len(models), 1)
+        # fastapi converts the model into a pydantic dataclass. Hence, it is a different model
+        # having the same attributes
+        self.assertEqual(models[0].__name__, TestModel.__name__)
+
+    def test_add_openapi_xml_schema(self) -> None:
+        """
+        The test_add_openapi_xml_schema function tests the
+        :func:`fastapi_xml.xmlbody.add_openapi_xml_schema` function.
+
+        It does so by creating a FastAPI app and adding an endpoint to
+        it, then testing if the schema has been modified. The test also
+        checks if the function returns None when components or its
+        schemas are missing.
+        """
+
+        @pydantic.dataclasses.dataclass
+        class TestModel:
+            x: str
+
+        router = APIRouter()
+
+        @router.get("/", response_model=TestModel)
+        def dummy_endpoint() -> None:  # pragma: no cover
+            """a dummy endpoint."""
+            pass
+
+        test_app = FastAPI()
+        test_app.include_router(router)
+
+        # test if the schema has been modified
+        test_app.openapi_schema = None
+        test_openapi = OpenAPI(**test_app.openapi())
+        self.assertTrue(openapi.add_openapi_xml_schema(test_app, test_openapi))
+
+        # tests if the function returns None if components or its schemas are missing
+        test_openapi = OpenAPI(**test_app.openapi())
+        test_openapi.components = None
+        self.assertFalse(openapi.add_openapi_xml_schema(test_app, test_openapi))
+        test_openapi.components = Components()
+        self.assertFalse(openapi.add_openapi_xml_schema(test_app, test_openapi))
diff --git a/tests/test_response.py b/tests/test_response.py
new file mode 100644
index 0000000..85e372a
--- /dev/null
+++ b/tests/test_response.py
@@ -0,0 +1,88 @@
+#  type: ignore
+import unittest
+from dataclasses import dataclass
+from dataclasses import field
+
+from fastapi import APIRouter
+from fastapi import FastAPI
+from fastapi.routing import APIRoute
+from pydantic import BaseModel
+
+from fastapi_xml.decoder import XmlParser
+from fastapi_xml.response import XmlResponse
+from fastapi_xml.response import XmlSerializer
+from fastapi_xml.xmlbody import XmlBody
+
+
+class TestXmlResponse(unittest.TestCase):
+    def setUp(self) -> None:
+        """
+        The setUp function is called before each test function.
+
+        It creates a FastAPI app with two endpoints, one using a
+        dataclass and the other not. The setUp function also stores the
+        API routes in self.api_routes.
+        """
+
+        class NotADataclass(BaseModel):
+            x: str
+
+        @dataclass
+        class Model:
+            x: str = field(metadata={"type": "Element"})
+
+        router = APIRouter()
+
+        @router.get("/model")
+        def endpoint_model(x: Model = XmlBody()) -> None:  # pragma: no cover
+            """a dummy endpoint."""
+            x.x = x.x
+
+        @router.get("/dclazz")
+        def endpoint_dclazz(x: NotADataclass = XmlBody()) -> None:  # pragma: no cover
+            """a dummy endpoint."""
+            x.x = x.x
+
+        self.app = FastAPI()
+        self.app.include_router(router)
+        self.api_routes = [r for r in self.app.routes if isinstance(r, APIRoute)]
+
+    def test_get_serializer(self) -> None:
+        """The test_get_serializer function tests the
+        :func:`fastapi_xml.xmlbody.XmlResponse.get_serializer` function The
+        test checks if a serializer is returned and if it is an instance of
+        XmlSerializer."""
+        # a previous serializer might be available. Hence, backup and reset it
+        current_serializer = XmlResponse.serializer
+        XmlResponse.serializer = None
+
+        # actual test
+        serializer = XmlResponse.get_serializer()
+        self.assertIsInstance(serializer, XmlSerializer)
+
+        # reset to previous serializer
+        XmlResponse.serializer = current_serializer
+
+    def test_render(self) -> None:
+        """
+        The test_render function tests the
+        :meth:`fastapi_xml.xmlbody.XmlResponse.render` method.
+
+        It creates a dummy dataclass, instantiates it with a value for
+        its only field, and validates that the render method odes return
+        a byte string for bytestring. Next, the test validates that the
+        serialized string deserializes the same datastructure.
+        """
+
+        @dataclass
+        class Dummy:
+            x: str = field(metadata={"type": "Element"})
+
+        test_obj = Dummy(x="test")
+        test_response = XmlResponse(content=test_obj)
+        test_body = test_response.render(test_obj)
+        self.assertIsInstance(test_body, bytes)
+
+        parsed_obj = XmlParser().from_bytes(test_body, clazz=Dummy)
+        self.assertIsInstance(parsed_obj, Dummy)
+        self.assertEqual(parsed_obj.x, test_obj.x)
diff --git a/tests/test_fastapi.py b/tests/test_route.py
similarity index 97%
rename from tests/test_fastapi.py
rename to tests/test_route.py
index 8527464..0ebf38c 100644
--- a/tests/test_fastapi.py
+++ b/tests/test_route.py
@@ -1,3 +1,4 @@
+#  type: ignore
 import asyncio
 from dataclasses import dataclass
 from dataclasses import field
@@ -10,9 +11,9 @@
 from xsdata.formats.dataclass.parsers import XmlParser
 from xsdata.formats.dataclass.serializers import XmlSerializer
 
-from fastapi_xml import NonJsonRoute
 from fastapi_xml import XmlAppResponse
 from fastapi_xml import XmlBody
+from fastapi_xml.route import XmlRoute
 
 
 @dataclass
@@ -32,7 +33,7 @@ def setUp(self) -> None:
 
         self.app = FastAPI()
         router = self.app.router
-        router.route_class = NonJsonRoute
+        router.route_class = XmlRoute
         router.default_response_class = XmlAppResponse
 
     def test_same_model_io(self) -> None:
diff --git a/tests/test_xmlbody.py b/tests/test_xmlbody.py
new file mode 100644
index 0000000..9a0c337
--- /dev/null
+++ b/tests/test_xmlbody.py
@@ -0,0 +1,9 @@
+#  type: ignore
+import unittest
+
+from fastapi_xml import XmlBody
+
+
+class TestXmlBody(unittest.TestCase):
+    def test_run_xmlbody(self):
+        XmlBody()