Parsing multi nested child in XML in PHP - php

Firstly first I am a beginner with PHP. Especially when i have to deal with XML Parsing , I am having a struggle with it right now, lots of xml. Hours and hours already I tried solving it and its already giving me a headache.
Here the XML :
<v1:product>
<v1:data>
<v1:account_id>5637</v1:account_id>
<v1:account_name>John Doe</v1:account_name>
<v1:product_id>f4dc8300-1f13-11e8-bfa43d4d9ee60f6b</v1:product_id>
<v1:product_name>Product Test</v1:product_name>
<v1:product_desc>
<v1:name>Arc</v1:name>
<v1:unit>boxes</v1:unit>
<v1:value>10</v1:value>
</v1:product_desc>
<v1:product_desc>
<v1:name>Birg</v1:name>
<v1:unit>kilos</v1:unit>
<v1:value>2</v1:value>
</v1:product_desc>
<v1:product_desc>
<v1:name>Cyitha</v1:name>
<v1:unit>Minutes</v1:unit>
<v1:value>30</v1:value>
</v1:product_desc>
<v1:offer>
<v1:offer_id>3575374</v1:offer_id>
<v1:offer_name>Flash</v1:offer_name>
</v1:offer>
</v1:data>
<v1:data>
<v1:account_id>5892</v1:account_id>
<v1:account_name>John Doe</v1:account_name>
<v1:product_id>jsad2sdx-asd2-983j</v1:product_id>
<v1:product_name>Product Test 2</v1:product_name>
<v1:product_desc>
<v1:name>Arc</v1:name>
<v1:unit>boxes</v1:unit>
<v1:value>2</v1:value>
</v1:product_desc>
<v1:product_desc>
<v1:name>Birg</v1:name>
<v1:unit>kilos</v1:unit>
<v1:value>10</v1:value>
</v1:product_desc>
<v1:product_desc>
<v1:name>Cyitha</v1:name>
<v1:unit>Minutes</v1:unit>
<v1:value>99</v1:value>
</v1:product_desc>
<v1:offer>
<v1:offer_id>3575374</v1:offer_id>
<v1:offer_name>Flash</v1:offer_name>
</v1:offer>
</v1:data>
</v1:product>
and this is what i have in PHP
$objGetEmAll = new DOMDocument();
$objGetEmAll->loadXML($theXML);
$datas = $objGetEmAll->getElementsByTagName('data');
$responseValue = array();
foreach($datas as $data) //each element of DATA
{
$dataValue = array();
if($data->childNodes->length)
{
foreach($data->childNodes as $i)
{
$dataValue[$i->nodeName] = $i->nodeValue;
}
}
$responseValue[] = $dataValue;
}
but still fail when i want to extract value dynamically inside product_desc tag. I want to change XML above into an array which is like below
Array
(
[0] => Array
(
[v1:account_id] => 5637
[v1:account_name] => adsfafds
[v1:product_id] => 124asd
[v1:product_name] => HALO
[v1:product_desc] => Array
(
[0] => Array
(
[v1:name] => A
[v1:unit] => BOXes
[v1:value] => 7
)
[1] => Array
(
[v1:name] => B
[v1:unit] => mins
[v1:value] => 1000
)
[2] => Array
(
[v1:name] => C
[v1:unit] => call
[v1:value] => 700
)
[3] => Array
(
[v1:name] => D
[v1:unit] => GB
[v1:value] => 4
)
)
[v1:offer] => Array
(
[v1:offer_id] => 3575374
[v1:offer_name] => Flash
)
)
)
I know maybe this is the easy one, but for me who just joined programming in these last month, this is confusing. Links or helps are welcome

