Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[BUG][php] json_decode missing when response is object #8394

Closed
5 of 6 tasks
ybelenko opened this issue Jan 9, 2021 · 7 comments · Fixed by #8481
Closed
5 of 6 tasks

[BUG][php] json_decode missing when response is object #8394

ybelenko opened this issue Jan 9, 2021 · 7 comments · Fixed by #8481

Comments

@ybelenko
Copy link
Contributor

ybelenko commented Jan 9, 2021

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

When I call API via generated PHP client I got encoded json string instead of decoded object. Response looks like:

["{\"foobar\":\"foobaz\"}"]
openapi-generator version

5.0.0-beta

OpenAPI declaration file content or url
paths: 
  '/objects/{id}':
    parameters:
      - name: id
        in: path
        required: true
    get:
      operationId: getSingleObjectById
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FreeFormObject'
components:
  schemas:
    FreeFormObject:
      type: object
Generation Details
generate -i \"spec.yaml\" -g php -o \"api-client-sdk\"
Steps to reproduce

Send request via generated SDK to staged server /objects/1 where response is correct JSON item. The response will be json encoded string instead of object.

Related issues/PRs

#6566 Seems close but not really. I'm ok with response as object instead of model. It's just json_decode execution missed.

Suggest a fix
diff --git a/modules/openapi-generator/src/main/resources/php/ObjectSerializer.mustache b/modules/openapi-generator/src/main/resources/php/ObjectSerializer.mustache
index 58200ac713..42fd2658d1 100644
--- a/modules/openapi-generator/src/main/resources/php/ObjectSerializer.mustache
+++ b/modules/openapi-generator/src/main/resources/php/ObjectSerializer.mustache
@@ -294,6 +294,7 @@ class ObjectSerializer
         }
 
         if ($class === 'object') {
+            $data = is_string($data) ? json_decode($data) : $data;
             settype($data, 'array');
             return $data;
         }

I don't know why json_decode executes based on string check and doesn't check Content-Type header but it's a quick fix.

@wing328
Copy link
Member

wing328 commented Feb 21, 2021

["{"foobar":"foobaz"}"]

Is this more like a server issue? In other words, the server seems to returning an invalid response. Should the correct response look like the following instead?

[{"foobar":"foobaz"}]

@ybelenko
Copy link
Contributor Author

Nope, original response from server(I expect single object):

"{\"foobar\":\"foobaz\"}"

then client generator cast it to array with the line:

settype($data, 'array');

That's why raw request body then looks like:

["{\"foobar\":\"foobaz\"}"]

then cause JSON parsing exception.

@wing328
Copy link
Member

wing328 commented Feb 21, 2021

components:
  schemas:
    FreeFormObject:
      type: object

Is that a correct description of the response?

I'm confused on the actual response. In #8394 (comment), you said the following is the response

["{\"foobar\":\"foobaz\"}"]

but in #8394 (comment), the "original" response from the server is

"{\"foobar\":\"foobaz\"}"

Which one is the response from the server?

@ybelenko
Copy link
Contributor Author

Is that a correct description of the response?

Yes, schema is correct I expect single object.

I use client php generator to call server and pass response to php output(like an API wrapper, proxy). So php client serializes server response with ObjectSerializer.

That one is response from original server:

"{\"foobar\":\"foobaz\"}"

Then proxy client SDK returns me array with single string(["{\"foobar\":\"foobaz\"}"]) instead of work with decoded object. I cannot retrieve in php $response['foobar'] because $response is array with single string item.

@wing328
Copy link
Member

wing328 commented Feb 22, 2021

"{\"foobar\":\"foobaz\"}"

Hmm... why would type:object be a correct description of the payload?

And why would the server further encodes JSON payload as a string before sending it to the client?

Shouldn't the payload returned by the server be as simple as the following?

{"foobar":"foobaz"}

What's the content-type of the response? applicatoin/json?

@ybelenko
Copy link
Contributor Author

"{\"foobar\":\"foobaz\"}"

Hmm... why would type:object be a correct description of the payload?

I need to retrieve single object with foobar property not an array with one object.

And why would the server further encodes JSON payload as a string before sending it to the client?

Let's forget about second encoding. The main issue is that I cannot read foobar property in PHP because my result of client object serialization is array with one string item instead of hash map.
Thats in generated SDK:

        if ($class === 'object') {
            // here comes by some unknown reason not serialized data, string like {"foobar":"foobaz"}
            // instead of decode it serializer tries to convert it to array and, so output is ['{"foobar":"foobaz"}']
            // I cannot retrieve foobar field from that array like I normally do with array or model
             settype($data, 'array');
             return $data;
         }

Shouldn't the payload returned by the server be as simple as the following?

{"foobar":"foobaz"}

It can be, but you can face issues with chars which should be escaped. That's why php json_encode escapes string with backward slashed by default. It option can be disabled, but I never even thought about it. Ref: php json_encode

What's the content-type of the response? applicatoin/json?

Correct

Check the whole serialization function, it decodes json when data is string, but we expect not scalar value(L267, L282):

if (strcasecmp(substr($class, -2), '[]') === 0) {
$data = is_string($data) ? json_decode($data) : $data;
if (!is_array($data)) {
throw new \InvalidArgumentException("Invalid array '$class'");
}
$subClass = substr($class, 0, -2);
$values = [];
foreach ($data as $key => $value) {
$values[] = self::deserialize($value, $subClass, null);
}
return $values;
}
if (preg_match('/^(array<|map\[)/', $class)) { // for associative array e.g. array<string,int>
$data = is_string($data) ? json_decode($data) : $data;
settype($data, 'array');
$inner = substr($class, 4, -1);
$deserialized = [];
if (strrpos($inner, ",") !== false) {
$subClass_array = explode(',', $inner, 2);
$subClass = $subClass_array[1];
foreach ($data as $key => $value) {
$deserialized[$key] = self::deserialize($value, $subClass, null);
}
}
return $deserialized;
}
if ($class === 'object') {
settype($data, 'array');
return $data;
}

@wing328
Copy link
Member

wing328 commented Feb 22, 2021

Can you please PM me in Slack when you've time? Thanks.

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

Successfully merging a pull request may close this issue.

2 participants