Building custom JSON data with variable multidimensional arrays

Hi,

I am trying to find the best ways to output custom JSON data based on the the array that is passed in. I cannot just use json_encode as there are specific rules that it will not service.

In the passed-in $data $key / $values pairs, if the $values is an array it needs to run the same method as the original passed-in $data.

Here is a example convert_array_to_js( $data ) method that needs to process each inner array:

private function convert_array_to_js( $data, $parenthsis = false ) {

	$js ='';
	$count = count( $data );
	$i = 1;

	foreach( $data as $key => $value ) {

		if ( true == $parenthsis ) {
			$js .= $this->newlines( 1 ) . '{';
		} else if ( '{' === $value ) {
			$js .= $this->newlines( 1 ) . '{';
		} else  if ( is_bool( $value ) ) {
			$js .= $this->newlines( 2 ) . "'$key': " . json_encode( $value );
			$js .= $this->get_comma( $count, $i, $js );
		} else if ( '[' == $value ) {
			$js .= '['; 
		} else if ( is_int( $value ) ) {
			$js .= $this->newlines( 2 ) . "'$key': " . $value;
			$js .= $this->get_comma( $count, $i, $js );
		} else if ( ']' == $value ) {
			$js .= $value;
			$js .= $this->get_comma( $count, $i, $js );
		} else if ( $count == $i && true == $parenthsis ) {
			$js .= $this->newlines( 1 ) . '}';
		} else if (  '}' === $value ) {
			$js .= $this->newlines( 1 ) . '}';
		} else {
			$js .= $this->newlines( 2 ) . "'$key': " . "'$value'";
			$js .= $this->get_comma( $count, $i, $js );
		}

		$i++;
	}

	return $js;
}

Here is an example arbitrary array:

$values = array( 
'foods' => 
	array( 
	  '['
	  , '{'
	  , array ( 
	    'name' => 'carrot'
	   , 'type' => 'veggie'
	   , 'goodies' => array(
		 'chocolate' => array( 'bars', 'cake', 'beans' )
		, 'fruits' => array( 'apples', 'pears', 'blue-berries' )
	    ) 
         )
  , '}'
  , ']'
  )
);

Now I tried using array_walk_recursive() calling itself; however it does not run the callback on $values that are arrays, so this wonā€™t work.

I also thought about using the SPL iterator classes to define custom behaviour. I have not worked with these very much so I was able to put together a version that flattened the entire array, but this is not what I want.

Output this should define a JS object literal like so:

'foods': [
  {
    { 'name' : 'carrot', 'type' :  'veggie'  }
    , 'goodies' : {
       'chocolate': { 'bars', 'cake', 'beans' }
      'fruits': { 'apples', 'pears', 'blue-berries' }
    }
  }
];

If I have a simpler array like:

$data = array( 
'data' => '{'
  , array{ 'name' => 'weather', 'temp'' => 'cool' }
  , array{ 'name' => 'vehicle', 'type' => 'truck' }
 , '}'
);

It should output:

 'data' :  {
    { 'name' : 'weather', 'temp' : 'cool' }
  , { 'name' :  'vehicle', 'type' : ; 'truck' }
 }

Do you have any recommendations on the best way to do this from the perspective: fast execution, ease of code understanding ( for team members ), and minimizes the foreach depths like array_map and array_walk_recursive do?

Thanks,
Steve

What is it that you want to do that json_encode canā€™t handle?

This is what json_encode( $array ) returns:

{"foods":["[","{",{"name":"carrot","type":"veggie","goodies":["chocolate","fruits","gummy bears"]},"}","]"]} 

of the array that I provided in the first post. It should be output like the example output I provided.

Regards,
Steve

Hi,

So here is a little that may help describe what the code should do.

A user can pass a single dimension, or multi-dimension, associative or non associative array will be turned into valid JS.

Rules;

  • these square brackets should never be quoted [ and ]
  • the { and } parenthesise should never be quoted
  • all other single non-multi-dimension values should be quoted
  • commas are put at the end of key / value pairs and closing }
  • the array depths should never exceed 4 levels or recursion; although technically there should not be a set limit on how deep in an array it could behave.

Hope this helps.

Steve

Hey Steve,

I guess I must be missing something. Why would you have an array like this in the first place, with curly braces and square brackets as part of the data? Is it parsed from some other format?

Hi,

It relates to the fact that one third party api that we need to use requires the array like:

my.adConfig = {
"site": "vp_ind.com",
"async": true,
"sra": true,
"networkId": "3081",
"mobileSite": "vp.m",
"disableInitialLoad": false,
"zone": "index",
    "keys": {
      "nk":  "print",
      "pr":  "vp",
      "imp":  "index",
      "ck":  "index"	
   },
"adslots":[
	{ "name": "gpt-impulse",  "type": "impulse" },
	{ "name": "gpt-wallpaper",  "type": "wallpaper" }								
  ]
			
};

