Customize json_decode behaviour - php

I have a JSON string returned by a REST API which follows:
'{"success":true,"product":{"id":"2","category_id":"2","type":"9","name":".ch","description":"","visible":"1","domain_options":"0","stock":"0","qty":"0","autosetup":"2","subdomain":"","owndomain":"0","tax":"0","upgrades":"","sort_order":"0","client_limit":"0","rel":"Product","paytype":"DomainRegular","m_setup":"0.00","q_setup":"0.00","s_setup":"0.00","a_setup":"0.00","b_setup":"0.00","t_setup":"0.00","p4_setup":"0.00","p5_setup":"0.00","d_setup":"0.00","w_setup":"0.00","h_setup":"0.00","m":"0.00","q":"0.00","s":"0.00","a":"0.00","b":"0.00","t":"0.00","p4":"0.00","p5":"0.00","d":"0.00","w":"0.00","h":"0.00","ptype":"DomainsType","options":"3","module":"13","server":"","tlds":null,"periods":{"1":{"product_id":"2","period":"1","register":"17.00","transfer":"17.00","renew":"17.00"}},"tag_name":".ch","tag_description":"","free_domain":"0","product_id":"2","not_renew":"0","epp":true,"ns":["ns3.dfinet.ch","ns4.dfinet.ch","",""],"nsips":"|||","tld":".ch","nsip":["","","",""],"asciimode":true,"app_id":"1","app_ns1":"","app_ns2":"","app_ns3":"","app_ns4":"","app_ip1":"","app_ip2":"","app_ip3":"","app_ip4":"","emails":{"AfterRegistrarRegistration":"28","AfterRegistrarRenewal":"29","AfterRegistrarTransfer":"30","expiringDomain":"54"}},"config":false,"call":"getProductDetails","server_time":1412061849}'
I am trying to convert this to an object and then serve an XML for a soap webservice, what I was doing up to now was
retrieving the result from the rest API -> convert it to object with json_decode($obj)
serve the soap handle() with the converted object
The problem is that, with the following JSON, there are some properties that are "numeric" but not sequential, so the JSON convert the string to an object as follows:
$o = new stdClass();
$o->1 = 'a string';
The problem is that when soap converts object to XML, the node named <1> is an invalid XML markup.
What can I do to "pre-parse" the JSON and convert all of those fake objects to sequentials arrays?
EDIT: Solution based on dmikam answer
I did something cleaner based on the proposed solution:
function fixVariables($variables)
{
if (!is_array($variables) && !is_object($variables)) {
return $variables;
}
foreach ($variables as $k => &$variable) {
if (is_object($variable)) {
if (is_numeric(key($variable))) {
$values = array();
foreach ($variable as $value) {
$values[] = $value;
}
$variable = $values;
unset($values);
}
$this->fixVariables($variable);
} elseif (is_array($variable)) {
if (is_numeric(key($variable))) {
$variable = array_values($variable);
}
$this->fixVariables($variable);
}
}
return $variables;
}

Well, I think you could walk through the resulting parsed object and convert all items that contains numeric indexes into arrays. The function would be something like this:
function fixJsonObject($obj){
if (is_object($obj)){
foreach(get_object_vars($obj) as $key=>$value){
$obj->$key = fixJsonObject($obj->$key);
if (is_numeric($key)){
return (array)$obj;
}
}
}elseif (is_array($obj)){
foreach($obj as $key=>$value){
$obj[$key] = fixJsonObject($obj[$key]);
}
}
return $obj;
}
$json = json_decode('{your: "json"}');
$json = fixJsonObject($json);
I have tested it a bitand looks like it works.

Related

dreamfactory php script post.post_process payload modify

