I decoded XML document to an array via a json string like this:
$xmldoc =
'<music genre="electronic">
<festival name="Berlin">
<concert status="Not Started" date="24.03.2017">
<organizers>
<person name="V. Smith" id="130171"/>
</organizers>
</concert>
</festival>
</music>';
$xml = simplexml_load_string($xmldoc);
$json = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'replace_unicode_escape_sequence',json_encode($xml,true));
$array = json_decode($json,TRUE);
However, the elements with attributes were created with another "#attributes" layer like this:
Array
(
[#attributes] => Array
(
[genre] => electronic
)
[festival] => Array
(
[#attributes] => Array
(
[name] => Berlin
)
[concert] => Array
(
[#attributes] => Array
(
[status] => Not Started
[date] => 24.03.2017
)
[organizers] => Array
(
[person] => Array
(
[#attributes] => Array
(
[name] => V. Smith
[id] => 130171
)
)
)
)
)
)
How can I remove the "#attributes" layer, so that the resulting array looks like this instead:
Array
(
[genre] => electronic
[festival] => Array
(
[name] => Berlin
[concert] => Array
(
[status] => Not Started
[date] => 24.03.2017
[organizers] => Array
(
[person] => Array
(
[name] => V. Smith
[id] => 130171
)
)
)
)
)
I tried recursively traversing the array, and I can find the "#attributes", but then I'm having difficulty moving that section one dimension up to get rid of the "#attributes" layer.
Thank you.
Try below solution:
<?php
$xmldoc =
'<music genre="electronic">
<festival name="Berlin">
<concert status="Not Started" date="24.03.2017">
<organizers>
<person name="V. Smith" id="130171"/>
</organizers>
</concert>
</festival>
</music>';
$xml = simplexml_load_string($xmldoc);
$json = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'replace_unicode_escape_sequence',json_encode($xml,true));
$array = json_decode($json,TRUE);
function removeSpecificKey(&$desired_array, $actualArray, $key_to_remove)
{
if (is_array($actualArray)) {
foreach ($actualArray as $key => $map) {
if ($key !== $key_to_remove) {
$desired_array[$key] = array();
removeSpecificKey($desired_array[$key], $actualArray[$key], $key_to_remove);
} else {
removeSpecificKey($desired_array, $actualArray[$key], $key_to_remove);
}
}
} else {
$desired_array = $actualArray;
}
}
$desired_array = array();
removeSpecificKey($desired_array, $array, '#attributes');
print_r($desired_array);
Output:
Array
(
[genre] => electronic
[festival] => Array
(
[name] => Berlin
[concert] => Array
(
[status] => Not Started
[date] => 24.03.2017
[organizers] => Array
(
[person] => Array
(
[name] => V. Smith
[id] => 130171
)
)
)
)
)
I'm going to start by saying to you what I say to everyone using more and more convoluted ways of abusing SimpleXML and json_encode - why do you need this? Take a step back, and think about what you're going to use this array for, and why you can't just use SimpleXML itself. Look at the examples in the manual for how you could.
For instance, you want to list the organizers of each festival? Then you'd loop over like this:
$xmldoc =
'<music genre="electronic">
<festival name="Berlin">
<concert status="Not Started" date="24.03.2017">
<organizers>
<person name="V. Smith" id="130171"/>
</organizers>
</concert>
</festival>
</music>';
$xml = simplexml_load_string($xmldoc);
foreach ( $xml->festival as $festival ) {
echo '<h2>', (string)$festival['name'], '</h2>';
echo '<ul>';
foreach ( $festival->concert as $concert ) {
foreach ( $concert->organizers->person as $organizer ) {
echo '<li>', (string)$organizer['name'], '</li>';
}
}
}
If you still think an array that throws away half the structure of the XML is what you want, then use SimpleXML to create it:
function turn_simplexml_into_broken_array($element) {
// Handle elements with text content
// By the way, this will break if the element has text content
// as well as children or attributes. So will the JSON hack.
// Because XML doesn't fit neatly in an array.
$text_content = trim( (string)$element );
if ( strlen($text_content) > 0 ) {
return $text_content;
}
$array = [];
foreach ( $element->attributes() as $name => $value ) {
$array[$name] = (string)$value;
}
foreach ( $element->children() as $name => $value ) {
// Handle multiple child elements with the same name
// Of course, this means the structure will be different
// for concerts with one organizer that concerts with multiple
// which leaving the object as SimpleXML would have saved you from
if ( count($element->{$name}) > 1 ) {
$array[$name][] = turn_simplexml_into_broken_array($value);
}
else {
$array[$name] = turn_simplexml_into_broken_array($value);
}
}
return $array;
}
$xml = simplexml_load_string($xmldoc);
print_r(turn_simplexml_into_broken_array($xml));
Note that we're not "removing #attributes" here. We're taking the XML attributes, and putting them into an array, along with the child elements.
Obviously, you can hack around with this to better suit the particular XML you're working with, and the structure you want. At which point, ask yourself again why you need a generic function for this at all.
Related
I have an array that can have many values at any given point, what I would like to accomplish is to combine all the array indexes and form one index with my final value. Merge other values that are the same
Say I have the array result below
Array
(
[0] => stdClass Object
(
[component] => sodium chloride
[generic_results] => Average:=99.20%
)
[1] => stdClass Object
(
[component] => sodium chloride
[generic_results] => RSD:=0.54%
)
[2] => stdClass Object
(
[component] => sodium chloride
[generic_results] => n:=3
)
)
What I would like is something like this
Array
(
[0] => stdClass Object
(
[component] => sodium chloride
[generic_results] => Average:=99.20%,RSD:=0.54%, n:=3
)
)
I have tried array unique but its not working.
Example code generating the results:
$arr=array(
(object) array(
'component'=>'sodium chloride',
'generic_results'=>'Average:=99'
),
(object) array(
'component'=>'sodium chloride',
'generic_results'=>'RSD:=0.54'
),
(object) array(
'component'=>'sodium chloride',
'generic_results'=>'n:=3'
)
);
print('<pre>');
print_r($arr);
print('</pre>');
Any Suggestions for this problem?
Try this
$new = array();
foreach ($array as $obj){
// By setting the key you guarantee it being unique
$new[$obj->component][$obj->generic_results] = $obj->generic_results;
}
$new2 = array();
foreach ($new as $comp=>$arr){
$new2['component'][$comp] = implode(',',$arr);
}
This will return an array but you can (although its not always sufficient) then use json_decode(json_encode($new2), false) to convert it to the object. Hope that helps.
You can use array_reduce, which iterates over an array to combine all elements with a given callback function:
$result = array_reduce($arr, function($result, $item) {
if ($result === null) {
// initialize with first item
return [$item];
}
// add generic_results of current item to result
$result[0]->generic_results .= ',' . $item->generic_results;
return $result;
}
);
Demo: https://3v4l.org/KBUBl
Ok, so arrays are my weakness and I'm struggling with this. It's probably dead simple too. Basically, I have an array which is
Code:
Array
(
[data] => Array
(
[0] => Array
(
[name] => getalbum
[fql_result_set] => Array
(
[0] => Array
(
[aid] => 187046464639937_101906
[name] => photo tab
[cover_pid] => 187046464639937_1661168
)
[1] => Array
(
[aid] => 187046464639937_99627
[name] => Cover Photos
[cover_pid] => 187046464639937_1661054
)
)
)
[1] => Array
(
[name] => getcover
[fql_result_set] => Array
(
[0] => Array
(
[src] => http://photos-g.ak.fbcdn.net/hphotos-ak-prn1/526499_403098366368078_187046464639937_1661168_217706037_s.jpg
)
[1] => Array
(
[src] => http://photos-b.ak.fbcdn.net/hphotos-ak-ash3/562470_403049039706344_187046464639937_1661054_361917190_s.jpg
)
)
)
)
)
I'm using a foreach to call the array by using
PHP Code:
foreach($fql_query_obj['data'] as $albums){
foreach($albums['fql_result_set'] as $album){
//print_r($album);
if ( ($album['name'] != "Wall Photos")
&& ($album['name'] != "Cover Photos")
&& ($album['name'] != "photo tab"))
{
echo $album['name'].' :: '.$album['aid'];
echo '<img src="'.$album['src'].'" /><br />';
}
}
}
Now it seems to go through the getalbum array then the getcover. How can I do it so it merges both of them together?
How do you get your data, some kind of database? Maybe you can merge it when you fetching that data?
Anyway, if your data is always ordered that way (matching arrays, one with first part of data, second with additional) how about something like that?
foreach($fql_query_obj['data'][0]['fql_result_set'] as $key_album => $album){
//print_r($album);
if ( ($album['name'] != "Wall Photos")
&& ($album['name'] != "Cover Photos")
&& ($album['name'] != "photo tab"))
{
echo $album['name'].' :: '.$album['aid'];
echo '<img src="'.$fql_query_obj['data'][1]['fql_result_set'][$key_album]['src'].'" /><br />';
}
}
It seems like you're trying to iterate the queries (getalbum and getcover). Instead, you probably want to iterate the albums and treat the cover and album data as a unified thing. I would try restructuring the data to be easier to work with:
list($albumsQuery, $coversQuery) = $fql_query_obj['data'];
$albums = array();
foreach ($albumsQuery['fql_result_set'] as $album) {
$albums[] = $album;
}
for ($i = 0, $numAlbums = count($albums); $i < $numAlbums; ++$i) {
$albums[$i] = array_merge($albums[$i], $coversQuery['fql_result_set'][$i]);
}
foreach ($albums as $album) {
// Here, $album contains data from both arrays
}
Note that this assumes there are no conflicting keys (there aren't in your example data), and there are lots of other ways to do this.
I'm trying to use a specific object type from a JSON feed, and am having a hard time specifying it. Using the code below I grab and print the specific array (max) I want,
$jsonurl = "LINK";
$json = file_get_contents($jsonurl,0,null,null);
$json_output = json_decode($json,true);
$max_output = $json_output["max"];
echo '<pre>';
print_r($max_output);
echo '</pre>';
And from the Array below, all I want to work with is the [1] objects in each array. How can I specify and get just those values?
Array
(
[0] => Array
(
[0] => 1309924800000
[1] => 28877
)
[1] => Array
(
[0] => 1310011200000
[1] => 29807
)
[2] => Array
(
[0] => 1310097600000
[1] => 33345
)
[3] => Array
(
[0] => 1310184000000
[1] => 33345
)
[4] => Array
(
[0] => 1310270400000
[1] => 33345
)
[5] => Array
(
[0] => 1310356800000
[1] => 40703
)
Well you could fetch those values with array_map:
$max_output = array_map(function($val) { return $val[1]; }, $json_output["max"]);
This requires PHP 5.3, if you use an earlier version, then you can use create_function to achieve similar results:
$max_output = array_map(create_function('$val', 'return $val[1];'), $json_output["max"]);
When you need to create new array which will contain only second values, you may use either foreach loop which will create it or use array_map() (just for fun with anonymous function available since php 5.3.0):
$newArray = array_map( function( $item){
return $item[1]
},$array);
Then you want to use last ("max" -> considering array with numeric keys) item, you can use end():
return end( $item);
And when you can process your data sequentially (eg. it's not part of some big getData() function) you can rather use foreach:
foreach( $items as $key => $val){
echo $val[1] . " is my number\n";
}
After you get $max_output...
for( $i = 0; $i < length( $max_output ); $i++ ) {
$max_output[$i] = $max_output[$i][1];
}
try this:
$ones = array();
foreach ($max_output as $r)
$ones[] = $r[1];
I want would like to get the parent node of the array below, but I don't find a way to easily do this.
Normally for getting the id, I would do this in PHP:
echo $output['posts']['11']['id'];
But how can I get to the parent node "11" when I get the value of "id" passed from a $_GET/$_POST/$_REQUEST? (ie. $output['posts']['11'][$_GET[id]])
Array
(
[posts] => Array
(
[11] => Array
(
[id] => 482
[time] => 2011-10-03 11:26:36
[subject] => Test
[body] =>
[page] => Array
(
[id] => 472
[title] => News
)
[picture] => Array
(
[0] => link/32/8/482/0/picture_2.jpg
[1] => link/32/8/482/1/picture_2.jpg
[2] => link/32/8/482/2/picture_2.jpg
[3] => link/32/8/482/3/picture_2.jpg
)
)
)
)
$parent = null;
foreach ($array['posts'] as $key => $node) {
if ($node['id'] == $_GET['id']) {
echo "found node at key $key";
$parent = $node;
break;
}
}
if (!$parent) {
echo 'no such id';
}
Or possibly:
$parent = current(array_filter($array['posts'], function ($i) { return $i['id'] == $_GET['id']; }))
How this should work exactly depends on your array structure though. If you have nested arrays you may need a function that does something like the above recursively.
array_keys($output['posts']);
will give you all keys within the posts array, see http://php.net/manual/en/function.array-keys.php
you could try with something like:
foreach ($posts as $post){
foreach( $items as $item){
if ( $item['id'] == [$_GET[id] ){
// here, the $post is referring the parent of current item
}
}
}
I don't think it is possible while an array of array is not a DOM or any tree structure. In an array you can store any reference. but you can not naturally refer to an array containing a reference.
Check rolfs example for array_filter at php manual http://www.php.net/manual/en/function.array-filter.php#100813
So you could do it like this
$filtered = array_filter($output, function ($element) use ($_GET["id"]) { return ($element["id"] == $_GET["id"]); } );
$parent = array_pop($filtered);
<root>
<gallery name="First"/>
<gallery name="Second"/>
<gallery name="Third"/>
</root>
I'm trying to rename multiple "name" attributes at once:
$rename = array();
foreach($_POST['name'] as $value) {
$rename[] = $value;
}
$objXML = new SimpleXMLElement(XML_FILE_NAME, null, true);
$gallery = $objXML->xpath('/root/gallery/#name');
print_r($gallery);
print_r($rename);
$objXML->asXML(XML_FILE_NAME);
Returns:
Array ( [0] => SimpleXMLElement Object ( [#attributes] => Array ( [name] => First ) ) [1] => SimpleXMLElement Object ( [#attributes] => Array ( [name] => Second ) ) [2] => SimpleXMLElement Object ( [#attributes] => Array ( [name] => Third ) ) )
Array ( [0] => First New [1] => Second New [2] => Third New )
How can I get php to save the New values back to the XML? Does it need another foreach loop? The code seems to be getting too complex already.
I'm trying this, but no dice:
foreach( $objXML->xpath('/root/gallery/#name') as $gallery ) {
$gallery = $_POST['name'];
}
Simplexml is buid to returns node only. That's weird, but '/root/gallery/#name' and '/root/gallery'.
These two queries
$aList = $objXML->xpath('/root/gallery/#name');
$bList = $objXML->xpath('/root/gallery');
will return the same instances
for($i=0, $count=count($aList); $i<$count; $i++) {
$a = $aList[$i];
$b = $aList[$i];
var_dump($a==$b); // true
}
So the only way for changing the attribute of a node is with the array syntaxe
foreach($aList as $node) {
$node['name'] = 'foo' . $i;
}
var_dump($objXML);