Modify API's JSON output to return JSON, not string in one of the fields

tl;dr How can I modify JSON output of GET request of my API application so that anything user puts into one of the model’s fields (textbox; presumably JSON) is output as a JSON, not as a string?


I have a very simple RESTapi application (based on Yii 2 Advanced Project Template). It is simple enough so I can use basic controller setup of:

namespace app\controllers;

use yii\rest\ActiveController;

class UserController extends ActiveController
{
    public $modelClass = 'app\models\User';
}

I don’t have any extra actions and therefore I do not modify output in anyway. I have just configured my application to output JSON, but I let all the dirty work with output generation to be done by Yii.

My current setup generates responses like that:

{
    "id": 1,
    "to": 2,
    "from": 1,
    "type": 1,
    "status": 3,
    "body": "",
    "result": "",
    "created_at": 1698085035,
    "updated_at": 1698085315
}

User can enter anything (but presumably JSON) into body fields and its gets output like that:

{
    "id": 5,
    "to": 2,
    "from": 1,
    "type": 0,
    "status": 1,
    "body": "{\r\n    \"operation\": \"move\",\r\n    \"engine\": \"left-1\",\r\n    \"speed\": 5,\r\n    \"timeframe\": 4,\r\n    \"direction\": 1\r\n}",
    "result": "",
    "created_at": 1698085399,
    "updated_at": 1698221734
}

What steps must I undertake to have the body field formatted / output like it would be just another part of the main JSON code returned by this GET request?

For me it doesn’t matter, if for body JSON is formatted at the output only or if user-data provided to textbox gets converted and stored as JSON in DB. All that matters is to get pure / clean JSON instead of above-exampled string-like “garbage”.

The model behind is in relation to another model, so if I use expand field in request, I am getting a beautifully merged data, all in JSON:

{
    "id": 1,
    "to": 2,
    "from": 1,
    "type": 1,
    "status": 3,
    "body": "",
    "result": "",
    "created_at": 1698085035,
    "updated_at": 1698085315,
    "destination": {
        "id": 2,
        "type": 1,
        "status": 1,
        "tid": "T1A-1",
        "name": "TC-MED Platform",
        "created_at": 1698079476,
        "updated_at": 1698079476
    }
}

But it seems I can’t achieve the same for user-formatted data.

BTW: This question is to only know the methodology that I must implement. I know and understand that I have to utilize any kind of checking and purification of the user-provided input. But that’s not a case in this question.

It’s all about some field type juggling. At input the ‘body’ model attribute is an string, while at output it should be an array.

  1. At some point you have to decode it to array using e.g. https://www.yiiframework.com/doc/api/2.0/yii-helpers-json#decode()-detail
  2. Preparing model for output you can use fields overriding https://www.yiiframework.com/doc/guide/2.0/en/rest-resources#overriding-fields to have same field name in model but different representation, this time as an array, probably using some callback.
1 Like

Without knowing how body come into the scene it is hard to say. But it seems it comes as string not array. so do json_decode on it before attaching to the final output.

$body = wherever_body_is_coming();
$finaljson['body'] = json_decode($body);
1 Like

Thanks for trying to help. It’s already given it the question.

It comes directly from model / database. My API controller is completely empty (pointing to correct model only). Everything that happens between getting the value from database and bringing body to the scene is a pure internal Yii 2 magic.

At this moment I do not modify absolutely nothing around this database column / model attribute. Whatever user puts into textbox in Gii-generated from is stored as a string in database and is published through API endpoint.

Since my controller has nothing more than just this:

namespace app\controllers;

use yii\rest\ActiveController;

class UserController extends ActiveController
{
    public $modelClass = 'app\models\User';
}

The question is: where (actionIndex?) and what to put to have body in response converted from string to array / JSON output?

BTW: I’ll try Bart’s approach this evening and will report any results here.

In that case it is easy. Just override fields function and decode the model value (which seems to me an encoded JSON into string)

here is an example

<?php 

namespace app\models;

use yii\db\ActiveRecord;
use yii\helpers\Json;

class User extends ActiveRecord
{
    //All normal AR stuffs

    function fields()
    {
        $fields = parent::fields();
        $fields['body'] = function($model){
            return Json::decode($model->body);
        };

        return $fields;
    }
}
3 Likes

This is exactly what I was looking for. Thank you.

I hope that I would manage to figure out the same myself. You were just faster! :]

1 Like

You are welcome!
:grinning: