Using json_encode on objects in PHP (regardless of scope) - php

I'm trying to output lists of objects as json and would like to know if there's a way to make objects usable to json_encode? The code I've got looks something like
$related = $user->getRelatedUsers();
echo json_encode($related);
Right now, I'm just iterating through the array of users and individually exporting them into arrays for json_encode to turn into usable json for me. I've already tried making the objects iterable, but json_encode just seems to skip them anyway.
edit: here's the var_dump();
php > var_dump($a);
object(RedBean_OODBBean)#14 (2) {
["properties":"RedBean_OODBBean":private]=>
array(11) {
["id"]=>
string(5) "17972"
["pk_UniversalID"]=>
string(5) "18830"
["UniversalIdentity"]=>
string(1) "1"
["UniversalUserName"]=>
string(9) "showforce"
["UniversalPassword"]=>
string(32) ""
["UniversalDomain"]=>
string(1) "0"
["UniversalCrunchBase"]=>
string(1) "0"
["isApproved"]=>
string(1) "0"
["accountHash"]=>
string(32) ""
["CurrentEvent"]=>
string(4) "1204"
["userType"]=>
string(7) "company"
}
["__info":"RedBean_OODBBean":private]=>
array(4) {
["type"]=>
string(4) "user"
["sys"]=>
array(1) {
["idfield"]=>
string(2) "id"
}
["tainted"]=>
bool(false)
["model"]=>
object(Model_User)#16 (1) {
["bean":protected]=>
*RECURSION*
}
}
}
and here's what json_encode gives me:
php > echo json_encode($a);
{}
I ended up with just this:
function json_encode_objs($item){
if(!is_array($item) && !is_object($item)){
return json_encode($item);
}else{
$pieces = array();
foreach($item as $k=>$v){
$pieces[] = "\"$k\":".json_encode_objs($v);
}
return '{'.implode(',',$pieces).'}';
}
}
It takes arrays full of those objects or just single instances and turns them into json - I use it instead of json_encode. I'm sure there are places I could make it better, but I was hoping that json_encode would be able to detect when to iterate through an object based on its exposed interfaces.

All the properties of your object are private. aka... not available outside their class's scope.
Solution for PHP >= 5.4
Use the new JsonSerializable Interface to provide your own json representation to be used by json_encode
class Thing implements JsonSerializable {
...
public function jsonSerialize() {
return [
'something' => $this->something,
'protected_something' => $this->get_protected_something(),
'private_something' => $this->get_private_something()
];
}
...
}
Solution for PHP < 5.4
If you do want to serialize your private and protected object properties, you have to implement a JSON encoding function inside your Class that utilizes json_encode() on a data structure you create for this purpose.
class Thing {
...
public function to_json() {
return json_encode(array(
'something' => $this->something,
'protected_something' => $this->get_protected_something(),
'private_something' => $this->get_private_something()
));
}
...
}
A more detailed writeup

In PHP >= 5.4.0 there is a new interface for serializing objects to JSON : JsonSerializable
Just implement the interface in your object and define a JsonSerializable method which will be called when you use json_encode.
So the solution for PHP >= 5.4.0 should look something like this:
class JsonObject implements JsonSerializable
{
// properties
// function called when encoded with json_encode
public function jsonSerialize()
{
return get_object_vars($this);
}
}

In RedBeanPHP 2.0 there is a mass-export function which turns an entire collection of beans into arrays. This works with the JSON encoder..
json_encode( R::exportAll( $beans ) );

Following code worked for me:
public function jsonSerialize()
{
return get_object_vars($this);
}

I didn't see this mentioned yet, but beans have a built-in method called getProperties().
So, to use it:
// What bean do we want to get?
$type = 'book';
$id = 13;
// Load the bean
$post = R::load($type,$id);
// Get the properties
$props = $post->getProperties();
// Print the JSON-encoded value
print json_encode($props);
This outputs:
{
"id": "13",
"title": "Oliver Twist",
"author": "Charles Dickens"
}
Now take it a step further. If we have an array of beans...
// An array of beans (just an example)
$series = array($post,$post,$post);
...then we could do the following:
Loop through the array with a foreach loop.
Replace each element (a bean) with an array of the bean's properties.
So this...
foreach ($series as &$val) {
$val = $val->getProperties();
}
print json_encode($series);
...outputs this:
[
{
"id": "13",
"title": "Oliver Twist",
"author": "Charles Dickens"
},
{
"id": "13",
"title": "Oliver Twist",
"author": "Charles Dickens"
},
{
"id": "13",
"title": "Oliver Twist",
"author": "Charles Dickens"
}
]
Hope this helps!