Use this
$objGetEmAll = new DOMDocument();
$objGetEmAll->loadXML($theXML);
$datas = $objGetEmAll->getElementsByTagName('data');
$responseValue = array();
foreach($datas as $data) //each element of DATA
{
$dataValue = array();
$product_desc_counter = 0;
$offer_counter = 0;
if($data->childNodes->length)
{
foreach($data->childNodes as $i) {
// for v1:product_desc group case :1
if($i->nodeName == 'v1:product_desc' ){
foreach($i->childNodes as $p) {
$dataValue[$i->nodeName][ $product_desc_counter][$p->nodeName] = $p->nodeValue;
}
$product_desc_counter ++;
} else if($i->nodeName == 'v1:offer' ) // for offer group case:2
{
foreach($i->childNodes as $p) {
$dataValue[$i->nodeName][ $offer_counter][$p->nodeName] = $p->nodeValue;
}
$offer_counter ++;
}
else // case:3
$dataValue[$i->nodeName] = $i->nodeValue;
}
}
$responseValue[] = $dataValue;
}
In the above lines we loop over all data nodes then check if it has any nodes if it doesn't( like for items account_name, product_id ..) we simply store it's nodeValue in our result array ie. in $responseValue. If it has any child (v1:product_desc,v1:offer ie case:1 or 2) we again loop over its children and append them to combination of corresponding key and the corresponding loop counter ( $product_desc_counter or $offer_counter ) .It might be hard at first to understand (specially array indexes) but after fiddling with it for a while you will grasp it.

Related

Array return, enumerat or other solution?

I'm having trouble identifying each row of this array ... would it be possible to enumerate or otherwise be able to pull a specific array field?
My code is:
<?php
/*
Generic function to fetch all input tags (name and value) on a page
Useful when writing automatic login bots/scrapers
*/
function get_input_tags($html)
{
$post_data = array();
// a new dom object
$dom = new DomDocument;
//load the html into the object
$dom->loadHTML($html);
//discard white space
$dom->preserveWhiteSpace = false;
//all input tags as a list
$input_tags = $dom->getElementsByTagName('input');
//get all rows from the table
for ($i = 0; $i < $input_tags->length; $i++)
{
if( is_object($input_tags->item($i)) )
{
$name = $value = '';
$name_o = $input_tags->item($i)->attributes->getNamedItem('name');
if(is_object($name_o))
{
$name = $name_o->value;
$value_o = $input_tags->item($i)->attributes->getNamedItem('value');
if(is_object($value_o))
{
$value = $input_tags->item($i)->attributes->getNamedItem('value')->value;
}
$post_data[$name] = $value;
}
}
}
return $post_data;
}
/*
Usage
*/
error_reporting(~E_WARNING);
$html = file_get_contents("https://secure.donman.net.au/client/ozchild/Donate.aspx");
echo "<pre>";
print_r(get_input_tags($html));
echo "</pre>";
?>
Generate the result
[radInMemDiscloseAmount] => No
[txtInMemOfAddress] =>
[txtInMemOfSuburb] =>
[txtInMemOfState] =>
[txtInMemOfPostCode] =>
[txtInHonorOfName] =>
[txtInHonorOfAddress] =>
[txtInHonorOfSuburb] =>
[txtInHonorOfState] =>
[txtInHonorOfPostCode] =>
[HonorEventType] => gf_other_choice
[HonorEventTypeOtherText] => Other
how can I have something like this?
[0][radInMemDiscloseAmount] => No
[1][txtInMemOfAddress] =>
[2][txtInMemOfSuburb] =>
[3][txtInMemOfState] =>
[4][txtInMemOfPostCode] =>
[5][txtInHonorOfName] =>
[6][txtInHonorOfAddress] =>
[7][txtInHonorOfSuburb] =>
[8][txtInHonorOfState] =>
[9][txtInHonorOfPostCode] =>
[10][HonorEventType] => gf_other_choice
[11][HonorEventTypeOtherText] => Other
how can I have something like this? or some way to identify array rows?
I'm having trouble identifying each row of this array ... would it be possible to enumerate or otherwise be able to pull a specific array field?

How to append value in array with keys without getting overriding

