Skip to content

[Python] serialize not respect type in model's openapi_types #11675

New issue

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

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

Already on GitHub? Sign in to your account

Closed
YingChen1996 opened this issue Feb 21, 2022 · 4 comments
Closed

[Python] serialize not respect type in model's openapi_types #11675

YingChen1996 opened this issue Feb 21, 2022 · 4 comments

Comments

@YingChen1996
Copy link

Is your feature request related to a problem? Please describe.

I am generating client python code by swagger.json using openapi-generator-cli. To do that I used the

  • npm install @openapitools/openapi-generator-cli -g
  • openapi-generator-cli version-manager set 5.2.0
  • run just like: openapi-generator-cli generate -i ./swagger.json -g python-legacy -o ./_restclients --additional-properties packageName=designer

then it will gennerate sanitize_for_serialization function in api_client.py, which is like:

        """Builds a JSON POST object.

        If obj is None, return None.
        If obj is str, int, long, float, bool, return directly.
        If obj is datetime.datetime, datetime.date
            convert to string in iso8601 format.
        If obj is list, sanitize each element in the list.
        If obj is dict, return the dict.
        If obj is OpenAPI model, return the properties dict.

        :param obj: The data to serialize.
        :return: The serialized form of data.
        """
        if obj is None:
            return None
        elif isinstance(obj, self.PRIMITIVE_TYPES):
            return obj
        elif isinstance(obj, list):
            return [self.sanitize_for_serialization(sub_obj)
                    for sub_obj in obj]
        elif isinstance(obj, tuple):
            return tuple(self.sanitize_for_serialization(sub_obj)
                         for sub_obj in obj)
        elif isinstance(obj, (datetime.datetime, datetime.date)):
            return obj.isoformat()

        if isinstance(obj, dict):
            obj_dict = obj
        else:
            # Convert model obj to dict except
            # attributes `openapi_types`, `attribute_map`
            # and attributes which value is not None.
            # Convert attribute name to json key in
            # model definition for request.
            if hasattr(obj, 'openapi_types'):
                obj_dict = {obj.attribute_map[attr]: getattr(obj, attr)
                            for attr, _ in six.iteritems(obj.openapi_types)
                            if getattr(obj, attr) is not None}
            else:
                return str(obj)
        return {key: self.sanitize_for_serialization(val)
                for key, val in six.iteritems(obj_dict)}

This function will serialize a obj according to its actual type ,such as serialize a list obj to a list, but does not respect type in model's openapi_types. For example, we pass a list obj, but we want to serialize it to a string. We found that this function dose not use type in obj.openapi_types, is this expected?

Describe the solution you'd like

We want this function can serialize obj according to type in obj.openapi_types.

@spacether
Copy link
Contributor

Have you tried using python-experimental? That respects schema types when serializing.

@0x7162
Copy link
Contributor

0x7162 commented May 7, 2022

Any updates on the issue?

@0x7162
Copy link
Contributor

0x7162 commented May 7, 2022

This regression happens after #7965

If you looks at the method before the change happened,
https://github.com/OpenAPITools/openapi-generator/blob/b3bc926b1d48840903a1293b0c74c49e9391966c/modules/openapi-generator/src/main/resources/python/api_client.mustache

you can see:

    def sanitize_for_serialization(self, obj):
...
        else:
            # Convert model obj to dict except
            # attributes `openapi_types`, `attribute_map`
            # and attributes which value is not None.
            # Convert attribute name to json key in
            # model definition for request.
            obj_dict = {obj.attribute_map[attr]: getattr(obj, attr)
                        for attr, _ in six.iteritems(obj.openapi_types)
                        if getattr(obj, attr) is not None}

        return {key: self.sanitize_for_serialization(val)
                for key, val in six.iteritems(obj_dict)}

Currently it is:

    def sanitize_for_serialization(self, obj):
...
        elif isinstance(obj, (list, tuple)):
            return [cls.sanitize_for_serialization(item) for item in obj]
        if isinstance(obj, dict):
            return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()}
        raise ApiValueError(
            'Unable to prepare type {} for serialization'.format(
                obj.__class__.__name__))

I assume it is because of optimizations are done and models are not imported by default (during init).

@spacether
Copy link
Contributor

This is fixed in v6.2.0 and onward
In python in v6.2.0 and onward, serialization starts with schema instances which preserve openapi types
One can see that working in RequestBody.serialize
and this is also done in the parameter serializers like
HeaderParameter.serialize

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants