foreach shows attributes of first xml node only - php

I am trying to display attributes foreach global_ivr_variable:
$xml = '
<response method="switchvox.ivr.globalVariables.getList">
<result>
<global_ivr_variables>
<global_ivr_variable id="1" name="cid_name" value="Smith" />
<global_ivr_variable id="2" name="Q_ID_Global" value="COS" />
</global_ivr_variables>
</result>
</response>
';
$sxml = simplexml_load_string($xml);
foreach($sxml->result->global_ivr_variables->global_ivr_variable->attributes() as $a => $b)
{
echo $a .'=' . $b . "<br>";
}
All I get is the attributes of the first node:
id="1"
name="cid_name"
value="Smith"
I've also tried the following, which gives me no values at all...
foreach($sxml->result->global_ivr_variables as $xvar)
{
$a = $xvar->global_ivr_variable->id;
$b = $xvar->global_ivr_variable->name;
$c = $xvar->global_ivr_variable->value;
echo 'a='.$a.', b='.$b.', c='.$c.'<br>';
}
a=, b=, c=
Thank you all who step up to help the needy!

You almost had it but you need to iterate through each of the <global_ivr_variable> elements and then pull out the attributes:
foreach($sxml->result->global_ivr_variables->global_ivr_variable as $variable)
{
foreach($variable->attributes() as $a => $b)
{
echo $a .'=' . $b . "<br>";
}
}

If you make use of PHP variables, those problems normally easily disappear because your code is more readable.
Additionally you can access the attributes in SimpleXML via array-notation:
$variables = $sxml->result->global_ivr_variables->global_ivr_variable;
foreach ($variables as $variable)
{
printf("%s = %s\n", $variable['name'], $variable['value']);
}
This gives the following output:
cid_name = Smith
Q_ID_Global = COS
As you know upfront for which attributes you're looking for, your code is much more clear and stable by using those names.
However, if you're looking for just all attributes of the global_ivr_variable elements, then it's easier to iterate over them with XPath:
$allAttributes = $sxml->xpath('//global_ivr_variable/#*');
foreach ($allAttributes as $attribute) {
printf("%s = %s\n", $attribute->getName(), $attribute);
}
This then gives the following output:
id = 1
name = cid_name
value = Smith
id = 2
name = Q_ID_Global
value = COS
Here is the full code-example:
$xml = <<<XML
<response method="switchvox.ivr.globalVariables.getList">
<result>
<global_ivr_variables>
<global_ivr_variable id="1" name="cid_name" value="Smith" />
<global_ivr_variable id="2" name="Q_ID_Global" value="COS" />
</global_ivr_variables>
</result>
</response>
XML;
$sxml = simplexml_load_string($xml);
$variables = $sxml->result->global_ivr_variables->global_ivr_variable;
foreach ($variables as $variable)
{
printf("%s = %s\n", $variable['name'], $variable['value']);
}
echo "----\n";
$allAttributes = $sxml->xpath('//global_ivr_variable/#*');
foreach ($allAttributes as $attribute) {
printf("%s = %s\n", $attribute->getName(), $attribute);
}

Related

How to loop through two XML files and print result

I've been trying unsuccessfully with PHP to loop through two XML files and print the result to the screen. The aim is to take a country's name and output its regions/states/provinces as the case may be.
The first block of code successfully prints all the countries but the loop through both files gives me a blank screen.
The countries file is in the format:
<row>
<id>6</id>
<name>Andorra</name>
<iso2>AD</iso2>
<phone_code>376</phone_code>
</row>
And the states.xml:
<row>
<id>488</id>
<name>Andorra la Vella</name>
<country_id>6</country_id>
<country_code>AD</country_code>
<state_code>07</state_code>
</row>
so that country_id = id.
This gives a perfect list of countries:
$xml = simplexml_load_file("countries.xml");
$xml1 = simplexml_load_file("states.xml");
foreach($xml->children() as $key => $children) {
print((string)$children->name); echo "<br>";
}
This gives me a blank screen except for the HTML stuff on the page:
$xml = simplexml_load_file("countries.xml");
$xml1 = simplexml_load_file("states.xml");
$s = "Jamaica";
foreach($xml->children() as $child) {
foreach($xml1->children() as $child2){
if ($child->id == $child2->country_id && $child->name == $s) {
print((string)$child2->name);
echo "<br>";
}
}
}
Where have I gone wrong?
Thanks.
I suspect your problem is not casting the name to a string before doing your comparison. But why are you starting the second loop before checking if it's needed? You're looping through every single item in states.xml needlessly.
$countries = simplexml_load_file("countries.xml");
$states = simplexml_load_file("states.xml");
$search = "Jamaica";
foreach($countries->children() as $country) {
if ((string)$country->name !== $search) {
continue;
}
foreach($states->children() as $state) {
if ((string)$country->id === (string)$state->country_id) {
echo (string)$state->name . "<br/>";
}
}
}
Also, note that naming your variables in a descriptive manner makes it much easier to figure out what's going on with code.
You could probably get rid of the loops altogether using an XPath query to match the sibling value. I don't use SimpleXML, but here's what it would look like with DomDocument:
$search = "Jamaica";
$countries = new DomDocument();
$countries->load("countries.xml");
$xpath = new DomXPath($countries);
$country = $xpath->query("//row[name/text() = '$search']/id/text()");
$country_id = $country[0]->nodeValue;
$states = new DomDocument();
$states->load("states.xml");
$xpath = new DomXPath($states);
$states = $xpath->query("//row[country_id/text() = '$country_id']/name/text()");
foreach ($states as $state) {
echo $state->nodeValue . "<br/>";
}

How to get 2 child ID value from XML?

I wanted to try to pick up Volume value from the Level1Data node.
Here is the xml:
<Response>
<Content>
<Level1Data Tick="U" Currency="USD" TickSize="0.0001000000" TickValue="0" AssetClass="Equity" InstrumentState="Open" LastPrice="24.1550" LotSize="10"
MinPermittedPrice="0" MaxPermittedPrice="0" ClosePrice="24.0300" OpenPrice="24.1500" FirstPrice="24.1500"
HighPrice="24.7800" LowPrice="24.0000" MaxPrice="24.7800" MinPrice="24.0000" Volume="16238302"
AskSize="105597" BidSize="97618" AskPrice="24.1600" BidPrice="24.1500" Symbol="BAC.NY"
MarketTime="12:08:41.356" Message="L1DB"/>
</Content>
</Response>
And then my main script:
<?php
$result = file_get_contents("lvl1.xml");
// echo $result;
$xml = new SimpleXMLElement($result);
// $dom = new DOMDocument();
// $dom->loadXML("lvl1.xml");
// $vol = dom->getElementsByTagName('Level1Data');
$vol=$xml->children->children('Level1Data');
$id = $xml["Volume"];
echo $id;
?>
Nothing gets returned and I am having a hard time reading the php documentation and their examples.
Thank you.
You can try to find the XML node using attributes() and foreach what attribute you want as per your requirements. If you need only single attribute then discard foreach looping.
<?php
$result =<<<EOT
<Response>
<Content>
<Level1Data Tick="U" Currency="USD" TickSize="0.0001000000" TickValue="0" AssetClass="Equity" InstrumentState="Open" LastPrice="24.1550" LotSize="10"
MinPermittedPrice="0" MaxPermittedPrice="0" ClosePrice="24.0300" OpenPrice="24.1500" FirstPrice="24.1500"
HighPrice="24.7800" LowPrice="24.0000" MaxPrice="24.7800" MinPrice="24.0000" Volume="16238302"
AskSize="105597" BidSize="97618" AskPrice="24.1600" BidPrice="24.1500" Symbol="BAC.NY"
MarketTime="12:08:41.356" Message="L1DB"/>
</Content>
</Response>
EOT;
$volume = '';
$xml = new SimpleXMLElement($result);
foreach($xml->Content->Level1Data[0]->attributes() as $a => $b) {
if($a=='Volume'){
$volume = $b;
}
}
echo $volume;
?>
Demo https://eval.in/839942
OR for single attribute e.g Volume
echo $xml->Content->Level1Data[0]->attributes()->Volume;
If you want to pickup Volume only, it can also be done as follows.
<?php
$result = <<<EOM
<Response>
<Content>
<Level1Data Tick="U" Currency="USD" TickSize="0.0001000000" TickValue="0" AssetClass="Equity" InstrumentState="Open" LastPrice="24.1550" LotSize="10"
MinPermittedPrice="0" MaxPermittedPrice="0" ClosePrice="24.0300" OpenPrice="24.1500" FirstPrice="24.1500"
HighPrice="24.7800" LowPrice="24.0000" MaxPrice="24.7800" MinPrice="24.0000" Volume="16238302"
AskSize="105597" BidSize="97618" AskPrice="24.1600" BidPrice="24.1500" Symbol="BAC.NY"
MarketTime="12:08:41.356" Message="L1DB"/>
</Content>
</Response>
EOM;
$xml = new SimpleXMLElement($result);
echo $xml->Content->Level1Data[0]->attributes()->Volume;
EDIT
<?php
$result = <<<EOM
<Response>
<Content>
<Level1Data Tick="U" Currency="USD" TickSize="0.0001000000" TickValue="0" AssetClass="Equity" InstrumentState="Open" LastPrice="24.1550" LotSize="10"
MinPermittedPrice="0" MaxPermittedPrice="0" ClosePrice="24.0300" OpenPrice="24.1500" FirstPrice="24.1500"
HighPrice="24.7800" LowPrice="24.0000" MaxPrice="24.7800" MinPrice="24.0000" Volume="16238302"
AskSize="105597" BidSize="97618" AskPrice="24.1600" BidPrice="24.1500" Symbol="BAC.NY"
MarketTime="12:08:41.356" Message="L1DB"/>
</Content>
</Response>
EOM;
$xml = new SimpleXMLElement($result);
function recur($obj){
if ( in_array('Level1Data', array_keys( (array) $obj->children()) ) === false){
recur($obj->children());
}else{
var_dump($obj->children()->Level1Data);
exit;
}
}
recur($xml);