Hi I'm using Dreamfactory as REST API backend and I need a PHP script to pre process a POST api request that can modify my received payload from this:
{“Time”:“2018-12-21T07:49:23”,“BME680”:{“Temperature”:20.3,“Humidity”:41.8,“Pressure”:1021.1,“Gas”:286.65}
to this:
{“Time”:“2018-12-21T07:49:23”,“Temperature”:20.3,“Humidity”:41.8,“Pressure”:1021.1,“Gas”:286.65}
How can I acive this with a PHP script ?
First, let's define a helper function which makes the result friendly
function getFriendlyResult(k, input) {
var output = {};
for (var key in input) {
if (key !== k) output[key] = input[key];
}
for (var innerKey in input[k]) output[innerKey] = input[innerKey];
return output;
}
and you can call it like:
getFriendlyResult(“BME680”, {“Time”:“2018-12-21T07:49:23”,“BME680”:{“Temperature”:20.3,“Humidity”:41.8,“Pressure”:1021.1,“Gas”:286.65});
EDIT
To achieve this in PHP, you can call json_decode and pass your JSON, like
$resultArray = json_decode($input, true);
and then implement the same algorithm in PHP as I described above in Javascript.
EDIT
This is an untested implementation in PHP:
function getFriendlyResult($k, $input) {
$output = array();
foreach ($input as $key => $value) {
if ($key !== $k) $output[$key] = $value;
}
foreach ($input[$k] as $innerKey => $innerValue) {
$output[$innerKey] = $innerValue;
}
return $output;
}
$result = json_decode($yourJSON, true);

How can I access the "count" from the response in SPLObjectStorage?

I am getting this:
How do I receive the count. I've tried a couple of ways like $r->count and $r->count();
This is a gist.
// Results is an SplObjectStorage object where each request is a key
foreach ($results as $request) {
// Get the result (either a ResponseInterface or RequestException)
$result = $results[$request];
if ($result instanceof ResponseInterface) {
// Interact with the response directly
$r = $result->getBody();
echo $r;
} else {
// Get the exception message
echo $result->getMessage();
}
}
Since that's a JSON string, You could decode it into an object using json_decode
$r = $result->getBody();
$response=json_decode($r);
echo $response->count;
Fiddle
I found the answer myself. I had to use get_object_vars(). Converts an object to array.
$r = $result->getBody();
$response=json_decode($r);
$s = get_object_vars($response);
dd($s->count);

Get values from Multidimensional nested array in PHP

Hi I am trying to get fulltext values from this json file on this Link
I am getting only top of the array how can I get all values from all arrays even the nested ones
$json_output = json_decode($json, true);
var_dump($json_output);
foreach($json_output['results'] as $item) {
echo '<br/>'. $item['fulltext'];
}
Id try looking at spl in PHP, it gives you useful iterator classes including ones specially for complex arrays - http://php.net/manual/en/class.recursivearrayiterator.php
another approach would be to
1) convert your json to xml (json -> php array -> xml) - you can use this recursive function:
function to_xml(SimpleXMLElement $object, array $data)
{
foreach ($data as $key => $value)
{
if (is_array($value))
{
$new_object = $object->addChild($key);
to_xml($new_object, $value);
}
else
{
$object->addChild($key, $value);
}
}
}
$xml = new SimpleXMLElement('<rootTag/>');
to_xml($xml, $my_array);
2) query the xml with XPath to fetch the collection of data you need, for example
$titles = $xml->xpath('//#fulltext');
Utilising XPath you can easily fetch data also by complex constraints, check out the docs
Found a solution
foreach($json_output['results'] AS $result) {
foreach($result['printouts']['Covers topic'] AS $topic) {
echo "<ul><li>".$topic['fulltext']."</li></ul>";
}
echo "<br/>".$result['fulltext'];
}
Thanks for your help guys

how to cast an xml object to a associative array?

I am doing some geocoding with the google api and was wondering how do i cast the returned simplexml object? I tried the following but it does no cast the child objects.. ie.. i would like a multi dimensional array.
$url = "http://maps.googleapis.com/maps/api/geocode/xml?address=".$adr."
&sensor=false";
$result = simplexml_load_file($url);
$result = (array) $result;
You could make a JSON request rather than XML; it's recommended; unless your application requires it. Then use:
json_decode( $result, true );
http://us2.php.net/manual/en/function.json-decode.php
I found very useful this function for converting Object to Array recursively:
http://forrst.com/posts/PHP_Recursive_Object_to_Array_good_for_handling-0ka
Adapted from the site above, to use it outside classes:
function object_to_array($obj) {
$arrObj = is_object($obj) ? get_object_vars($obj) : $obj;
foreach ($arrObj as $key => $val) {
$val = (is_array($val) || is_object($val)) ? object_to_array($val) : $val;
$arr[$key] = $val;
}
return $arr;
}
Turn the SimpleXMLElement object into json and decode the json string again into an associative array:
$array = json_decode(json_encode($result), 1);
The simple cast to array does not go more deep in there, that's why the trick via json_encode and json_decode is used.

How to dynamically set array keys in php

