remove empty Arrays from multidimensional Array generated with simplexml - php

I have an xml file with the following structure:
<categories>
<category>
<id>3</id>
<title><![CDATA[Testname]]></title>
<text><![CDATA[]]></text>
<keywords><![CDATA[]]></keywords>
<description><![CDATA[]]></description>
</category>
</categories>
Now I'm loading this file and creating an array of it:
$xmlData = simplexml_load_file( 'categories.xml', null, LIBXML_NOCDATA);
$array = json_decode(json_encode($xmlData), true);
That generates me the following Result (print_r Output):
Array
(
[#attributes] => Array
(
[version] => 1.0
)
[categories] => Array
(
[category] => Array
(
[0] => Array
(
[id] => 3
[title] => Testname
[text] => Array
(
)
[keywords] => Array
(
)
[description] => Array
(
)
)
)
)
)
Here is my question, how could I remove those empty arrays? I tried it with array Filter, but this didn't work. (I need the keys, but they should be empty)
I know there would be a way, in my next step where I'm renaming the array keys as needed, i could check for empty arrays in the foreach loop, but I think there is an easier way, because every field (except id) could be empty in the xml file.
foreach($array['categories']['category'] as $key => $category){
$results[$key]['id'] = $category['id'];
$results[$key]['headline'] = $category['title'];
$results[$key]['content'] = $category['text'];
$results[$key]['metaKeywords'] = $category['keywords'];
$results[$key]['metaDescription'] = $category['description'];
}
Does someone has an idea, what i could do after the json_decode? Or is there an easier way for all I'm trying to accomplish here?
Thanks!

Every time I see someone use that json_decode(json_encode()) hack, it makes me sad. You don't need to turn the SimpleXML object into an array to loop over it, just read the usage examples in the manual.
If you loop over the SimpleXML object directly, you will never get those arrays, so will never need to remove them:
$xmlData = simplexml_load_file('categories.xml'); // LIBXML_NOCDATA is NOT needed
foreach($xmlData->categories->category as $key => $category){
$results[$key]['id'] = (string)$category->id;
$results[$key]['headline'] = (string)$category->title;
$results[$key]['content'] = (string)$category->text;
$results[$key]['metaKeywords'] = (string)$category->keywords;
$results[$key]['metaDescription'] = (string)$category->description;
}
The (string) tells SimpleXML you want the text content of a particular element (including CDATA), and will give you an empty string for the empty elements.

Hope this will work. PHP code demo
<?php
$result=Array
(
"categories" => Array
(
"category" => Array
(
0 => Array
(
"id" => 3,
"title" => "Testname",
"text" => Array
(
),
"keywords" => Array
(
),
"description" => Array
(
)
)
)
)
);
$results=array();
foreach($result['categories']['category'] as $key => $category){
$results[$key]['id'] = $category['id'];
$results[$key]['headline'] = $category['title'];
$results[$key]['content'] = is_array($category['text']) && count($category['text'])>0 ? $category['text'] : false;
$results[$key]['metaKeywords'] = is_array($category['keywords']) && count($category['keywords'])>0 ? $category['keywords'] : false;
$results[$key]['metaDescription'] = is_array($category['description']) && count($category['description'])>0 ? $category['description'] : false;
$results[$key]=array_filter($results[$key]);
}
print_r($results);

Related

XML parsing is not working when it's only one array

I am parsing the xml like following:
$result = '
<sms>
<status>0</status>
<message>
<contact_lists>
<contact_list><cl_id>11111</cl_id><phone>999999999</phone><name>Neu</name></contact_list>
<contact_list><cl_id>222222</cl_id><phone>888888888</phone><name>Alt</name></contact_list>
</contact_lists>
</message>
</sms>';
$xml = simplexml_load_string($result, "SimpleXMLElement", LIBXML_NOCDATA);
$json = json_encode($xml);
$array = json_decode($json,true);
$contact_lists = $array['contact_lists']['contact_list'];
A sometimes the array look like this, which is works.
Array ( [status] => 0 [message] => Array ( ) [contact_lists] => Array ( [contact_list] => Array ( [0] => Array ( [cl_id] => 11111 [phone] => 999999999 [name] => Neu ) [1] => Array ( [cl_id] => 222222 [phone] => 888888888 [name] => Alt ) ) ) )
B but sometime if the array has only one contact_list, it will look like following
Array ( [status] => 0 [message] => Array ( ) [contact_lists] => Array ( [contact_list] => Array ( [cl_id] => 11111 [phone] => 999999999 [name] => Neu ) ) )
when i use $contact_listsin foreach loop it works with A since there are multiple array keys like 0,1,2,etc... but with B it shows error Warning: Illegal string offset 'name' etc.. since there are no array key like 0,1,2,etc...
so parsing the xml is automatically removing the key numbering which causing the the issue.
1- is there a way to keep the key numbering if only one array?
2- tried to use if (count($contact_lists) >= 1) { , but its not working as well..
Any idea for a workaround to solve such issue ?
SOLUTION:
$contact_lists_found = isset($array['contact_lists']['contact_list']) ? $array['contact_lists']['contact_list'] : '';
if ($contact_lists_found !== '' ) {
if (array_key_exists('0', $contact_lists_found)) {
// more than contact list
$contact_lists = $array['contact_lists']['contact_list'];
} else {
// only one contact list
$contact_lists[0] = $array['contact_lists']['contact_list'];
}
} else {
$contact_lists = array();
}
You could just check, if the key 0 is set, and if not, then simply overwrite the element contact_list with itself wrapped into an array:
if(!isset($array['message']['contact_lists']['contact_list'][0])) {
$array['message']['contact_lists']['contact_list'] = [
$array['message']['contact_lists']['contact_list']
];
}
0 is not a valid tag name in XML, so you should never get that, unless there was more than one <contact_list> in the XML.

PHP Foreach loop with a single array to MySQL from XML

I start from xml file with following input:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<severa>
<mesaj id="6caca93f" tip="ATENTIONARE"cod="GALBEN"" />
<mesaj id="6caca93g" tip="ATENTIONARE" cod="GALBEN" />
</severa>
Using PHP with xml2Array function I obtain below array:
Array
(
[0] => Array
(
[#attributes] => Array
(
[id] => 6caca93f
[tip] => ATENTIONARE
[cod] => GALBEN
)
)
[1] => Array
(
[#attributes] => Array
(
[id] => 6caca93g
[tip] => ATENTIONARE
[cod] => GALBEN
)
)
)
I read this result with foreach and insert this 2 records into MySql.
Now the problem: this works only for multiple records in xml (>2). If I have only one record in xml the array look like below and no row is inserted. Could you please advice what I should do?
Seems that this array with a single entry have a different form. I hope this is not the reason
Thank you so much!
Array
(
[#attributes] => Array
(
[id] => 6caca93f
[tip] => ATENTIONARE
[cod] => GALBEN
)
)
Foreach is like this:
foreach ($a as $row) {
$atributes = $row['#attributes'];
$id = $atributes['id'];
$tip = $atributes['tip'];
$cod = $atributes['cod'];
mysqli_stmt_execute($st);
}
Your xml2Array function is treating a single record differently from a list of records, adding an extra dimension in the second case. You need to treat the first case specially.
if (isset($a['#attributes'])) { // Single record, turn it into an array
$a = array($a);
}
Then your loop will work for both cases.

Problems changing values in an array/object nested combination array

I don't know what to do to get this done what would like to do. I tried multiple approaches, e.g. I used array_map, array_walk, nested foreach loops with get_object_vars and I worked with json_decode/encode and so on. I always come a little bit further but never reach my goal and I would like to get some guidance from you
Basically when you see the array below, how would you proceed when you want to change some value in the path array for multiple values in the array itself?
My questions:
1) Is it right that I must convert both nested objects to an array first or is this not nesessary to do this? I mean I always get some type conversion error which tells me that I either have everything as an object or array. Is this right?
2) If this mistery is solved, which php array function is the appropriate one to change values in an array(/object)? As I have written above, I tried so many and I don't see the trees in the woods anymore. Which one do you suggest to me to use in a foreach loop?
Array
(
[0] => stdClass Object
(
[doc] => stdClass Object
(
[path] => Array
(
[0] => Bob
[1] => pictures
[2] => food
)
)
)
[1] => stdClass Object
(
[doc] => stdClass Object
(
[path] => Array
(
[0] => Alice
[1] => pictures
[2] => vacations
[3] => rome
)
)
)
)
I would suggest that,
you create an array with keys as new path and value as old path (
path to be replaced).
Loop you path array and check if it is available in above defined array.
If available replace it with key of above defined array.
For example
// array defined as point 1
$change_path_array= array('pics'=>'pictures','meal'=>'food');
// $array is your array.
foreach ($array as $value) {
// loop you path array
for($i=0;$i<count($value->doc->path);$i++){
// check if the value is in defined array
if(in_array($value->doc->path[$i],$change_path_array)){
// get the key and replace it.
$value->doc->path[$i] = array_search($value->doc->path[$i], $change_path_array);
}
}
}
Out Put: picture is replaced with pics and food with meal
Array
(
[0] => stdClass Object
(
[doc] => stdClass Object
(
[path] => Array
(
[0] => Bob
[1] => pics
[2] => meal
)
)
)
[1] => stdClass Object
(
[doc] => stdClass Object
(
[path] => Array
(
[0] => Alice
[1] => pics
[2] => vacations
[3] => rome
)
)
)
)
You can modify the code to check casesensitive.
Example of changing all pictures to photos:
$doc1 = new \stdClass;
$doc1->doc = new \stdClass;
$doc1->doc->path = array('Bob', 'pictures', 'food');
$doc2 = new \stdClass;
$doc2->doc = new \stdClass;
$doc2->doc->path = array('Alice', 'pictures', 'vacations', 'rome');
$documents = array($doc1, $doc2);
/* change all 'pictures' to 'photos' */
foreach ($documents as &$doc) {
foreach ($doc->doc->path as &$element) {
if ($element == 'pictures') {
$element = 'photos';
}
unset($element);
}
unset($doc);
}
print_r($documents);
You can do it like this:
for($i = 0; $i < count($arr); $i++){
$path_array = $arr[$i]->doc->path;
// do your modifications for [i]th path element
// in your case replace all 'Bob's with 'Joe's
$path_array = array_map(function($paths){
if($paths == 'Bob') return 'Joe';
return $paths;
}, $paths_array);
$arr[$i]->doc->path = $path_array;
}

XML to one level php array

I want to convert this xml output:
<?xml version="1.0" encoding="UTF-8"?>
<levela xmlns="https://restapi" version="1.0.0">
<levelb>
<levelc>
<var1>01</var1>
<var2>String1</var2>
</levelc>
<levelc>
<var1>02</var1>
<var2>String2</var2>
</levelc>
<levelc>
<var1>08</var1>
<var2>String3</var2>
</levelc>
</levelb>
</levela>
to this php array:
array(
'01' => 'String1',
'02' => 'String2',
'08' => 'String3'
)
I tried in many ways, but it's more difficult than I thought (for me). I hope someone can help me. Many thanks in advance!
It's an easy task with SimpleXML:
Load the XML into a SimpleXML object:
$xml = simplexml_load_string( $string );
Perform a foreach() loop through all <issuer> nodes and add <issuername> child node text value to your array using <issuerid> as key:
$result = array();
foreach( $xml->directory->issuer as $node )
{
$result[ $node->issuerid->__toString() ] = $node->issuername->__toString();
}
print_r( $result );
The output is:
Array
(
[01] => ABN Amro Bank
[02] => ASN Bank
[08] => Friesland Bank
)
SimpleXML return an object, so you need to cast as string node values with ->toString() method to add it as string.
Read more about SimpleXML
You can use the follwong little trick to first get an array.
$xml = simplexml_load_string($xmlstring);
$json = json_encode($xml);
$array = json_decode($json,TRUE);
The result of this will be an array representing exactly the XML structure, like that :
Array
(
[#attributes] => Array
(
[version] => 1.0.0
)
[directory] => Array
(
[issuer] => Array
(
[0] => Array
(
[issuerid] => 01
[issuername] => ABN Amro Bank
)
[1] => Array
(
[issuerid] => 02
[issuername] => ASN Bank
)
[2] => Array
(
[issuerid] => 08
[issuername] => Friesland Bank
)
)
)
)
With just a little bit of work, you will be able to build the array you want.

How to use a variable to access certain element within a multidimensional array?

I am using arrays in PHP to modify xml data and write it back. This is the xml structure (simplified for demonstration purpose):
<docs>
<folder>
<name>Folder name</name>
<date>20.06.2009</date>
<folder>
<name>Subfolder1</name>
<date></date>
</folder>
<folder>
<name>Subfolder1</name>
<date></date>
</folder>
<file>
<name></name>
</file>
</folder>
<name></name>
<date></date>
</docs>
Using this code, this is then parsed and transformed into a multidimensional array:
Array
(
[docs] => Array
(
[_c] => Array
(
[folder] => Array
(
[_c] => Array
(
[name] => Array
(
[_v] => Folder name
)
[date] => Array
(
[_v] => 20.06.2009
)
[folder] => Array
(
[0] => Array
(
[_c] => Array
(
[name] => Array
(
[_v] => Subfolder1
)
[date] => Array
(
[_v] =>
)
)
)
[1] => Array
(
[_c] => Array
(
[name] => Array
(
[_v] => Subfolder1
)
[date] => Array
(
[_v] =>
)
)
)
)
[file] => Array
(
[_c] => Array
(
[name] => Array
(
[_v] =>
)
)
)
)
)
[name] => Array
(
[_v] =>
)
[date] => Array
(
[_v] =>
)
)
)
)
Lengthy, I know. But now to the actual issue. If I want to add another file to a sub folder called Subfolder2 in this case, it's not a problem to do it by hand when u see the structure:
array_push($array['docs']['_c']['folder']['_c']['folder'][1], $newfile);
Now when I want to do it via the function that only knows a path to the folder (like docs/Folder name/Subfolder2), the algorithm has to analyze the array structure (check the name of each [folder], check if there is one or more folders ['_c'] or [number]) - all good, but I cannot find a way to create a variable that would have an "array" path for that new file.
I was thinking somewhere along these lines:
$writepath = "['docs']['_c']['folder']...[1]"; // path string
array_push($array{$writepath}, $newfile);
Of course this is not a valid syntax.
So, how can I make a variable that contains a path through the array elements? I did a bit of research on w3c and php.net finding no helpful info on multidimensional arrays...
If anyone has any other suggestions regarding structure, xml transformation/manipulation etc. by all means, I know it is far from sufficient way of data handling.
Thanks for any input,
Erik
Edit: Regarding the reference, is it possible to reference the reference? As that would be the way to move the 'pointer' through a set of arrays? Something as such:
$pointer = &$array['docs'];
if (key($pointer) == '_c') { $pointer = &$pointer['_c']; }
else (
// create an array with '_c' key instead of empty '_v' array
)
This syntax does not work.
Edit: The syntax works, never mind... Thanks for all your help guys!
Although this isn't exactly an answer to your question: Instead of the xml<->array code you could use SimpleXML and its XPath capabilities.
<?php
$xml = '<docs>
<folder>
<name>Folder name</name>
<date>20.06.2009</date>
<folder>
<name>Subfolder1</name>
<date></date>
</folder>
<folder>
<name>Subfolder2</name>
<date></date>
<folder>
<name>Subfolder3</name>
<date></date>
</folder>
</folder>
</folder>
</docs>';
$doc = new SimpleXMLElement($xml);
$pathToParentelement = '//folder[name="Subfolder3"]';
$element = $doc->xpath($pathToParentelement);
isset($element[0]) or die('not found');
$newFolder = $element[0]->addChild("folder");
$newFolder->name = "Subfolder4.1";
$newFolder->date = date(DATE_RFC822);
// let's see the result
echo $doc->asxml();$pathToParentelement is more or less your $writepath.
Using references might help.
You could firstly write a function that returns a reference to the given part of the array for a path string. For example, get_path_array("Documents") would return $array['docs']['_c']['folder']['_c']['folder'][1], $newfile)
<?php
function &get_path_array($path_str)
{
// your code to seek to seek the path in the array
return $result;
}
>?php
Now to add an element, you could just do
array_push(get_path_array("docs/Folder name/Subfolder2"), $newfile);
Is that what you were looking for?
(See php references for more info)
Edit: In reply to Eric's comment (a bit too hard to fit into a comment)
I think you may be confused about how arrays work. There isn't really any such thing as multidimentional arrays in php, just arrays that are storing other arrays. For example,
<?php
$a = array(array(1,2,3), array(4,5,6));
$b = $a[1];
echo $b[0];
?>
Will output "4".
Note that in the above code, $b is a copy of $a[1], changing $b won't affect $a[1].
However, using references, this can be made to work:
<?php
$a = array(array(1,2,3), array(4,5,6));
$b = &$a[1]; // the & make $b a reference to $a[1]
$b[0] = 4242;
print_r($a);
?>
Outputs:
Array
(
[0] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[1] => Array
(
[0] => 4242
[1] => 5
[2] => 6
)
)
So there is never a need to generate the string $array['doc']['path'], you just use a loop to seek the right array.

Categories