XML to JSON conversion in PHP SimpleXML - php

$data = "<QRYRESULT>
<ISSUCCESS>Y</ISSUCCESS>
<EBLCUSTOMER ACCOUNTNO='11111'>
<CUSTACCTNO>121212</CUSTACCTNO>
<ACCTSTATUS>active</ACCTSTATUS>
<CCYDESC>BDT</CCYDESC>
<BALANCE>9999</BALANCE>
<AVAILABLEBALANCE>99</AVAILABLEBALANCE>
<CUSTOMERNAME>cus_name</CUSTOMERNAME>
<AMOUNTONHOLD>1000</AMOUNTONHOLD>
<ODLIMIT>99</ODLIMIT>
</EBLCUSTOMER>
</QRYRESULT>";
this is the XML string I am trying to convert. I have used the folloung code.
$result = str_replace(array("\n", "\r", "\t"), '', $data);
$xml = simplexml_load_string($result);
$object = new stdclass();
$object->webservice[] = $xml;
$result = json_encode($object);
header('content-Type: application/json');
echo $result;
And I am getting the following json data.
{
"webservice": [
{
"ISSUCCESS": "Y",
"CUSTSUMMARY": {
"#attributes": {
"ACCOUNT": "11111"
},
"IDACCOUNT": "1010101",
"CODACCTCURR": "BDT",
"NUMBALANCE": "99999",
"ACCTDESC": "22222",
"PRDNAME": "name"
}
}
]
}
But i don't want the "#attributes". I want the output like below:
{
"QRYRESULT": {
"ISSUCCESS": "Y",
"EBLCUSTOMER": {
"-ACCOUNTNO": "11111",
"CUSTACCTNO": "121212",
"ACCTSTATUS": "active",
"CCYDESC": "BDT",
"BALANCE": "9999",
"AVAILABLEBALANCE": "99",
"CUSTOMERNAME": "cus_name",
"AMOUNTONHOLD": "1000",
"ODLIMIT": "99"
}
}
}
How can I do that ?

You don't want to have the "#attributes" field encoded in the JSON, however this is the standard way how PHP JSON serializes a SimpleXMLElement.
As you say you want to change that, you need to change the way how PHP JSON serializes the object. This is possible by implementing JsonSerializable with a SimpleXMLElement on your own and then provide the JSON serialization as you wish:
class JsonSerializer extends SimpleXmlElement implements JsonSerializable
{
/**
* SimpleXMLElement JSON serialization
*
* #return null|string
*
* #link http://php.net/JsonSerializable.jsonSerialize
* #see JsonSerializable::jsonSerialize
*/
function jsonSerialize()
{
// jishan's SimpleXMLElement JSON serialization ...
return $serialized;
}
}
E.g. by using the attributes as fields like all the child elements.
You can then just integrate it easily, e.g. instead of
$xml = simplexml_load_string($result);
you can use
$xml = simplexml_load_string($result, 'JsonSerializer');
or just
$xml = new JsonSerializer($result);
and the rest of your function works the same but just with your wishes serialization.
Example:
$result = str_replace(array("\n", "\r", "\t"), '', $data);
$xml = new JsonSerializer($result);
$object = new stdclass();
$object->webservice[] = $xml;
$result = json_encode($object, JSON_PRETTY_PRINT);
header('content-Type: application/json');
echo $result;
Output:
{
"webservice": [
{
"EBLCUSTOMER": {
"ACCOUNTNO": "11111",
"CUSTACCTNO": "121212",
"ACCTSTATUS": "active",
"CCYDESC": "BDT",
"BALANCE": "9999",
"AVAILABLEBALANCE": "99",
"CUSTOMERNAME": "cus_name",
"AMOUNTONHOLD": "1000",
"ODLIMIT": "99"
}
}
]
}
The serialization function for the example above is:
function jsonSerialize()
{
// text node (or mixed node represented as text or self closing tag)
if (!count($this)) {
return $this[0] == $this
? trim($this) : null ;
}
// process all child elements and their attributes
foreach ($this as $tag => $element) {
// attributes first
foreach ($element->attributes() as $name => $value) {
$array[$tag][$name] = $value;
}
// child elements second
foreach($element as $name => $value) {
$array[$tag][$name] = $value;
}
}
return $array;
}
Some notes here:
In the serialization you have to take care of the type of element your own. The differentiation is done on top for the single elements with no children. If you need attribute handling on these, you need to add it.
The trim($this) perhaps already spares you the issue you try to catch with $result = str_replace(array("\n", "\r", "\t"), '', $data);. SimpleXMLElement in any case would JSON serialize "\r" characters (SimpleXMLElement makes use of "\n" for breaks). Additionally you might be interested in the rules of whitespace normalization in XML.
In case an attribute has the same name as a child element, it will be overwritten by the child element.
In case a child element that follows another child element with the same name, it will be overwritten.
The two last points are just to keep the example code simple. A way that is aligned to standard PHP JSON serialization of a SimpleXMLElement is given in a series of blog posts of mine.
Basics of exactly this procedure and an exemplary JsonSerialize implementation is available in the third post: SimpleXML and JSON Encode in PHP – Part III and End.
Another related question is:
PHP convert XML to JSON group when there is one child