I have some logic that is being used to sort data but depending on the user input the data is grouped differently. Right now I have five different functions that contain the same logic but different groupings. Is there a way to combine these functions and dynamically set a value that will group properly. Within the function these assignments are happening
For example, sometimes I store the calculations simply by:
$calcs[$meter['UnitType']['name']] = ...
but other times need a more specific grouping:
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] =...
As you can see sometimes it is stored in a multidiminesional array and other times not. I have been trying to use eval() but without success (not sure that is the correct approach). Storing the data in a temporary variable does not really save much because there are many nested loops and if statements so the array would have to be repeated in multiple places.
EDIT
I hope the following example explains my problem better. It is obviously a dumbed down version:
if(){
$calcs[$meter['UnitType']['name']] = $data;
} else {
while () {
$calcs[$meter['UnitType']['name']] = $data;
}
}
Now the same logic can be used but for storing it in different keys:
if(){
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
} else {
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
}
Is there a way to abstract out the keys in the $calc[] array so that I can have one function instead of having multiple functions with different array keys?
You can use this if you want to get&set array values dynamically.
function getVal($data,$chain){
$level = $data;
for($i=0;$i<count($chain);$i++){
if(isset($level[$chain[$i]]))
$level = $level[$chain[$i]];
else
return null; // key does not exist, return null
}
return $level;
}
function setVal(&$data,$chain,$value){
$level = &$data;
for($i=0;$i<count($chain);$i++){
$level = &$level[$chain[$i]]; // set reference (&) in order to change the value of the object
}
$level = $value;
}
How it works:
Calling getVal($data,array('foo','bar','2017-08')) will return the equivalent of $data['foo']['bar']['2017-08'].
Calling setVal($data,array('foo','bar','2017-08'),'hello') will set value as if you called
$data['foo']['bar']['2017-08'] = 'hello'. non-existent keys will be created automatically by php magic.
This can be useful if you want to build the structure of the array dynamically.
Here's a function I wrote for setting deeply nested members on arrays or objects:
function dict_set($var, $path, $val) {
if(empty($var))
$var = is_array($var) ? array() : new stdClass();
$parts = explode('.', $path);
$ptr =& $var;
if(is_array($parts))
foreach($parts as $part) {
if('[]' == $part) {
if(is_array($ptr))
$ptr =& $ptr[];
} elseif(is_array($ptr)) {
if(!isset($ptr[$part]))
$ptr[$part] = array();
$ptr =& $ptr[$part];
} elseif(is_object($ptr)) {
if(!isset($ptr->$part))
$ptr->$part = array();
$ptr =& $ptr->$part;
}
}
$ptr = $val;
return $var;
}
Using your example data:
$array = [];
$array = dict_set($array, 'resource1.unit1.2017-10', 'value1');
$array = dict_set($array, 'resource1.unit2.2017-11', 'value2');
$array = dict_set($array, 'resource2.unit1.2017-10', 'value3');
print_r($array);
Results in output like:
Array
(
[resource1] => Array
(
[unit1] => Array
(
[2017-10] => value1
)
[unit2] => Array
(
[2017-11] => value2
)
)
[resource2] => Array
(
[unit1] => Array
(
[2017-10] => value3
)
)
)
The second argument to dict_set() is a $path string in dot-notation. You can build this using dynamic keys with period delimiters between the parts. The function works with arrays and objects.
It can also append incremental members to deeply nested array by using [] as an element of the $path. For instance: parent.child.child.[]
Would it not be easier to do the following
$calcs = array(
$meter['Resource']['name'] => array(
$meter['UnitType']['name'] => 'Some Value',
$meter['UnitType']['name2'] => 'Some Value Again'
),
);
or you can use Objects
$calcs = new stdClass();
$calcs->{$meter['UnitType']['name']} = 'Some Value';
but I would advice you build your structure in arrays and then do!
$calcs = (object)$calcs_array;
or you can loop your first array into a new array!
$new = array();
$d = date('Y-m',$start);
foreach($meter as $key => $value)
{
$new[$key]['name'][$d] = array();
}
Give it ago and see how the array structure comes out.
Try to use a switch case.
<?php
$userinput = $calcs[$meter['UnitType']['name']] = $data;;
switch ($userinput) {
case "useriput1":
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
break;
case "userinput2":
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
break;
...
default:
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
}
?>
I agree with the comment on the OP by #Jake N that perhaps using objects is a better approach. Nonetheless, if you want to use arrays, you can check for the existence of keys in a conditional, like so:
if(
array_key_exists('Resource', $meter)
) {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
} else {
$calcs[$meter['UnitType']['name']] = $data;
}
On the other hand, if you want to use objects, you can create a MeterReading object type, and then add MeterReading instances as array elements to your $calcs array, like so:
// Object defintion
class MeterReading {
private $data;
private $resource;
private $startDate;
private $unitType;
public function __construct(Array $meter, $start, $data) {
$this->unitType = $meter['UnitType']['name'];
$this->resource = $meter['Resource']['name'];
$this->startDate = date('Y-m',$start);
}
public function data() {
return $this->data;
}
public function resource() {
return $this->resource;
}
public function startDate() {
return $this->startDate;
}
public function unitType() {
return $this->unitType;
}
}
// Example population
$calcs[] = new MeterReading($meter, $start, $data);
// Example usage
foreach($calcs as $calc) {
if($calc->resource()) {
echo 'Resource: ' . $calc->resource() . '<br>';
}
echo 'Unit Type: ' . $calc->unitType() . '<br>';
echo 'Start Date: ' . $calc->startDate() . '<br>';
echo 'Data: ' . $calc->data() . '<br>';
}
Obviously you can take this further, such as checking the existence of array keys in the object constructor, giving the object property resource a default value if not provided, and so on, but this is a start to an OO approach.
You can use this library to get or set value in multidimensional array using array of keys:
Arr::getNestedElement($calcs, [
$meter['Resource']['name'],
$meter['UnitType']['name'],
date('Y-m', $start)
]);
to get value or:
Arr::handleNestedElement($calcs, [
$meter['Resource']['name'],
$meter['UnitType']['name'],
date('Y-m', $start)
], $data);
to set $data as value.

Categories