I usually include a small function in my objects which allows me to dump to array or json or xml. Something like:
public function exportObj($method = 'a')
{
if($method == 'j')
{
return json_encode(get_object_vars($this));
}
else
{
return get_object_vars($this);
}
}
either way, get_object_vars() is probably useful to you.

$products=R::findAll('products');
$string = rtrim(implode(',', $products), ',');
echo $string;

Here is my way:
function xml2array($xml_data)
{
$xml_to_array = [];
if(isset($xml_data))
{
if(is_iterable($xml_data))
{
foreach($xml_data as $key => $value)
{
if(is_object($value))
{
if(empty((array)$value))
{
$value = (string)$value;
}
else
{
$value = (array)$value;
}
$value = xml2array($value);
}
$xml_to_array[$key] = $value;
}
}
else
{
$xml_to_array = $xml_data;
}
}
return $xml_to_array;
}

for an array of objects, I used something like this, while following the custom method for php < 5.4:
$jsArray=array();
//transaction is an array of the class transaction
//which implements the method to_json
foreach($transactions as $tran)
{
$jsArray[]=$tran->to_json();
}
echo json_encode($jsArray);

Related

Format array of objects for possible json output for Postman/GET requests

So I can't seem to figure this out, so I'm reaching out to see if someone might be able to help me.
Please let me know what the best output is so that I could use GET to retrieve clean data for the endpoint that I've created.
I have the following method:
function instagram_posts(): bool|string
{
if (!function_exists('is_plugin_active')) {
include_once(ABSPATH . 'wp-admin/includes/plugin.php');
}
if (!is_plugin_active('fh-instagram/autoload.php')) {
return false;
}
if (empty($items = Instagram::get_items_for_api(50))) {
return false;
}
var_dump($items);
var_dump(json_encode($items));
return json_encode($items);
}
var_dump($items); gives me the following output:
array(50) {
[0]=>
object(Plugin\Instagram\Item)#976 (7) {
["id":"Plugin\Instagram\Item":private]=>
}
[1]=>
object(Plugin\Instagram\Item)#1030 (7) {
["id":"Plugin\Instagram\Item":private]=>
string(17) "17842233125750202"
}
}
When I run var_dump(json_encode($items)); I get the following output:
string(151) "[{},{}]"
How can I convert my array of objects so that it can transform it to json and then use it within Postman? This is what it currently looks like in Postman:
array(50) {
[0]=>
object(Plugin\Instagram\Item)#973 (7) {
["id":"Plugin\Instagram\Item":private]=>
string(17) "17992874035441353"
}
[1]=>
object(Plugin\Instagram\Item)#1027 (7) {
["id":"Plugin\Instagram\Item":private]=>
string(17) "17842233125750202"
}
}
It should be outputted such as:
[
{"id": etc..}
]
All help will be appreciated!
The instagram_posts method is being use below:
add_action('rest_api_init', function () {
register_rest_route( 'instagram', '/posts/', [
'methods' => 'GET',
'callback' => 'instagram_posts',
]);
});
So I can use Postman to access the endpoint: http://headlesscms.com.local/wp-json/instagram/posts
Since the property you want is private, it won't be included in the results of json_encode(). Only public properties will.
You need to create a multidimensional array with the structure you want and encode that.
// This is the new array we will push the sub arrays into
$results = [];
foreach($items as $item) {
$results[] = ['id' => $item->get_id()];
}
return json_encode($results);
This will give you a json structure that looks like:
[
{
"id": 1
},
{
"id": 2
},
...
]
Alternative format
If you only want a list of id's, you might not need to create a multidimensional array, but rather just return a list of ids.
In that case, do this:
$results = [];
foreach ($items as $item) {
$results[] = $item->get_id();
}
return json_encode($results);
That would give you:
[
1,
2,
...
]

Combine two foreach loops for better inefficiency?

