How to avoid serializer to escape double quote to \u0022? - php

In my controller I want to return a JsonResponse:
public function index(CkeditorTemplateRepository $ckeditorTemplateRepository, SerializerInterface $serializer): Response
{
$data = $ckeditorTemplateRepository->findAll();
return new JsonResponse($serializer->serialize($data, 'json'));
}
But when I request this endpoint, the response includes too many \u prefixes:
"[{\u0022created\u0022:\u00222019-08-31T07:28:56+00:00\u0022,\u0022id\u0022:1,\u0022content\u0022:\u0022\u003Ctr\u003E\u003Ctd height=\\u002252\\u0022 valign=\\u0022bottom\\u0022 class=\\u0022h24\\u0022\u003E\u003Cspan class=\\u0022h21copy1\\u0022\u003E\u5b66\u4e60\u56de\u526f\u603b\u7406\u7684\u8bb2\u8bdd \u003C\/span\u003E\u003C\/td\u003E\u003C\/tr\u003E\u003Ctr\u003E\u003Ctd valign=\\u0022top\\u0022\u003E\u003Cp style=\\u0022margin-bottom:10\\u0022 align=\\u0022center\\u0022 class=\\u0022font6\\u0022\u003E\u003C\/p\u003E\u003C\/td\u003E\u003C\/tr\u003E\u0022}]"