i'm getting result in a foreach loop now i want to form a new array with keys and form that array with my new data. Now when i try to assign data it gets override to the previous one as it's not getting new index. how can i achive that so far i have done that:
foreach ($result as $key) {
$pickup_location = $key->locationid;
if (isset($pickup_location)) {
$pickup_location = $this->db->get_where('locations', array('id' => $pickup_location ))->row();
if (!empty($pickup_location)) {
$supplier_dashboard['pickup_location_name'] = $pickup_location->name_en;
}
}
$dropoff_location = $key->location_dropoff;
if (isset($dropoff_location)) {
$dropoff_location = $this->db->get_where('locations', array('id' => $dropoff_location ))->row();
if (!empty($dropoff_location)) {
$supplier_dashboard['dropoff_location_name'] = $dropoff_location->name_en;
}
}
$car_make = $key->car_id;
if (isset($car_make)) {
$car_details = $this->db->get_where('chauffeur_rates', array('chauffeur_id' => $car_make))->row();
if (!empty($car_details)) {
$supplier_dashboard['car_make'] = $car_details->chauffeur_make;
}
}
}
return $supplier_dashboard;
}
and my resulting array is:
Array
(
[pickup_location_name] => Seoul Downtown
[dropoff_location_name] => Disneyland Paris
[car_make] => makecar
)
however i have atleast 7 location names and car makes instead of getting added as a new array it overrides the previous one, i should have get the result as
Array
[0](
[pickup_location_name] => Seoul Downtown
[dropoff_location_name] => Disneyland Paris
[car_make] => makecar
)
Array
[1](
[pickup_location_name] => Seoul 1
[dropoff_location_name] => Disneyland 1
[car_make] => makecar
)
Array
[2](
[pickup_location_name] => Seoul 2
[dropoff_location_name] => Disneyland 2
[car_make] => makecar
)
... upto 7
Every time you looping the key of the array is always the same that's why you are getting overide the results. You need to put a key that is unique. You can try this
$i = 0; //First key
foreach ($result as $key){
$supplier_dashboard[$i]['pickup_location_name'] = $pickup_location->name_en;
$i++; //add +1 to the key so the next element not overrides
}
You have to add $key to array in which your record get added
use
$supplier_dashboard[$key]['pickup_location_name'] = //your code
instead of
$supplier_dashboard['pickup_location_name'] = //your code
Instead of adding the items the your array, you are directly setting the value in the top most collection.
Instad of
$myobject['foo'] = $value;
You need to do (where $iteration is the current position in your loop)
$myobject[$iteration]['foo'] = $value;
Try the following :
$carItemNumber = 0;
foreach ($result as $key) {
$pickup_location = $key->locationid;
if (isset($pickup_location)) {
$pickup_location = $this->db->get_where('locations', array('id' => $pickup_location ))->row();
if (!empty($pickup_location)) {
$supplier_dashboard[$carItemNumber]['pickup_location_name'] = $pickup_location->name_en;
}
}
$dropoff_location = $key->location_dropoff;
if (isset($dropoff_location)) {
$dropoff_location = $this->db->get_where('locations', array('id' => $dropoff_location ))->row();
if (!empty($dropoff_location)) {
$supplier_dashboard[$carItemNumber]['dropoff_location_name'] = $dropoff_location->name_en;
}
}
$car_make = $key->car_id;
if (isset($car_make)) {
$car_details = $this->db->get_where('chauffeur_rates', array('chauffeur_id' => $car_make))->row();
if (!empty($car_details)) {
$supplier_dashboard[$carItemNumber]['car_make'] = $car_details->chauffeur_make;
}
}
$carItemNumber++;
}
return $supplier_dashboard;

Php array and json