Read attributes of first and children elements with SAX XML Parser in PHP

After some research on the internet I realized that SAX XML Parser was the best option for me, since I was looking for the fastest XML Parser for large (really large) xml files.
So I was working on this codes I found in a tutorial and it works pretty nice indeed, I just couldn't figure out how to read the first and secondary element's attributes, just what's inside each element.
Here is the code:
XML
<?xml version="1.0" encoding="iso-8859-1"?>
<items>
<item id="100" name="First Element 1" />
<item id="101" name="First Element 2" />
<item id="102" name="First Element 3" />
<item id="103" name="First Element 4">
<attribute name="Second Element 4" value="508" />
</item>
<item id="104" name="First Element 5" />
<item id="105" name="First Element 6">
<attribute name="Second Element 6" value="215" />
</item>
</items>
PHP
$items = array();
$elements = null;
$item_attributes = null; //I added that myself, not sure if it's correct
// Called to this function when tags are opened
function startElements($parser, $name, $attrs) {
global $items, $elements, $item_attributes; // <-- added it here aswell
if(!empty($name)) {
if ($name == 'ITEM') {
if (!empty($attrs['ID'])) {
$item_attributes []= array(); // <-- here aswell
}
// creating an array to store information
$items []= array();
}
$elements = $name;
}
}
// Called to this function when tags are closed
function endElements($parser, $name) {
global $elements;
if(!empty($name)) {
$elements = null;
}
}
// Called on the text between the start and end of the tags
function characterData($parser, $data) {
global $items, $elements;
if(!empty($data)) {
if ($elements == 'ATTRIBUTE') {
$items[count($items)-1][$elements] = trim($data);
}
}
}
// Creates a new XML parser and returns a resource handle referencing it to be used by the other XML functions.
$parser = xml_parser_create();
xml_set_element_handler($parser, "startElements", "endElements");
xml_set_character_data_handler($parser, "characterData");
// open xml file
if (!($handle = fopen('./pages/scripts/sax.xml', "r"))) {
die("could not open XML input");
}
while($data = fread($handle, 4096)) {
xml_parse($parser, $data); // start parsing an xml document
}
xml_parser_free($parser); // deletes the parser
$i = 1;
foreach($items as $course) {
echo $i.' -';
echo ' ITEM ID: '.$course['ID'].'(?),';
echo ' NAME: '.$course['NAME'].'(?)<br/>';
echo 'ATTRIBUTE NAME: ???,';
echo ' ATTRIBUTE VALUE: ???<hr/>'; // not sure how to pull those results
$i++;
}
So I was trying to get the id and name from the tag item and the name and value from the tag attribute inside the first element item...
Any ideas?
UPDATE: Note: The course['ID'] and course['NAME'] isn't echoing anything, but when I use course['ITEM'] or course['ATTRIBUTE'] it echoes whatever is inside the tag item or attribute ex: <item> this </item>, and what I want to get is: <item THIS="this" />
I would do this a little bit different of your way, but It still goes for the same porpoise...
I know this isn't a very practical method but I think it'll do for you... You'll also be able to get the elements attribute more than once if it existed:
Note that at start_element function you can edit the attributes of both item and attribute elements in the arrays of the variables $item_attr and $field_attr.
I would also recommend you to run this code entirely so you can see what it prints and then feel free to edit it as you wish.
XML:
<?xml version="1.0" encoding="iso-8859-1"?>
<items>
<item id="100" name="First Element 1" />
<item id="101" name="First Element 2" />
<item id="102" name="First Element 3" />
<item id="103" name="First Element 4">
<attribute name="Second Element 4" value="508" />">
<attribute name="Third Element 4" value="509" />
</item>
<item id="104" name="First Element 5" />
<item id="105" name="First Element 6">
<attribute name="Second Element 6" value="215" />
</item>
</items>
PHP
<?php
$GLOBALS['currentIndex'] = 0; // identify where you are at in each element item
$GLOBALS['currentAttrIndex'] = 0; // the same but for the element attribute
$GLOBALS['currentField'] = ''; // specifies the element inside item
$GLOBALS['items'] = array(); // creates the array for the elements in items
$GLOBALS['attrs'] = array(); // creates the array for the elements attributes (in case of more than one)
$GLOBALS['items_attr'] = '';
$GLOBALS['fields_attr'] = '';
$parser = xml_parser_create();
xml_set_element_handler($parser, 'start_element', 'end_element');
xml_set_character_data_handler($parser, 'cdata');
xml_parse($parser, file_get_contents('./pages/test/sax.xml'), true);
xml_parser_free($parser);
//display results in a familiar way as a debugger...
$items = $GLOBALS['items'];
$attrs = $GLOBALS['attrs'];
$items_attr = $GLOBALS['items_attr'];
$fields_attr = $GLOBALS['fields_attr'];
$i = 1;
if (count($items) > 0) {
foreach($items as $item){
echo 'START ITEM</br>';
echo ($items_attr[$i-1]['id'] ? 'ID: '.$items_attr[$i-1]['id'].'</br>' : '');
echo ($items_attr[$i-1]['name'] ? 'NAME: '.$items_attr[$i-1]['name'].'</br>' : '');
$a = 0;
foreach ($attrs as $attr_id => $attr_name) {
if($attr_id == $i-1){
$g_i_attr_bits = explode(",", substr($attr_name, 0, -1));
foreach($g_i_attr_bits as $g_i_at_b){
$a++;
echo '  START ATTRIBUTE</br>';
echo ($fields_attr[$g_i_at_b]['name'] ? '  ; NAME: '.$fields_attr[$g_i_at_b]['name'].'</br>' : '');
echo ($fields_attr[$g_i_at_b]['value'] ? '  ; VALUE: '.$fields_attr[$g_i_at_b]['value'].'</br>' : '');
echo '  END ATTRIBUTE</br>';
}
}
}
if($a > 0){
echo 'END ITEM</br></br>';
}
$i++;
}
}
function start_element($parser, $name, $attributes){
switch($name){
case 'ITEM':
$item = array('attribute'=>'');
$GLOBALS['items'][] = $item;
$item_attr = array('id'=>''.$attributes['ID'].'','name'=>''.$attributes['NAME'].'');
$GLOBALS['items_attr'][] = $item_attr;
break;
case 'ATTRIBUTE':
$field_attr = array('name'=>''.$attributes['NAME'].'','value'=>''.$attributes['VALUE'].'');
$GLOBALS['fields_attr'][] = $field_attr;
$GLOBALS['currentField'] = 'attribute';
$attr .= $GLOBALS['currentAttrIndex'].',';
$GLOBALS['attrs'][$GLOBALS['currentIndex']] .= $attr;
break;
}
}
function end_element($parser, $name){
switch($name){
case 'ITEM':
$GLOBALS['currentIndex']++;
break;
case 'ATTRIBUTE':
$GLOBALS['currentAttrIndex']++;
break;
}
}
function cdata($parser, $data){
$currentIndex = $GLOBALS['currentIndex'];
$currentField = $GLOBALS['currentField'];
if($data != ''){
$GLOBALS['items'][$currentIndex][$currentField] = $data;
}
}
?>