I have the following code but I am wondering how I can make it more efficient.
if ($genres){
$arr = array();
foreach ($genres as $i) {
$arr[] = $i->name;
}
$genres_arr = $arr;
}
if ($themes){
$arr = array();
foreach ($themes as $i) {
$arr[] = $i->name;
}
$themes_arr = $arr;
}
var_dump($genres_arr);
var_dump($themes_arr);
I've tried putting them into an if statement but because they both always exists only the first one runs. I want to check to see if both exist and always run them both through a foreach loop. If only one exists I want only the one to run.
These are the array structures.
["genres"]=>
array(1) {
[0]=>
object(stdClass)#1579 (2) {
["id"]=>
int(25)
["name"]=>
string(26) "Hack and slash/Beat 'em up"
}
}
["themes"]=>
array(3) {
[0]=>
object(stdClass)#1576 (2) {
["id"]=>
int(1)
["name"]=>
string(6) "Action"
}
}
I want to have them as flattered as at the moment they are inside objects. I am then going to implode them into a list for WordPress use.
This code works but its repetitive and some help would be great!
I think you can use array column because it can read values from "A multi-dimensional array or an array of objects from which to pull a column of values from" like this:
if ($genres) {
$genres_arr = array_column($genres, 'name');
}
if ($themes) {
$themes_arr = array_column($themes, 'name');
}
var_dump($genres_arr);
var_dump($themes_arr);
In this way the easiest simplification would be to introduce a new function, which builds the array.
function getNames($arr) {
if (!is_array($arra)) return false;
return array_map(function($item) {
return $item->name;
}, $arr);
}
$themes_arr = getNames($themes);
$genre_arr = getNames($genres);
This is how I would combine them
$sets = [];
if ($genres){
$sets['genres'] = $genres;
}
if ($themes){
$sets['themes'] = $themes;
}
$arr = array();
foreach( $sets as $type => $data ){
foreach ($genres as $i) {
$arr[$type][] = $i->name;
}
}
claudio's answer is perfect for this situation, but in the more general case you can also define a mapping function, which you then use with array_map for both sets of objects, e.g.
$mapper = function ($item) { return $item->name; };
$genres_arr = array_map($mapper, $genres);
$themes_arr = array_map($mapper, $themes);
This has the advantage of being able to run more complex logic (using a getter function instead of direct property access, etc), if you need it in future.
Ideally, both objects would implement a common interface, so that it was clear exactly what kinds of objects the mapping function was designed for.

Converting multidimensional object to array

I have a following object:
object(Intro\IntroBundle\Entity\User)#333 (6) {
["id":"Intro\IntroBundle\Entity\User":private]=>
int(3)
["username":"Intro\IntroBundle\Entity\User":private]=>
string(3) "aaa"
["email":"Intro\IntroBundle\Entity\User":private]=>
string(9) "aa#aa.com"
["password":"Intro\IntroBundle\Entity\User":private]=>
string(4) "a1a1"
["age":"Intro\IntroBundle\Entity\User":private]=>
int(10)
["dateCreated":"Intro\IntroBundle\Entity\User":private]=>
object(DateTime)#330 (3) {
["date"]=>
string(26) "2011-01-01 01:01:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/Berlin"
}
}
I used methods:
array($entity);
json_decode(json_encode($entity), true);
get_object_vars ($entity);
and with a personal method
private function objToArray($obj){
$arr = array();
if(!is_object($obj) && !is_array($obj)){
$arr = $obj;
die("invalid data");
}
foreach ($obj as $key => $value)
{
if (!empty($value))
{
$arr[$key] = array();
objToArray($value, $arr[$key]);
}
else
{
$arr[$key] = $value;
}
}
return $arr;
}
All those methods return an empty array. Please tell me why? I mention that my object input ($entity) is not empty.
And also please don't give me dislike if you do not know the answer.
Your object's properties are private. If you var_dump() an object as you did it prints also private properties.
Have a look at this article http://php.net/manual/en/language.oop5.iterations.php. You can iterate object as you do but it iterates only public properties.
First of all, we can't see the scope. All your code is private (object properties and the method). Show the scope and we can help you better.
The problem seems to be here:
if (!empty($value))
{
$arr[$key] = array();
objToArray($value, $arr[$key]);
}
You are passing a second argument that doesn't defined in the method
private function objToArray($obj){...}
So you are passing a value to objToArray(), that value is a string I guess, and when it reach this line:
$arr = array();
if(!is_object($obj) && !is_array($obj)){
$arr = $obj;
die("invalid data");
}
Then it will display "invalid data", and empty your array.

Convert multidimensional objects to array [duplicate]

