Pass through JSON data with JSON view

I’m receiving fully formed JSON response from Postgres and attempting to pass it to a jQuery AJAX request. Cake wants to treat my DB data as raw data, though, and JSON encode it again, which is breaking things.

The data I get from the DB is pretty simple. It looks like this (two rows in this example).

{
"data":[
            {"id":111870,"site_id":3076021583,"points":7,"area":37236.0725966764},
            {"id":111802,"site_id":3076021583,"points":7,"area":37236.0725966764}
           ]
}

After Cake gets done with it, though, it looks like this.

{
"content": [
    "{\u0022data\u0022:[{\u0022id\u0022:111870,\u0022site_id\u0022:3076021583,\u0022points\u0022:7,\u0022area\u0022:37236.0725966764},{\u0022id\u0022:111802,\u0022site_id\u0022:3076021583,\u0022points\u0022:7,\u0022area\u0022:37236.0725966764}]}"
]

}

I need to turn that off, and just allow the response to go through normally, with the correct Accept and Content-Type headers in place.

Any help or clues for the clueless?

Did you try setting hydration off? But it should return as an array instead of an entity (and maybe you should pass it on json_decode)

In the book ORM: Getting the first result (a little upper but there is no link)

I’m not using the query builder. I’m working with a GIS system and running fairly complex spatial queries that Cake isn’t really suited to handle, so I’m executing queries directly from the statement handle. Because the DB is handling the JSON creation, the result set is a single row.

I merely want to pass that row back to the client with the correct content-type. But if I set the response type to ‘json’, the ORM wants to process it and actually create the JSON for me. That’s what I want to shut off.

Right now I’m having to return the data as text/plain, and have the JSON.parse function on the client create the actual object.

Try json_encode() with options JSON_UNESCAPED_UNICODE.

That’s the thing, though. I’m not using Cake (or even PHP) at all to encode anything. The DB already does that for me.

Here is a simple example of what I mean. This is my query.

        select array_to_json(array_agg(f))
        from
                (SELECT id
                , site_id
                , ST_Npoints(polygon) as points
                , ST_Area(polygon::geography) * 0.000001  as area
                from convex_hulls
                where hull_type = 2
                ) as f
        ");

That returns a single row, an array of records in JSON format.

It looks like this

"{"data":[{"id":111917,"site_id":3076021581,"points":4,"area":6163.60421897346},{"id":111759,"site_id":3076021581,"points":4,"area":6163.60421897346}]}"

That is already JSON encoded. I don’t need Cake or PHP to do anything with it other than return it as the body of the response…

Connection:Keep-Alive
Content-Length:141
Content-Type:application/json; charset=UTF-8
Date:Sat, 27 Aug 2016 16:35:06 GMT
Keep-Alive:timeout=5, max=100
Server:Apache/2.4.6 (Red Hat Enterprise Linux) PHP/5.6.23
X-DEBUGKIT-ID:96902bf2-6336-42e3-ab3c-ad9f42d5b72d
X-Powered-By:PHP/5.6.23

{"data":[{"id":111917,"site_id":3076021581,"points":4,"area":6163.60421897346},{"id":111759,"site_id":3076021581,"points":4,"area":6163.60421897346}]}

But any time I set the content-type appropriately to JSON, Cake wants to json_encode it for me. So I’m setting the content type to text/plain, which gives me the server side behavior that I want (unmolested data) but forces me to treat it differently on the client side (having to parse it with JSON.parse()).

Does that clarify my problem a bit?

Thanks for the help!

Do you use Router::parseExtensions(‘json’);
and url like mysite/controller/func.json? - by these two things Cake knows it’s AJAX request.

What do you see in browser if you type your json’s url?
$this->autoRender = false; - ?

Just for test , Could you try to fetch data by Cake style and encode it .
$data = $this->Controller->find(‘all’,$conditions);

return json_encode($data).

I use this contraction and it works, if I send request to server as plain text I get plain text answer, if encoded - server return encoded.

I don’t use the Routers, and the request is REST based so it’s /site/controller/site/id. I turn autorendering off (I think)

$this->viewBuilder()->autoLayout(false);

If I call the action directly in the browser, the content returned is based on whatever the browser sends as it’s Accept: header. If it says it will accept application/json, then Cake provides that and tries to encode my data. If I ask for text/plain, it doesn’t. That is expected behavior.

I’m not trying to figure out why Cake is doing what it’s doing. I know that. I’m trying to figure out how to turn it off for certain requests. I want to return application/json as a content type and simply have my action return exactly what the DB returns as the response payload, without any attempt by Cake to encode it. It’s already encoded JSON when Cake receives it from the DB.

you can probably try to give the the output directly i.e.

$this->response->body($json);
return $this->response;

in your action but its kind of hacky and also im not 100% sure about this ‘solution’ :slight_smile:

I finally figured out the answer to this.

I load the ResponseHandler component in my controller parent (AppController). Under normal circumstances, this handler would look at the request, determine what response format the client is asking for, and format the entities acordingly. If I’m just pulling entities from the DB, this works splendid. But since I’m pulling JSON directly out of the DB, response handler wants to turn it into JSON, too. So I end up with my DB’s JSON record encoded in Cake’s JSON response.

Answer, don’t load ResponseHandler, and set your response codes by hand.

In my initialize, I add a geo+json header type.

$this->response->type(['geojson' => 'application/geo+json']);

And in my action, I simply set the response to geojson.

$this->response->type('geojson');

Since I don’t have RequestHandler loaded, Cake doesn’t try to help me format my data.