help me to convert the following array in to json.
I tried to convert the array.
Array
(
[0] => Array
(
[c_code] => 200001
[itemname] => 303 10CAP
[c_pack_code] => PK0075
[c_web_img_link] =>
)
[1] => Array
(
[c_code] => 200005
[itemname] => 3P 4TAB
[c_pack_code] =>
[c_web_img_link] =>
)
)
current result for the following code is
public function searchOrder($idx, $data) {
if (!empty($data)) {
$result = OrderbukModel::func_get_searchlist($idx,$data);
if (!empty($result)) {
$resultArray[] = $result;
print_r(json_encode($result));
} else {
$resultArray[$idx] = ["Mysql returns empty result !"];
print_r(json_encode($resultArray));
exit;
}
}
}
now i got the result is like
[{"c_code":"200001","itemname":"303 10CAP","c_pack_code":"PK0075","c_web_img_link":""},{"c_code":"200005","itemname":"3P 4TAB","c_pack_code":"","c_web_img_link":""}]
But I need the result as follows
[{"c_code":"2000001","c_code":"200005"},
{"itemname":"303 10CAP","itemname":"3P 4TAB"},
{"c_pack_code":"PK0075","c_pack_code":""},
{"c_web_img_link":"","c_web_img_link":""}]
Example of how you can you make the json from array. Collect the data in two different array and after loop marge them and store the result in another array after that encode them.
Note: Your desired JSON is not a valid format, you can't use same index
for two data.
Online Example: https://3v4l.org/kdPDI
$arr = array(
array(
'c_code' => '200001',
'itemname' => '303 10CAP',
'c_pack_code' => 'PK0075',
'c_web_img_link' => ''
),
array(
'c_code' => '200005',
'itemname' => '3P 4TAB',
'c_pack_code' => '',
'c_web_img_link' => ''
)
);
$res1 = array();
$res2 = array();
foreach($arr as $val){
$res1['c_code'][] = $val['c_code'];
$res1['itemname'][] = $val['itemname'];
$res2['c_pack_code'][] = $val['c_pack_code'];
$res2['c_web_img_link'][] = $val['c_web_img_link'];
}
$out = array(array_merge($res1, $res2));
echo json_encode($out);

Getting XML attribute from the inner node using PHP SimpleXML is failing

Chaps,
I am trying to get attributes of the 'file' node in the following XML with SimpleXml but every time is gives me back null. I have successfully got the attributes for the study node but fail to get the 'file' atts .
here is the xml :
<?xml version="1.0" encoding="UTF-8"?>
<studies>
<study uid="1.3.12.2" acc="181">
<date>20051218</date>
<time>2156</time>
<ref>CG</ref>
<desc>Abdomen</desc>
<id></id>
<path>S00001</path>
<modality>CR</modality>
<reports>
<file cat="UNK" date="20141124">Card_Cloud.txt</file>
</reports>
</study>
and here is my code :
$studyXML = new SimpleXMLElement($studyXML);
$studyXML_array = array();
foreach ($studyXML ->study as $study)
{
// Getting uid and accession from XML attributes
$uid = (!empty($study)) ? (String)$study->attributes()->uid : '';
$acc = (!empty($study)) ? (String)$study->attributes()->acc : '';
// Getting the reports and putting them in an array
$reports = array ();
foreach($study->reports as $rep)
{
$cat = (String)$rep->attributes()->cat;
$reports[] = (String)$rep->file;
}
// Constructing the xml as an array
$studyXML_array[] = array
(
'uid' => $uid,
'acc' => $acc,
'date' => (String)$study->date,
'reports' => $reports
);
}
I can get "uid" and "acc" but I can't get "cat" and "date" inside file node.
When I look at the array of the xml in my debug variables I can see uid and acc attributes but no sign of cat and date attributes.
I Would really appreciate any help.
I prefer to stick to SimpleXML as all my code is using that so far.
Cheers
This is how I did it based on Ghost answer.
$reports = array ();
foreach($study->reports->file as $rep)
{
$temp = array();
$temp['repName'] = (String)$rep;
// Getting cat and date attributes of the report
foreach($rep->attributes() as $key => $rep)
{
$temp[$key] = (string) $rep;
}
$reports[] = $temp;
}
If you're trying to use print_r()/var_dump() to check, then it will do no justice. But it is there, try to traverse to it along with using ->attributes() inside the foreach as well:
$studyXML = new SimpleXMLElement($studyXML);
$studyXML_array = array();
foreach ($studyXML ->study as $study)
{
// Getting uid and accession from XML attributes
$uid = (!empty($study)) ? (String)$study->attributes()->uid : '';
$acc = (!empty($study)) ? (String)$study->attributes()->acc : '';
// Getting the reports and putting them in an array
$reports = array();
// loop each attribute
foreach($study->reports->file->attributes() as $key => $rep)
{
$reports[$key] = (string) $rep;
}
// Constructing the xml as an array
$studyXML_array[] = array(
'uid' => $uid,
'acc' => $acc,
'date' => (String) $study->date,
'reports' => $reports
);
}
echo '<pre>';
print_r($studyXML_array);
Sample Output:
Array
(
[0] => Array
(
[uid] => 1.3.12.2
[acc] => 181
[date] => 20051218
[reports] => Array
(
[cat] => UNK
[date] => 20141124
)
)
)

