Open joshribakoff opened 9 years ago
I somehow seem to remember that we had this topic before .. at least 1-2 years past. note you can override the service used for decoding of json with a custom service.
We could maybe provide an option for the JsonDecoder
to preserve objects ?
The outermost object would still need to be encoded as associative array.
or we move to https://github.com/webmozart/json in case it already provides the options we need
/cc @webmozart
If you set $assoc
to true
, that's what json_decode()
does. You need to either set $assoc
to false
or convert empty arrays to objects manually.
The problem is that json_decode()
is not schema-aware, but neither is webmozart/json. You could maybe create a feature request on the PHP bug tracker?
To be clear, this is not a PHP bug. PHP can preserve my data just fine.
$str = "{foo:[], bar:{}}";
var_dump(json_encode(json_decode($str)) === $str); // true
json_decode() works correctly by default, FOSRestBundle
is explicitly telling json_decode()
to 'destroy' the data's schema. json_decode
does not "change" variable types, unless you specifically ask for it to.
Unfortunately I think the problem is more complex than simply modifying the JsonDecoder service for FOSRestBundle
.
After fixing FOSRest, we still have other bundles that are casting objects to arrays in various points of the Symfony lifecycle:
Doctrine
has concept of entity having a property that is a "json array" - casts data to array when loading entities, causes problems if you pass in an object...SyliusResourceBundle
(which depends on FOSRest) it passes the request to the Symfony form which also crashes if it gets an array not an object.JMSSerializerBundle
also is configured by default to cast objects to arrays when creating the response...Hopefully you can see what I'm saying. We'd have to change the whole ecosystem, not just the FOSRestBundle.
Casting objects to arrays it would seem is a PHP "standard" or convention. What I ended up doing was casting the property back to an object in Angular side. Knowing that the PHP world is out to destroy my data (being facetious), the path of least resistance was to force an Angular coding style disallowing empty objects on the scope in the first place. I just wanted to open the issue for public discussion to see if I was overlooking anything, though.
I'm thinking out loud about a solution --
Authors of PHP libraries want to write $foo['bar']
and not write $foo->bar
, so they just cast all incoming json to an associative array. JS has no associative arrays [1], so PHP just casts non numerically indexed arrays to objects on the response.
[1] - (Writing foo['bar']
just creates an object in JS, as if you'd written foo.bar
)
This works -- JS sends an object, PHP converts to an array, processes it, converts it back to an object, JS gets an object back... Except it does not work, in the case of empty objects....
As an Angular developer, I want my backend to properly persist my JSON verbatim. Even for edge cases like empty objects. So what if my backend substituted empty objects with a "value array"? Confused?
Basically you need a request listener that:
{}
to a request param w/ the (array) value ['_blank_object'=>true]Any libraries like SyliusResourceBundle can just do $request->get('foo')
and get back the array ['_blank_object'=>true]
(instead of getting an object & failing a type check, etc.)
Then you'd have a response listener for when FOSRest is serializing a json response:
In theory, all the naughty PHP libraries can hook into the symfony lifecycle & cast my data to an array all they want without an issue now. They can remain ignorant as to the array's secret meaning. It is like passing a "trojan horse" array through symfony, by disguising my object as an array... since PHP devs like arrays, apparently. lol.
Could something like this be done through the existing hooks that FOS Rest provides? By writing custom encoders & decoder services that implement my idea? Would it be "hacky"?
maybe it would be safer to just add a flag on the request, like $request->attributes->set('_blank_object', true); which the view handler can then be made aware of
Is this issue still open? I got the same issue in my code getting an empty object {} from React client that is being transformed to []. I guess I got no choice, but write a listener.
OK, i've just found this thread and i think the response is a simple one. The documentation outlines how json_encode cna be used to force empty arrays to be converted to objects (docs here).
It's as simple as:
json_encode($b, JSON_FORCE_OBJECT)
@cia05rf That would typecast even the arrays sent in the json payload to object. We would ideally want to preserve all types sent in the payload.
If you POST some json like this:
In my controller
$request->get('foo')
returns[]
instead ofstdClass {}
. We're using Angular to POST to a controller that uses FOSRest.This completely crashes our app because Angular cannot $watch an empty array.
I tried to debug & I found this:
From the PHP docs:
This was kind of insidious for us to debug. I came up with this override which preserves my objects:
However, certain symfony components & commonly used 3rd party bundles still crash after this modification.
Currently, the only acceptable workaround we've found is to overwrite the corrupt json with valid json ourselves:
I guess the one thing I could suggest you do to fix this, without breaking BC, is you could add a configuration option that puts the correct JSON in
$request->get('_json')
or something like that..