PHP Array Comparison using If

I am having a hard time trying to understand why I can't compare the values of two arrays in PHP. If I echo both of these during the loop using "echo $description->ItemDesriptionName;" and "echo $item->ItemName;" the values seem to show as the same, but when I try to compare them using if, nothing works. What am I missing?
<?php
$xml=simplexml_load_file("test.xml") or die("Error: Cannot create object");
$categories = $xml->Menu->Categories;
$items = $xml->Menu->Categories->Items->ItemObject;
$itemdescription = $xml->Menu->Options->Description->DescriptionObject;
foreach($items as $item) {
echo $item->ItemName . ' - ' . $item->Price . '</br>';
foreach ($itemdescription as $description) {
if ($description->ItemDescriptionName == $item->ItemName) {
echo 'We have a match!';
//where I would echo $description->ItemDescription;
}
}
}
?>
Here is the XML file
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Menu>
<Categories>
<Name>Category 1</Name>
<Items>
<ItemObject>
<ItemName>Item 1</ItemName>
<Price>1</Price>
</ItemObject>
<ItemObject>
<ItemName>Item 2</ItemName>
<Price>3</Price>
</ItemObject>
</Items>
</Categories>
<Options>
<Description>
<DescriptionObject>
<ItemDescriptionName>Item 1</ItemDescriptionName>
<ItemDescription>A Great item</ItemDescription>
</DescriptionObject>
<DescriptionObject>
<ItemDescriptionName>Item 2</ItemDescriptionName>
<ItemDescription>A Great item as well</ItemDescription>
</DescriptionObject>
</Description>
</Options>
</Menu>
</Root>
compare as string
and you have typo in ItemDescriptioName (ItemDescriptionName)
if ( (string)$description->ItemDescriptionName == (string)$item->ItemName) {
Convert to string and then compare
<?php
$xml=simplexml_load_file("test.xml") or die("Error: Cannot create object");
$menu = $xml->Menu;
$categories = $xml->Menu->Categories;
$items = $xml->Menu->Categories->Items->ItemObject;
$itemdescription = $xml->Menu->Options->Description->DescriptionObject;
foreach($items as $item) {
$itemname = $item->ItemName;
foreach ($itemdescription as $description) {
$descriptionname = $description->ItemDescriptionName ;
echo $itemname." ---- ".$descriptionname."<br/>";
if((string)$itemname === (string)$descriptionname){
echo "Yes its matched";
}
}
}
?>
Working fine for me
The properties like $description->ItemDescriptionName are SimpleXMLElement objects. So you do not compare strings but two objects.
SimpleXMLElement objects implement the magic method __toString(). They can be cast to string automatically, but a compare between to objects will not trigger that. You can force it:
if ((string)$description->ItemDescriptionName === (string)$item->ItemName) {
...
Can you access them directly instead using an accordant index?
...
$items = $xml->Menu->Categories->Items->ItemObject;
$itemdescription = $xml->Menu->Options->Description;
$i = 0;
foreach ($items as $item) {
echo $i.' '.$item->ItemName . ' - ' . $item->Price;
echo $itemdescription->DescriptionObject[$i]->ItemDescriptionName[0];
echo ' ';
echo $itemdescription->DescriptionObject[$i]->ItemDescription[0];
echo '</br>';
$i++;
}

PHP SOAP XML DOM

I have a soap xml that contains a bunch of variables that I need to access. Here is the XML.
`<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<searchPersonsResponse xmlns="">
<searchPersonsReturn>
<attributes>
<attributes>
<name>ercreatedate</name>
<values>
<values>201104070130Z</values>
</values>
</attributes>
<attributes>
<name>status</name>
<values>
<values>Stuff1</values>
<values>Stuff2</values>
<values>Stuff3</values>
<values>Stuff4</values>
<values>Stuff5</values>
<values>Stuff6</values>
<values>Stuff7</values>
</values>
</attributes>
</attributes>
<itimDN>blah</itimDN>
<name>Smith, Bob</name>
<profileName>PER</profileName>
<select>false</select>
</searchPersonsReturn>
</searchPersonsResponse>
</soapenv:Body>
</soapenv:Envelope>
I'm trying to access the inner attribute node and pull out the Name and values into a multidimentional array like this ....
$array["status"][0]="stuff1";
$array["status"][1]="stuff2";
$array["status"][2]="stuff3";
$array["status"][3]="stuff4";
so far I have been able to access the nodes but not really get them the way I want. here is the code I have been playing around with .....
$dom_document = new DOMDocument();
$dom_document->loadXML($thexml);
$tag_els_names = $dom_document->getElementsByTagname('name');
$tag_els_values = $dom_document->getElementsByTagname('values');
$data = array();
$data2 = array();
foreach($tag_els_names as $node){
$data[] = array($node->nodeName => $node->nodeValue);
//grabs all the <name> node values
}
$i=0;$j=0;
foreach($tag_els_values as $node){
$j=0;
foreach($node->childNodes as $child) {
$data2[$i][$j] = $child->nodeValue;
//grabs all the value node values
$j++;
}
$i++;
$j=0;
}
Does anyone know an easy way to do this? I think that I have been looking at this for way to long.
How about something like:
$dom_document = new DOMDocument();
$dom_document->loadXML($thexml);
$xpath = new DOMXpath($dom_document);
$attr = $xpath->evaluate("//attribute/attributes");
$names = array();
$values = array();
$i = 0;
foreach($attr as $attr_node) {
$values[i] = array();
foreach($xpath->evaluate("name", $attr_node) as $name){
$names[] = $name->nodeValue;
}
$foreach($xpath->evaluate("value", $attr_node) as $value){
$values[i][] = $value->nodeValue;
}
i++;
}
This would, however, miss the <name> element that's outside of the <attributes> group. Did you mean to be including that?
I figured this out and thought it could help someone else
$doc = new DOMDocument();
$values=array();
if ($doc->loadXML($temp)) {
$attributes = $doc->getElementsByTagName('attributes');
foreach($attributes as $attribute) {
if($attribute->childNodes->length) {
$previous_nodeValue="";
foreach($attribute->childNodes as $i) {
if($i->nodeValue=="status"){
$previous_nodeValue=$i->nodeValue;
}
if($i->nodeName=="values" && $previous_nodeValue== "status"){
foreach($i->childNodes as $j){
$values[]=$j->nodeValue;
}
}
}
$previous_nodeValue="";
}
}
}

Categories