This question already has answers here:
Convert a PHP object to an associative array
(33 answers)
Closed 6 months ago.
I'm using amazon product advertising api. Values are returned as a multidimensional objects.
It looks like this:
object(AmazonProduct_Result)#222 (5) {
["_code":protected]=>
int(200)
["_data":protected]=>
string(16538)
array(2) {
["IsValid"]=>
string(4) "True"
["Items"]=>
array(1) {
[0]=>
object(AmazonProduct_Item)#19 (1) {
["_values":protected]=>
array(11) {
["ASIN"]=>
string(10) "B005HNF01O"
["ParentASIN"]=>
string(10) "B008RKEIZ8"
["DetailPageURL"]=>
string(120) "http://www.amazon.com/Case-Logic-TBC-302-FFP-Compact/dp/B005HNF01O?SubscriptionId=AKIAJNFRQCIJLTY6LDTA&tag=*********-20"
["ItemLinks"]=>
array(7) {
[0]=>
object(AmazonProduct_ItemLink)#18 (1) {
["_values":protected]=>
array(2) {
["Description"]=>
string(17) "Technical Details"
["URL"]=>
string(217) "http://www.amazon.com/Case-Logic-TBC-302-FFP-Compact/dp/tech-data/B005HNF01O%3FSubscriptionId%3DAKIAJNFRQCIJLTY6LDTA%26tag%*******-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB005HNF01O"
}
}
[1]=>
object(AmazonProduct_ItemLink)#17 (1) {
["_values":protected]=>
array(2) {
I mean it also has array inside objects. I would like to convert all of them into a multidimensional array.
I know this is old but you could try the following piece of code:
$array = json_decode(json_encode($object), true);
where $object is the response of the API.
You can use recursive function like below:
function object_to_array($obj, &$arr)
{
if (!is_object($obj) && !is_array($obj))
{
$arr = $obj;
return $arr;
}
foreach ($obj as $key => $value)
{
if (!empty($value))
{
$arr[$key] = array();
objToArray($value, $arr[$key]);
}
else {$arr[$key] = $value;}
}
return $arr;
}
function convertObjectToArray($data) {
if (is_object($data)) {
$data = get_object_vars($data);
}
if (is_array($data)) {
return array_map(__FUNCTION__, $data);
}
return $data;
}
Credit to Kevin Op den Kamp.
I wrote a function that does the job, and also converts all json strings to arrays too. This works pretty fine for me.
function is_json($string) {
// php 5.3 or newer needed;
json_decode($string);
return (json_last_error() == JSON_ERROR_NONE);
}
function objectToArray($objectOrArray) {
// if is_json -> decode :
if (is_string($objectOrArray) && is_json($objectOrArray)) $objectOrArray = json_decode($objectOrArray);
// if object -> convert to array :
if (is_object($objectOrArray)) $objectOrArray = (array) $objectOrArray;
// if not array -> just return it (probably string or number) :
if (!is_array($objectOrArray)) return $objectOrArray;
// if empty array -> return [] :
if (count($objectOrArray) == 0) return [];
// repeat tasks for each item :
$output = [];
foreach ($objectOrArray as $key => $o_a) {
$output[$key] = objectToArray($o_a);
}
return $output;
}
This is an old question, but I recently ran into this and came up with my own solution.
array_walk_recursive($array, function(&$item){
if(is_object($item)) $item = (array)$item;
});
Now if $array is an object itself you can just cast it to an array before putting it in array_walk_recursive:
$array = (array)$object;
array_walk_recursive($array, function(&$item){
if(is_object($item)) $item = (array)$item;
});
And the mini-example:
array_walk_recursive($array,function(&$item){if(is_object($item))$item=(array)$item;});
In my case I had an array of stdClass objects from a 3rd party source that had a field/property whose value I needed to use as a reference to find its containing stdClass so I could access other data in that element. Basically comparing nested keys in 2 data sets.
I have to do this many times, so I didn't want to foreach over it for each item I need to find. The solution to that issue is usually array_column, but that doesn't work on objects. So I did the above first.
Just in case you came here as I did and didn't find the right answer for your situation, this modified version of one of the previous answers is what ended up working for me:
protected function objToArray($obj)
{
// Not an object or array
if (!is_object($obj) && !is_array($obj)) {
return $obj;
}
// Parse array
foreach ($obj as $key => $value) {
$arr[$key] = $this->objToArray($value);
}
// Return parsed array
return $arr;
}
The original value is a JSON string. The method call looks like this:
$array = $this->objToArray(json_decode($json, true));

return objects on the fly in php

I know this can be done in javascript like so:
function doSomething(){
var something, something_else, another_thing;
// do something with these vars
return {
attribute1 : something,
array1 : [
something_else,
another_thing
]
}
}
can it be done in php?
You can create a new object of stdClass(), assign its attributes and return it.
$x = new stdClass();
$x->attribute1 = "something";
$x->array1 = array(1,2,3);
var_dump($x);
return $x;
PHP does not support object literals. However, it does have a generic stdClass class, which you can typecast an array into for a somewhat similar syntax.
function doSomething()
{
$something = 1;
$something_else = 2;
$another_thing = 3;
return (object) [
"attribute1" => $something,
"array1" => [
$something_else,
$another_thing
]
];
}
var_dump(doSomething());
will give (demo)
object(stdClass)#1 (2) {
["attribute1"]=> int(1)
["array1"]=> array(2) {
[0]=> int(2)
[1]=> int(3)
}
}
Note that you can only use short array syntax as of PHP 5.4. Before that you'd use array().

Categories