Dabbling with JSON, Object and Array conversions

Dabbling with JSON, Object and Array conversions

Written by Tina Hammar on Feb 1st, 2021 Views Report Post

This article mostly mentions working with JSON data, but the helpers we will create, are also very useful when you want to convert between stdClass objects and Arrays.

The syntax for handling JSON data in php can feel overwhelming. On the other hand, indexed php arrays aren't nice on your keyboard either.

In applications where you have to interact with a lot of JSON there are some really nice packages that will help, but sometimes you don't want to install packages, just to save some keystrokes.

Laravel helpers

Working with data objects doesn’t have to be painful. Laravel has some nifty helpers that does the heavy lifting for you.

Important note: The Laravel helpers are fantastic, they never return any error if the property doesn't exist, they should be your first choice!

Install a package

In a JSON heavy application you’ll want to install packages to help you out. If you choose this path you can ignore the rest of this article :)

My favourites:

Create your own helpers

So, you ended up with an edge case where you don't want to use data_get($array, 'foo.bar.baz') or $array[0][1][3]. You want to use the familiar foo->bar->baz arrow accessor. Ok, here you go:

We are going to create a few helpers:

I like to "camelCase" method names because when I read the code I understand that it is a custom method.

  • arrToObj($array): mixed
  • objToArr(arr|object $object): mixed
  • toJsonObj(arr|object $object): string
  • toJsonNumCheck(arr|object $object): string
  • toJsonArr(arr|object $object): string
  • jsonToPhpArr(string $json): mixed
  • jsonToPhpObj(string $json): mixed

Why not use php type casting?

The methods we are creating are recursive, php (object)/(array) casting is not. They will not work if you have a multidimensional array.

Associative Array example:

$address = [
  'street' => 'My street',
  'zip' => 12345,
  'phones' => [
    'home' => '561-999-9999',
    'cell' => '561-555-5555',
    'office' => [
      'number' => '561-777-7777',
      'extension' => '100',
    ],
  ],
];

Php type casting

$object = (object)$address;

/*
As you can see, the nested arrays are not converted to objects.
{
     "street": "My street",
     "zip": 12345,
     "phones": [
       "home" => "561-999-9999",
       "cell" => "561-555-5555",
       "office" => [
         "number" => "561-777-7777",
         "extension" => "100",
       ],
     ],
   }
*/

Access array


//default php
$extension = $address['phones']['office']['extension'];

//Laravel helper
$extension = data_get($address, 'phones.office.extension');

//Custom helper two lines
$obj = arrToObj($address);
$extension = $obj->phones->office->extension;

//Custom helper inline
$extension = arrToObj($address)->phones->office->extension;

Update array

//default php
$address['phones']['office']['number'] = '561-223';

//Laravel helper
data_set($address, 'phones.office.number', '561-222');

//Custom helper
$obj = arrToObj($address);
$obj->phones->office->number = '561-224';

Multidimensional Arrays to JSON conversion

$cars = array (
  array("Volvo",22,18),
  array("BMW",15,13),
  array("Saab",5,2),
  array("Land Rover",17,15)
);

Convert Array to encoded JSON array.

This one is a bit silly, it's the same as json_encode($cars, true), but I keep forgetting if the $flag should be true or false, so I use the helper instead.

$jsonArr = toJsonArr($cars);
[
    [ "Volvo", 22, 18, ],
    [ "BMW", 15, 13, ],
    [ "Saab", 5, 2, ],
    [ "Land Rover", 17, 15, ],
]

Convert Array to encoded JSON object

$cars = array (
  array('Brand' => 'Volvo', 'foo' => 22, 'bar' => 18),
  array('Brand' => 'BMW', 'foo' => 12, 'bar' => 8),
  array('Brand' => 'Saab', 'foo' => 5, 'bar' => 2),
  array('Brand' => 'Land Rover', 'foo' => 17, 'bar' => 15)
);
$jsonObj = toJsonObj($cars);
{
     "0": {
       "Brand": "Volvo",
       "foo": 22,
       "bar": 18,
     },
     "1": {
       "Brand": "BMW",
       "foo": 12,
       "bar": 8,
     },
     "2": {
       "Brand": "Saab",
       "foo": 5,
       "bar": 2,
     },
     "3": {
       "Brand": "Land Rover",
       "foo": 17,
       "bar": 15,
     },
}

Convert numeric strings to json numbers. Keep your eye on string-num, it is converted to a "real" number.

$cars = array (
  array('Brand' => 'Volvo', 'string-num' => "17", 'num' => 15)
);