The problem is that you are doing it wrong.
JsonResponse is a response object that helps you with serializing the response data to JSON.
But you are already doing the seralization yourself, so it is a bit redundant.
Couple of options:
return (new Response($serializer->serialize($data, 'json'))
->headers->set('Content-type', 'application/json');
Or if you keep using JsonResponse:
return (new JsonResponse())->setContent($serializer->serialize($data, 'json'));
Or instantiate JsonResponse directly from a factory method:
return JsonResponse::fromJsonString($serializer->serialize($data, 'json'));
(in either case, no need to set the content type, since JsonResponse does that for you.)
If you do:
new JsonResponse($data);
what you get is a response where the content is the JSON serialiazed $data. This works for simple data structures which you can easily serialize by calling json_encode. But you were already sending a JSON string, so by doing it this way you were serializing the data twice.

return new JsonReponse($data) deals with arrays, strings etc. It will json_encode
that data for output. With a pre-serialised string, you can use return JsonResponse::fromJsonString('{"key": "value"}'). Both accept a $status and an array of headers.
JsonResponse::fromJsonString() is, in fact, just a shortcut for calling the constructor with its final parameter with true.
__construct($data = null, int $status = 200, array $headers = [], bool $json = false)

Related

How can i set multiple Header Keys with Slim Framework 3

I am using the below sample code to return data to the browser
$response->write($image);
return $response->withHeader('Content-Type', 'image/jpeg');
which works fine but how would i go about it if i wanted to also return Content-Length ?
the only option i found so far is to copy the response into a new object like below but i don't think that's efficient if i have a $image in the response.
$response = $response->withAddedHeader('Content-Length', strlen($image));
I tried it as array but that doesn't work..
Quoting from Slim 3 docs
Reminder
Unlike the withHeader() method, this method appends the new value to the set of values that already exist for the same header name. The Response object is immutable. This method returns a copy of the Response object that has the appended header value.
Both withHeader() and with appendedHeader() methods return a copy of the response object. So even if your do not assign the return value of $response->withHeader() to a variable and return the result directly, you are still working with a copy of the response object.
Regarding your concern about efficiency, you should use streams instead of string as response body. The following is an example of how to use streams for returning an image as the response:
<?php
use Slim\App;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Http\Body;
return function (App $app) {
$container = $app->getContainer();
$app->get('/', function(Request $request, Response $response) {
$image = fopen('sample.jpg', 'r');
return $response->withBody(new Body($image))
->withHeader('Content-Type', 'image/jpeg')
->withAddedHeader('Content-Length', fstat($image)['size']);
});
};

How to send array of objects within request body in JSON format via Postman?

I want to test my API function by sending array of objects via postman
When I send my request in JSON format to the function and then looping through the array to access each object property it gives me the following error:
https://i.imgur.com/QV9MDsm.jpg
Here is my request:
https://i.imgur.com/4584wf3.jpg
I searched how to send an array of objects using postman and found that I am doing it the right way
I selected row under body section so I can add my request body and selected it's format as JSON and added "Content-Type: application/json" to the request header
My API function:
public function createRetailer(Request $request){
$machines = $request->machineInfo;
foreach($machines as $machine){
$newMachine = new Machine;
$newMachine->machine_no = $machine->machineNo;
$newMachine->account_type = $machine->accountType;
$newMachine->machine_type = $machine->machineType;
$newMachine->retialer_id = $retailer->retailerId;
$newMachine->save();
}
}
I expect that i can access each array object properties as a PHP object but I found that it is not an object by testing it using is_object() function:
public function createRetailer(Request $request){
$machines = $request->machineInfo;
foreach($machines as $machine){
return response()->json(is_object($machine));
}
}
I do not know if the problem is within my request or something that I might misunderstand while retrieving data of the request in my controller function
Either it is an array and in that case, you can call
$object = (object) $machine;
Or it is a string aka JSON, you can call
$object = json_decode($machine);
Or if it is an object/array use
$machine['machineType'];
Also please add a dump of the $machine var
EDIT
Try sending the request not using [] because they will be converted into an array with objects in it, instead, if you remove the [] and only have {} it should only be one object in the request
"machineInfo" : {
"machineNo" : "1123213",
"accountType" : "Paid",
//...rest here..
}
Try this:
public function createRetailer(Request $request){
$machines = $request->machineInfo;
foreach($machines as $machine){
$object = (object) $machine;
return response()->json(is_object($object));
}
}
Since your request sends data as an array, you can access the elements as so :
public function createRetailer(Request $request){
$machines = $request->machineInfo;
foreach($machines as $machine){
$newMachine = new Machine;
$newMachine->machine_no = $machine['machineNo'];
$newMachine->account_type = $machine['accountType'];
$newMachine->machine_type = $machine->['machineType'];
$newMachine->retialer_id = $retailer->['retailerId'];
$newMachine->save();
}
}

How to return complex objects in a Laravel API

I'm building a REST API using Laravel and wondering if there's a standard and accepted way (Framework or Package) to return complex collections as the response.
What I'm currently doing is, returning the object with json method,
public function show() {
$ad = Ad::with('category')->get()->find($id);
return Response::json($ad);
}
Or, just return the object where it automatically return as json
public function show() {
$ad = Ad::with('category')->get()->find($id);
return $ad;
}
This way is fine with the above method because relations are pre-contained before coveting to json. However, let's say I need to return the saved object just after store() is called. Like this...
public function store(SaveAdRequest $request){
$ad = new Ad();
$ad->title = $request->title;
$ad->description = $request->description;
$ad->category = $request->category;
$ad->save();
return $ad;
}
In above store() method, I have no direct way to get relations of the just saved object.
So, how to overcome this problem and is there a standard way of returning these complex objects rather than using json ?
Thanks!
There is no standard way for returning data from a REST API. You can return JSON, XML, HTML. Whatever suits you.
The common thing is that you have somehow serialize your (PHP) object before sending it over the wire. Recently JSON is used a lot because its light-weight, easy to process and readable by humans too.
A complex object is just another JSON object inside a JSON object.
public function store(SaveAdRequest $request){
$ad = new Ad();
$ad->title = $request->title;
$ad->description = $request->description;
$ad->category = $request->category;
$ad->save();
$data = Ad::where('id', $ad->id)->with('category')->get();
return Response::json($data );
}

How to return encoded json with json object in json codeigniter rest server

I am using the codeigniter rest by phil sturgeon.
I want to return a JSON object that contains another JSON object within.
My code looks like this
function volunteer_get()
{
if(!$this->get('id'))
{
$this->response(NULL, 400);
}
$this->load->model('user/users_model');
$user = $this->users_model->get( $this->get('id') );
$address = $this->address_model->getAddress( $this->get('id') );
$user->address = $address;
$userJson = json_encode($user);
var_dump($userJson);
/*if($user && $user->auth_level == 1)
{
$this->response($userJson, 200); // 200 being the HTTP response code
}
else
{
$this->response(NULL, 404);
}*/
}
It is not showing any result... If i do this without adding the php object in the other php object, it shows me the json!
D:\wamp\www\codeigniter\application\controllers\api\Users.php:37:string '{"user_id":"1","username":"abc","email":"abc","auth_level":"1","banned":null,"passwd":"abcdefg","passwd_recovery_code":"abcdefg","passwd_recovery_date":"2017-06-12 18:50:31","passwd_modified_at":"2016-11-18 21:20:30","last_login":"2017-08-30 15:10:36","created_at":"2016-09-02 12:01:46","modified_at":"2017-08-30 15:22:45","first_name":"aze","family_name":"'... (length=1354)
In general, you need to check whether you got a JSON object (usually a PHP dictionary or object) or a JSON representation (a string).
You can not add a string to another string. And if you add a string to a dictionary or object, it won't be properly encoded as a JSON sub-object because it is, well, a string.
So if you have a representation, you have to decode it first:
// Here, $dataOne is a string, and $dataTwo is too.
// we decode to array rather than to object (see manual for json_encode)
$dataOneJSON = json_decode($dataOne, true);
$dataTwoJSON = json_decode($dataTwo, true);
$dataOneJSON['two'] = $dataTwoJSON;
$result = json_encode($dataOneJSON);
$this->response($result, 200);

Guzzle 6: no more json() method for responses

Previously in Guzzle 5.3:
$response = $client->get('http://httpbin.org/get');
$array = $response->json(); // Yoohoo
var_dump($array[0]['origin']);
I could easily get a PHP array from a JSON response. Now In Guzzle 6, I don't know how to do. There seems to be no json() method anymore. I (quickly) read the doc from the latest version and don't found anything about JSON responses. I think I missed something, maybe there is a new concept that I don't understand (or maybe I did not read correctly).
Is this (below) new way the only way?
$response = $client->get('http://httpbin.org/get');
$array = json_decode($response->getBody()->getContents(), true); // :'(
var_dump($array[0]['origin']);
Or is there an helper or something like that?
I use json_decode($response->getBody()) now instead of $response->json().
I suspect this might be a casualty of PSR-7 compliance.
You switch to:
json_decode($response->getBody(), true)
Instead of the other comment if you want it to work exactly as before in order to get arrays instead of objects.
I use $response->getBody()->getContents() to get JSON from response.
Guzzle version 6.3.0.
If you guys still interested, here is my workaround based on Guzzle middleware feature:
Create JsonAwaraResponse that will decode JSON response by Content-Type HTTP header, if not - it will act as standard Guzzle Response:
<?php
namespace GuzzleHttp\Psr7;
class JsonAwareResponse extends Response
{
/**
* Cache for performance
* #var array
*/
private $json;
public function getBody()
{
if ($this->json) {
return $this->json;
}
// get parent Body stream
$body = parent::getBody();
// if JSON HTTP header detected - then decode
if (false !== strpos($this->getHeaderLine('Content-Type'), 'application/json')) {
return $this->json = \json_decode($body, true);
}
return $body;
}
}
Create Middleware which going to replace Guzzle PSR-7 responses with above Response implementation:
<?php
$client = new \GuzzleHttp\Client();
/** #var HandlerStack $handler */
$handler = $client->getConfig('handler');
$handler->push(\GuzzleHttp\Middleware::mapResponse(function (\Psr\Http\Message\ResponseInterface $response) {
return new \GuzzleHttp\Psr7\JsonAwareResponse(
$response->getStatusCode(),
$response->getHeaders(),
$response->getBody(),
$response->getProtocolVersion(),
$response->getReasonPhrase()
);
}), 'json_decode_middleware');
After this to retrieve JSON as PHP native array use Guzzle as always:
$jsonArray = $client->get('http://httpbin.org/headers')->getBody();
Tested with guzzlehttp/guzzle 6.3.3
$response is instance of PSR-7 ResponseInterface. For more details see https://www.php-fig.org/psr/psr-7/#3-interfaces
getBody() returns StreamInterface:
/**
* Gets the body of the message.
*
* #return StreamInterface Returns the body as a stream.
*/
public function getBody();
StreamInterface implements __toString() which does
Reads all data from the stream into a string, from the beginning to end.
Therefore, to read body as string, you have to cast it to string:
$stringBody = $response->getBody()->__toString()
Gotchas
json_decode($response->getBody() is not the best solution as it magically casts stream into string for you. json_decode() requires string as 1st argument.
Don't use $response->getBody()->getContents() unless you know what you're doing. If you read documentation for getContents(), it says: Returns the remaining contents in a string. Therefore, calling getContents() reads the rest of the stream and calling it again returns nothing because stream is already at the end. You'd have to rewind the stream between those calls.
Adding ->getContents() doesn't return jSON response, instead it returns as text.
You can simply use json_decode

Categories