Create array of hierarchical directories in PHP

I have the following code (I know that this code is not optimized but it's not for discussion):
function select_categories($cat_id)
{
$this->db = ORM::factory('category')
->where('parent', '=', $cat_id)
->find_all();
foreach ($this->db as $num => $category)
{
if($category->parent == 0)
{
$this->tmp[$category->parent][$category->id] = array();
}
else {
$this->tmp[$category->parent][$category->id] = array();
}
$this->select_categories($category->id);
}
return $this->tmp;
}
Function returns this array:
array(3) (
0 => array(2) (
1 => array(0)
2 => array(0)
)
2 => array(1) (
3 => array(0)
)
3 => array(2) (
4 => array(0)
5 => array(0)
)
)
But how should I change the code
else {
$this->tmp[$category->parent][$category->id] = array();
// ^^^^^^^^^^^^^^^^^^^^^^ (this bit)
}
To merge array[3] to array[2][3] for example (because array[3] is a subdirectory of array[2] and array[2] is a subdirectory of array[0][2]), so, I need to make this (when I don't know the level of subdirectories):
array (
0 => array (
1 => array
2 => array (
3 => array (
4 => array
5 => array
)
)
)
)
A long time ago I wrote some code to do this in PHP. It takes a list of entities (in your case, categories) and returns a structure where those entities are arranged in a tree. However, it uses associative arrays instead of objects; it assumes that the “parent” ID is stored in one of the associative array entries. I’m sure that you can adapt this to your needs.
function make_tree_structure ($nontree, $parent_field)
{
$parent_to_children = array();
$root_elements = array();
foreach ($nontree as $id => $elem) {
if (array_key_exists ($elem[$parent_field], $nontree))
$parent_to_children [ $elem[$parent_field] ][] = $id;
else
$root_elements[] = $id;
}
$result = array();
while (count ($root_elements)) {
$id = array_shift ($root_elements);
$result [ $id ] = make_tree_structure_recurse ($id, $parent_to_children, $nontree);
}
return $result;
}
function make_tree_structure_recurse ($id, &$parent_to_children, &$nontree)
{
$ret = $nontree [ $id ];
if (array_key_exists ($id, $parent_to_children)) {
$list_of_children = $parent_to_children [ $id ];
unset ($parent_to_children[$id]);
while (count ($list_of_children)) {
$child = array_shift ($list_of_children);
$ret['children'][$child] = make_tree_structure_recurse ($child, $parent_to_children, $nontree);
}
}
return $ret;
}
To see what this does, first try running it on a structure like this:
var $data = array (
0 => array('Name' => 'Kenny'),
1 => array('Name' => 'Lilo', 'Parent' => 0),
2 => array('Name' => 'Adrian', 'Parent' => 1)
3 => array('Name' => 'Mark', 'Parent' => 1)
);
var $tree = make_tree_structure($data, 'Parent');
If I’m not mistaken, you should get something like this out: (the “Parent” key would still be there, but I’m leaving it out for clarity)
array (
0 => array('Name' => 'Kenny', 'children' => array (
1 => array('Name' => 'Lilo', 'children' => array (
2 => array('Name' => 'Adrian')
3 => array('Name' => 'Mark')
)
)
)
Examine the code to see how it does this. Once you understand how this works, you can tweak it to work with your particular data.
Assuming you dont want any data/children tags in your array:
foreach ($this->db as $num => $category)
{
// save the data to the array
$this->tmp[$category->id] = array();
// save a reference to this item in the parent array
$this->tmp[$category->parent][$category->id] = &$this->tmp[$category->id];
$this->select_categories($category->id);
}
// the tree is at index $cat_id
return $this->tmp[$cat_id];
If you just need to retrieve the full tree out of the database, you can even simplify your query (get all records at once) and remove the recursive call in this function. You will need an extra check that will only set the $this->tmp[$catagory->id] when it does not exist and else it should merge the data with the existing data.

Categories