So a couple things regarding this:

  • a user may pass in basic paired data like the ā€œasyncā€: true, so it will need to follow the same format as the async pair.
  • a user may pass an array like how ā€˜keysā€™ is formatted;
  • the api may require other calls that have the same format of the adslots.Please notice how the adslots is formatted; it is a JavaScript Object Literal that has a property adslots that is an JS array.

The way that I thought to be able to handle this is if a unknown array was passed in, wherever it would include a [ and ] brackets I could detect it with php and honour the rule to keep it unquoted. The other { } donā€™t need to go into the array, they can follow rules and be added as needed or be subject to json_encode and will work well. Essentially I donā€™t want unknown arrays that cause those creating them to have to use more than one way to specify the object literal that they want added to the adConfig.

If you have a better suggestion Iā€™m all ears.

Regards,
Steve

Iā€™m also trying along the path of this:

public function a (  ) {
	$data =  $this->get_value_array();
	$iterator = new RecursiveArrayIterator( $data );
	iterator_apply ( $iterator, array( 'my_object', 'b' ), array( $iterator ) ); 	
}

public function b( $iterator ) {

	While ( $iterator->valid() ) {

		if ( $iterator -> hasChildren() ) {
			$this->b( $iterator -> getChildren() );
		} else {
			echo $this->newlines( 2 ) 
			. "'". $iterator->key() . "': " 
			. "'". $iterator->current() . "'" . PHP_EOL;
		}

		$iterator->next();
	
	}
}

But Iā€™m getting an ā€˜undefinedā€™ key errir when I pass the array shown in the last post?

Steve

So I just updated the last shown method to:

	public function a (  ) {

	$data =  $this->get_value_array();
	
	$iterator = new RecursiveArrayIterator( $data );
	iterator_apply ( $iterator, array( 'PN_Postmedia_Object', 'b' ), array( $iterator ) ); 

	
}

public function b( $iterator ) {

	While ( $iterator->valid() ) {

		if ( $iterator -> hasChildren() ) {
			$this->b( $iterator -> getChildren() );
		} else {
			echo $iterator->key() . "': " 
			. "'". $iterator->current() . "'" . PHP_EOL;
		}

		$iterator->next();
	
	}
}

and Iā€™m getting

 '0': '['
 '1': '{'
' name': 'gpt-impulse'
 'type': 'impulse'
 '0': 'chocolate'
 '1': 'fruits'
 '2': 'gummy bears'
 '3': '}'
 '4': ']'

So the RecursiveArrayIterator() does not seem to step down deeper in the array; or i donā€™t understand how to get it to go deeper?

Regards,
Steve

write out your desired json and run json_decode on it. print_r() on the array to see how the array is formatted.

using this example that you provided as

{
"site": "vp_ind.com",
"async": true,
"sra": true,
"networkId": "3081",
"mobileSite": "vp.m",
"disableInitialLoad": false,
"zone": "index",
    "keys": {
      "nk":  "print",
      "pr":  "vp",
      "imp":  "index",
      "ck":  "index"	
   },
"adslots":[
	{ "name": "gpt-impulse",  "type": "impulse" },
	{ "name": "gpt-wallpaper",  "type": "wallpaper" }								
  ]
			
}

the resulting array output is

stdClass Object ( [site] => vp_ind.com [async] => 1 [sra] => 1 [networkId] => 3081 [mobileSite] => vp.m [disableInitialLoad] => [zone] => index [keys] => stdClass Object ( [nk] => print [pr] => vp [imp] => index [ck] => index ) [adslots] => Array ( [0] => stdClass Object ( [name] => gpt-impulse [type] => impulse ) [1] => stdClass Object ( [name] => gpt-wallpaper [type] => wallpaper ) ) )

you use the key value pair as an associative array with variable depth to construct your array for json encoding.

$data["Name"] = "Boo";

the json would be

{
"Name" : "Boo"
}

Hopefully this help you out. The way your doing it would be to manually create the json(your array has json in it) when you can construct an associative array with some depth(multiple dimensions) and use the much faster json_encode().

Hey animedreamz73,

That wifi work really well! Great idea!

Thank you,
Steve

For anyone interested.

I ended up doing what @fretburner suggested ( or inferred / questioned );

It made it a WHOLE lot easier when I changed my array to:

$values = array( 
  'adslots' => 
	array( 
	    array ( 
		'name' => 'gpt-impulse'
		, 'type' => 'impulse'
		, 'goodies' =>
		array(
		    'chocolate'
		    , 'gummy bears'
		) 
	   )
       )
);

and then doing:

public function process_ad_property_data_to_json() {

	$data =  $this->get_value_array();

	return json_encode( $data,  JSON_PRETTY_PRINT );
}

Which outputs:

{
    "adslots": [
        {
            "name": "gpt-impulse",
            "type": "impulse",
            "goodies": [
                "chocolate",
                "fruits",
                "gummy bears"
            ]
        }
    ]
}

Thanks to all for your suggestions as you pointed me to a much easier path :smile:

2 Likes

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.