$fileContents= file_get_contents("https://www.feedforall.com/sample.xml");
$fileContents = str_replace(array("\n", "\r", "\t"), '', $fileContents);
$fileContents = trim(str_replace('"', "'", $fileContents));
$simpleXml = simplexml_load_string($fileContents);
$json = json_encode($simpleXml);
$array = json_decode($json,TRUE); // convert the JSON-encoded string to a PHP variable
return $array;
I'ts Better example:

Related

merge Array of objects into array with unique object

I have a array of various object, but I need turn this objects into unique object. Below I share my code.
$result = [];
$idiomas = Idioma::all()->toArray();
foreach ($idiomas as $lang) {
$result[] = [
$lang['palavra_chave'] => $lang[$id]
];
}
return response()->json($result);
reponse
[
{ "INICIAL": "Inicial"},{ "RELATORIOS": "Relatórios"},{ "FUNCIONARIO": "Funcionário"},{ "DATA": "Data"},{ "ANEXAR_IMAGEM": "Anexar imagem"},{ "DISCIPLINA": "Disciplina"}
]
But I need transform this objects into one, like this
[
{
"INICIAL": "Inicial",
"RELATORIOS": "Relatórios",
"FUNCIONARIO": "Funcionário",
"DATA": "Data",
"ANEXAR_IMAGEM": "Anexar imagem",
"DISCIPLINA": "Disciplina"
}
]
anyone can help me?
$idiomas = Idioma::all()->toArray();
if (count($idiomas)) {
//$result = new stdClass; # wouldn't work without base namespace
$result = new \stdClass;
foreach ($idiomas as $lang) {
$result->{$lang['palavra_chave']} = $lang[$id];
}
return response()->json([$result]);
}
// otherwise
Edit: #Tpojka's answer definitely looks more appropriate. Use the following one only if you can't change the way you retrieve data initially (I'm not familiar enough with Laravel).
The following should work:
// Take your initial JSON
$input = <<<JSON
[
{ "INICIAL": "Inicial"},{ "RELATORIOS": "Relatórios"},{ "FUNCIONARIO": "Funcionário"},{ "DATA": "Data"},{ "ANEXAR_IMAGEM": "Anexar imagem"},{ "DISCIPLINA": "Disciplina"}
]
JSON;
// Transform it into a PHP array
$input_as_array = json_decode($input);
// Reduce it into an associative array
$associative_array = array_reduce($input_as_array, function($associative_array, $item) {
return $associative_array += (array)$item;
}, []);
// Encode it back into JSON, as an array
$result = json_encode([$associative_array], JSON_PRETTY_PRINT);

PHP - Append new JSON object to nested array within JSON object