$object = toJsonNumCheck($cars);
[
     {
       "Brand": "Volvo",
       "string-num": 17,
       "bar": 15,
     },
]

Caveats, warning!

1. Keys must exist

Don't use this helpers to access or update data unless you're absolutely sure that the key=>value pairs exist in the array/json data. Otherwise use Laravel data_get()/data_set()

2. Warning, indexed arrays

If you're eager to use the -> array syntax with indexed arrays. Be careful not to end up with a much harder syntax than you bargained for.

$obj = arrToObj(array("Volvo", "BMW", "Toyota"));
//returns
{
     "0": "Volvo",
     "1": "BMW",
     "2": "Toyota",
}
//ouch, painful syntax ;)
$obj->{'0'} //"Volvo"

Working with Eloquent Models

You don't have much use of the helpers when working with Eloquent Models. Laravel allows you to use the arrow syntax directly.

The only thing to keep in mind is how to handle multidimensional arrays in json columns.

Problem: Setting nested values will overwrite any values not specified

return $user->phone_numbers;

/* [
    "home" => "561-999-9999"
    "cell" => "561-555-5555",
    "office" => [
        "number" => "561-777-7777",
        "extension" => "100"
    ],
] */

$user->update([
    'phone_numbers' => [
        'cell' => '222-222-2222',
        'international' => '+358 50 555 55 55',
    ],
]);

return $user->phone_numbers;

/* [
    "cell" => "222-222-2222",
    "international" => "+358 50 555 55 55"
] */

Solution 1: specify full paths in key-value pairs when updating the model.

$user->update([
    'phone_numbers->cell' => '222-222-2222',
    'phone_numbers->international' => '+358 50 555 55 55',
]);

return $user->phone_numbers;

/* [
    "home" => "561-999-9999"
    "cell" => "222-222-2222",
    "office" => [
        "number" => "561-777-7777",
        "extension" => "100"
    ],
    "international" => "+358 50 555 55 55"
] */

Solution 2: Use Laravel helpers.

$user = User::first();
data_set($user, 'phone_numbers.cell', '222-222-2222');
data_set($user, 'phone_numbers.international', '+358 50 555 55 55');
$user->save();

If you want to read more about how to handle Eloquent Model, json columns

The helper file

Create ArrayJsonHelper in App/Helpers, or a directory of your choice.

<?php

if (!function_exists('arrToObj')) {
    /**
     * Recursively convert php Array to stdClass Object
     * @param array $array
     * @return mixed
     */
    function arrToObj(array $array = []): mixed
    {
        return json_decode(json_encode($array, JSON_FORCE_OBJECT));
    }
}



if (!function_exists('objToArr')) {
    /**
     * Recursively convert stdClass Object or Array with nested Objects, to php array
     * @param array|object $object
     * @return mixed
     */
    function objToArr(array|object $object): mixed
    {
        return json_decode(json_encode($object), true);
    }
}

if (!function_exists('toJsonObj')) {
    /**
     * Recursively convert php stdClass Object or Array to JSON object
     * @param array|object $object
     * @return string
     */
    function toJsonObj(array|object $object): string
    {
        return json_encode($object, JSON_FORCE_OBJECT);
    }
}

if (!function_exists('toJsonNumCheck')) {
    /**
     * Recursively convert php stdClass Object or Array to JSON object
     * <br>and encodes numeric strings as numbers
     * @param array|object $object
     * @return string
     */
    function toJsonNumCheck(array|object $object): string
    {
        return json_encode($object, JSON_NUMERIC_CHECK);
    }
}

if (!function_exists('toJsonArr')) {
    /**
     * Recursively convert php stdClass Object or Array to JSON array
     * @param array|object $object
     * @return string
     */
    function toJsonArr(array|object $object): string
    {
        return json_encode($object, true);
    }
}

if (!function_exists('jsonToPhpArr')) {
    /**
     * Recursively convert serialized JSON to php Array
     * @param string $json
     * @return mixed
     */
    function jsonToPhpArr(string $json): mixed
    {
        return json_decode($json, true);
    }
}

if (!function_exists('jsonToPhpObj')) {
    /**
     * Convert JSON to stdClass Object, deep
     * @param string $json
     * @return mixed
     */
    function jsonToPhpObj(string $json): mixed
    {
        return json_decode($json, false);
    }
}

Register the helpers in composer.json

 "autoload": 
    "psr-4": {...},
    //other stuff
    "files": [
        "app/Helpers/ArrayJsonHelper.php"
    ]
},

Create more helpers using available php json constants

Happy coding // Tina :)

Comments (0)