Setup
I have retrieved a JSON string from a URL using 'file_get_contents' and have subsequently decoded it using `json_decode'
$search_URL="http://abcdefg.com"
$json = file_get_contents($search_URL);
$obj = json_decode($json, true);
The JSON represents an Apache Solr search response:
{
"responseHeader":{
"status":0,
"QTime":3,
"params":{
"q":"zzzzzzz",
"wt":"json"
}
},
"response":{
"numFound":20690,
"start":0,
"docs":[
{
"title":"yyyyy-y-y-yyyyy-yy-y",
"author":"author 1, author 2, author 3",
"url":"https://yyyyy.com",
"id":yyyy,
"_version_":yyyyy
},
{
"title":"xxxx-x-x-xxxx-xxxx",
"author":"author 1, author 2",
"url":"https://xxxxx.com",
"id":xxxxx,
"_version_":xxxxxx
},
]
}
}
I basically need to grab each author field in the docs[] array, split it by commas to get a new array
$author_array = explode(", ", $author);
I then need to call another web service with these author names that will return JSON. I would love to simply plug this JSON in to my existing JSON and send it all back
Question
I need to modify my JSON so that each doc contains this new JSON object. How can I do this?
{
"title":"xxxx-x-x-xxxx-xxxx",
"author":"author 1, author 2",
"url":"https://xxxxx.com",
"id":xxxxx,
"_version_":xxxxxx,
"authors":[
{
"name": "author1",
"id": "143"
},
{
"name": "author2",
"id": "72"
}
]
}
My Attempt
I have tried a simpler version of this where I simply have an array of author names
{
"title":"xxxx-x-x-xxxx-xxxx",
"author":"author 1, author 2",
"url":"https://xxxxx.com",
"id":xxxxx,
"_version_":xxxxxx,
"authors":[
"author1",
"author2"
]
}
I tried the following:
$response = $obj['response'];
$docs = $response['docs'];
foreach($docs as &$doc) {
$author = $doc['author'];
$authors_array = explode(" ,", $author);
$doc['authors'] = $authors_array;
}
This has ultimately failed as the new array is not appended.
Explode the author string, call the API on each element, make an array of all these results, and put it back into the object.
I use a reference below in foreach so it can modify the document element directly.
foreach ($obj['response']['docs'] AS &$doc) {
$author_names = explode(',', $doc['author']);
$author_array = array_map('author_api', $author_names);
$doc['authors'] = $author_array;
}

changing content of multidimensional array in php?

I have a multidimensional array in php, which I want to manipulate according to some rules.
Input-Array:
(Variable printed as JSON)
[
{
"meta":{
"title": "Adressdata",
"name": "adress"
},
"data":{
"desc":{ "tooltip": "text",
"maxlength": "100"
},
"line1":{ "tooltip": "Recipient",
"maxlength": "40"
}
}
]
Rules:
{
"0>meta>title": "Companyaddress",
"0>data>desc": {
"tooltip": "Another Text",
"maxLength": "150"
}
}
(There are 2 rules, the index explains which field from the input-array has to be changed, and the value contains the data to be inserted instead. Note that the second rule wants to insert an object)
Problem:
I want to change the content of the input-array according to the rules, but I struggle in doing that.
Here's the php-Code I already have:
<?php
$input = json_decode(file_get_contents("inputData.json"),true);
$rules = json_decode(file_get_contents("rules.json"),true);
foreach ($rules as $target => $replacement) {
//rule: "0>meta>title": "newTitle"
$node = $input;
foreach (explode(">",$target) as $index) {
$node = $node[$index];
}
$node = $replacement; //replace doesn't work
}
echo "Result-Array: ";
print_r($input);
?>
Everything works, in except that I can't change the values.
Why? Because everytime I set $node-Variable, I create a new Variable.
And I only change the content of $node, but not the content of $input.
So I tried to use references - which doesn't work either:
When I change the 2 $node-Lines to this code:
$node = &$input;
[...]
$node = &$node[$keys[$i]]; //go to child-node
But this doesn't work, because the second line, where I just want to navigate to a child-node, changes the parent-element (because $node is a reference).
I have no idea if there's a trick to do that, I hope someone can help me
Okay, here's some decision, but you should change $rules structure, it should be the same as $input:
$input = json_decode('[{"meta":{"title": "Adressdata","name": "adress"},"data":{"desc":{"tooltip":"text","maxlength":"100"},"line1":{"tooltip":"Recipient","maxlength":"40"}}}]',true);
$rules = json_decode('[{"meta":{"title": "Companyaddress"},"data":{"desc":{"tooltip":"Another Text","maxlength":"150"}}}]',true);
// if you print both arrays you will see that they have the similar structure
// but $rules consists only of items that you need to change.
// next:
echo'<pre>',print_r(array_replace_recursive($input, $rules)),'</pre>';
// BINGO!
Ok, after some frustrating hours, I finally found a way to achieve this.
This is the function I wrote:
/**
* Manipulates an array according to the given rules
*/
function manipulateData($input, $manipulations){
foreach ($manipulations as $target => $replacement) { //for each rule
$tmpRefNum = 0; //Variable-Nameprefix for the reference (don't generate random names)
$node = "node".(++$tmpRefNum); //"node1"
$$node = &$input; //$node1 --> $input
foreach (explode(">",$target) as $index) { //for each child in the rule
$parent = &$$node; //We search for a child. So the old node becomes a parent
if(!isset($parent[$index])){ //Does the child exist? no --> create missing child
$parent[$index] = ""; //give birth! (value will be set later)
}
$node = "node".(++$tmpRefNum); //Generate a new reference-Name: "node2", "node3", ...
$$node = &$parent[$index]; //Point to child
}
$$node = $replacement; //Change the child-content
//Unset all generated references (just to not spam the variables)
/* */ for($i=1;$i<=$tmpRefNum;$i++){
/* */ $node = "node".$i;
/* */ unset($$node);
/* */ }
/* */
}
return $input;
}
Description:
The solution is, that I just generate a new variable/reference for each child-node I find. In order to do this, I need the $$-Syntax provided by php.
It's a bit dirty, because I have a variable-factory, so if someone finds a better solution it would be awesome.
It would be great, if I would get some feedback if this is a good solution or not.

Issue with JSON

I am trying to have PHP read an XML file and then convert it to JSON to use in some classes that I wrote. The problem I am having is it will not loop thru all XML nodes.
For some reason it will only return one node and not both. My guess is maybe my JSON object is not formatted correctly. I have been messing with this for about 2 days, ugh! I am new to this so go easy on me ;)
tracker.xml
<?xml version="1.0" encoding="UTF-8"?>
<tracker>
<student>
<id>0425655</id>
<lname>Doe</lname>
<fname>John</fname>
</student>
<student>
<id>0123456</id>
<lname>Smith</lname>
<fname>Jane</fname>
</student>
</tracker>
xml.php
class xml
{
private $path;
public function __construct($path)
{
$this->path = $path;
}
public function xmlParse()
{
$xml = simplexml_load_file($this->path);
return json_encode($xml->children());
}
}
json.php
class json
{
private $xmlArray;
public function __construct($xmlArray)
{
$this->xmlArray = $xmlArray;
}
public function getJSON()
{
$json = json_decode($this->xmlArray);
foreach($json->student as $v)
{
return 'ID: '.$v->id.'Last: '.$v->lname.'First: '.$v->fname;
}
}
}
I know I can pass true as a second parameter to json_decode(), but I wanted to work with objects.
Here's the output for the json_decode() (after passing it through getJSON for formatting):
{
"student": [
{
"id": "0425655",
"lname": "Doe",
"fname": "John"
},
{
"id": "0123456",
"lname": "Smith",
"fname": "Jane"
}
]
}
return immediately, well, returns from the current function. You want echo for debugging, as in
foreach($json->student as $v)
{
echo 'ID: '.$v->id.'Last: '.$v->lname.'First: '.$v->fname;
}
If you want to return the result, either just return the JSON object or parse it into an array or string.
That JSON string looks right to me, problem lies with the return statement - as you're looping through an array, use echo instead.
Regarding your JSON
I reformatted your output. As you can see, there are two nodes under "student". Perhaps you missed the [ and ] characters.
Format your JSON next time to get a better idea of what's going on. :)
Regarding your function
You also might have missed it because your debug output is broken:
foreach($json->student as $v)
{
return 'ID: '.$v->id.'Last: '.$v->lname.'First: '.$v->fname;
}
You return after the first iteration.
Try:
$output = '';
foreach ($json->student as $v) {
$output .= "ID: {$v->id} Last: {$v->lname} First: {$v->fname}\n";
}
return $output;
To be honest, though, I'd expect a function named getJSON() to return, well... JSON. Not some author-written string. Your classes and functions are poorly named overall.
Perhaps your function should look like this:
public function getJSON()
{
$json = json_decode($this->xmlArray);
// some debug output for development
foreach ($json->student as $v) {
echo "ID: {$v->id} Last: {$v->lname} First: {$v->fname}\n";
}
return $json;
}

json_encode arrays with iso-8895 characters

I have a quite complex, even though not really big, array, with many levels of nesting.
The array contains values that are encoded in ISO-8895, and also objects, with the same issue.
If I just
json_encode($array)
PHP wil silently encode all the values contining ISO-8895 characters as null.
Looking at the PHP documentation, I managed to write a working solution:
function fixMultibyteSerializedObject($match)
{
return 's:' . mb_strlen($match[2]);
}
/**
* Useful to json-encode arrays of objects with ISO-8895 encoded values.
* Does not work with iso-encoded keys
* #param var $object array or object to be encoded
* #param int $options json_encode options
*/
function isoJsonEncode($object, $options = null)
{
$str = serialize($object);
$str = mb_convert_encoding($str, 'utf-8');
$str = preg_replace_callback(
'!(?<=^|;)s:(\d+)(?=:"(.*?)";(?:}|a:|s:|b:|d:|i:|o:|N;))!s',
'fixMultibyteSerializedObject',
$str);
$object = unserialize($str);
return json_encode($object, $options);
}
Apart from getting a better library, such as the Zend json encoding component, can you suggest a better solution?
Thank you,
Iacopo
What about something like this?
array_walk_recursive($array, function (&$elem) {
if (is_string($elem)) {
$elem = iconv('ISO-8895', 'UTF-8', $elem);
}
});
echo json_encode($array);

Categories