Show all items that Match in XML using PHP - php

This is my XML file named: full.xml
I need your help. I need a PHP script that open "full.xml"
and only display all values of the nodes that have .email
Example of the Output I want:
sales#company1.com
sales#company2.com
sales#company3.com
Thanks! I will thank you so much!
EDIT
$Connect = simplexml_load_file("full.xml");
return $Connect->table[0]->*.email;

The design of your XML is not very smart. With this xpath expression, you select all nodes with .email at the end of their name:
$xml = simplexml_load_string($x); // assume XML in $x
$results = $xml->xpath("//*[substring(name(),string-length(name())-" . (strlen('.email') - 1) . ") = '.email']");
--> result is an array with the selected nodes.
BTW: if you have any chance of CHANGING the structure of the XML, AVOID combining information within node names like <company1.email>, but do it like this:
...
<companies>
<company id="1">
<email>info#company1.com</email>
<tel>+498988123456</tel>
<name>somename</name>
</company>
<company id="2">
<email>info#company2.com</email>
<tel>+498988123457</tel>
<name>someothername</name>
</company>
</companies>
....
It will be much easier to read and parse.

Related

Getting a specific XML element value with PHP

I have an XML structure like this
<companies>
<company>
<vatno>12345678</vatno>
<name>
<founded>2013-12-31</founded>
<text>XYZ Inc</text>
</name>
<location>
<streetname>West Road</streetname>
<county>
<no>12345</no>
<text>East County</text>
<county>
</location>
</company>
</companies>
I am trying to get specific info from the elements into PHP variables.
To get "vatno" I use:
$vatno = $xmlObject->item($i)->getElementsByTagName('vatno')->item(0)->childNodes->item(0)->nodeValue;
But what if I need the county name for example?
I cannot use getElementsByTagName('text') as it would get the company name also using the element name "text".
You may be better off using SimpleXML, you can then access the various components in a more intuitive way.
The example above would be something like...
$data = <<< XML
<companies>
<company>
<vatno>12345678</vatno>
<name>
<founded>2013-12-31</founded>
<text>XYZ Inc</text>
</name>
<location>
<streetname>West Road</streetname>
<county>
<no>12345</no>
<text>East County</text>
</county>
</location>
</company>
</companies>
XML;
$xml = simplexml_load_string($data);
foreach ( $xml->company as $company ) {
echo $company->vatno.PHP_EOL;
echo $company->location->county->text.PHP_EOL;
}
So each sub element is accessed using ->.
If you wanted to stick with what you already had, you should be able to use...
$countyName = $xmlObject->item($i)->getElementsByTagName('text')->item(1)
->nodeValue;
Using item(1) will fetch the second instance of the <text> elements, so this assumes that the name will have this value as well.
It works with SimpleXML if I use
$xml = simplexml_load_string($data);
foreach ( $xml->companies->company as $company ) {
echo $company->vatno.PHP_EOL;
echo $company->location->county->text.PHP_EOL;
}

SimpleXML parsing through namespace items with ->children

I am parsing through the following XML file:
testxml.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?><document>
<node id="n0">
<data key="d6">
<y:GenericNode configuration="TEXT I WANT TO GET">
<y:Geometry height="56.030557066666574" width="181.68810666666667" x="638.4599149206349" y="143.24969103333325"/>
<y:Fill color="#FFCC66" color2="#FF9900" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="node_width" configuration="CroppingLabel" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="34.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="181.68810666666667" x="0.0" y="10.882466033333287">Text I want to Get<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="-0.5" labelRatioY="0.0" nodeRatioX="-0.5" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
I am interested in only a handful of attributes, namely the node id, data key which I am able to get with the code below. However, when I move into the y: namespace I get nothing.
xmlparser.php
<?php
$xml = simplexml_load_file("testxml.xml")
or die("Error: Cannot create object - check that the XML file exists and is
not corrupted"); print_r($xml);
echo $xml->node[0]['id']; // This works
echo $xml->node[0]->data[0]['key']; // This works
echo $xml->children('y', true)->GenericNode->attributes()->configuration; // Nothing
echo $xml->children('y', true)->GenericNode->NodeLabel; // Nothing
?>
I've read through previous answers on similar issues, based on which I adopted the children approach. However I can't get this to work, and I have no idea how to implement some of the other approaches such as declaring namespaces and the xpath approach.
Any help would be greatly appreciated.
That's because y:GenericNode isn't direct child of the root element, so you shouldn't be accessing it directly from $xml :
$xml->node->data->children('y', true)->GenericNode->attributes()->configuration;
quick test : https://eval.in/761412

Get all children from certain xml child element using SimpleXMLElement and xpath

I have xml like:
<root xmlns="urn:test:apis:baseComponents">
<books>
<book>
<name>50 shades of grey</name>
</book>
</books>
<disks>
<disk>
<name>Britney Spears</name>
</disk>
</disks>
</root>
And such php code:
$xml = new SimpleXMLElement($xml);
$books = $xml->books;
$disks = $xml->disks;
$disks->registerXPathNamespace('x', 'urn:test:apis:baseComponents');
$books->registerXPathNamespace('x', 'urn:test:apis:baseComponents');
$b_names = $books->xpath('//x:name');
b_names contains array with 2 values instead of 1. First holds books->book->name, second holds disks->disk->name.
Can you please explain what am I doing wrong and how could I find children of only one element?
The reason that I am using xpath instead of taking manually values using SimpleXMLElement, is that I don't know what value, which I want to search in advance.
Use $books->xpath('.//x:name') to search descendants of your $books variable and not descendants of the root node/document node (which the path //x:name does).

XML Assign More Than One Result To PHP Variable

I have done a bit of searching on this, but am just not sure I am searching for the right thing. Examples and things I have found have just confused me and possibly sent me in the wrong direction.
I am trying to figure out a php while statement, or if statement to return the results of XML output. The thing is the row/section I need may not always be the same number of results returned. For example there are ShoutCast streams, some have 1 mount point, and some have 3 mount points. Each mount point can have a different amount of listeners tuned in to that particular mount.
My Goal: To get the integer from all mount points returned in the XML, add them together to make a grand total of listeners.
The XML
<centovacast version="3.1.2" host="host.net">
<response type="success">
<message>Complete</message>
<data>
<status>
<mount>/stream</mount>
<sid>1</sid>
<listenercount>31</listenercount>
<genre>Blues</genre>
<url>http://www.websiteurl.com</url>
<title>Streams Name</title>
<currentsong>Artist Name - Track Name</currentsong>
<bitrate>128</bitrate>
<sourceconnected>1</sourceconnected>
<codec>audio/mpeg</codec>
<displayname>/stream</displayname>
<serverstate>1</serverstate>
<appstate>
<sctrans2>1</sctrans2>
</appstate>
<sourcestate>1</sourcestate>
<reseller/>
<useserver>1</useserver>
<ipaddress>11.11.111.111</ipaddress>
<port>8031</port>
<proxy>0</proxy>
<servertype>ShoutCast2</servertype>
<sourcetype>sctrans2</sourcetype>
</status>
<mountpoints>
<row>
<mount>/stream</mount>
<sid>1</sid>
<listenercount>31</listenercount>
<genre>Blues</genre>
<url>http://www.websiteurl.com</url>
<title>Stream Title Name</title>
<currentsong>Artist Name - Track Name</currentsong>
<bitrate>128</bitrate>
<sourceconnected>1</sourceconnected>
<codec>audio/mpeg</codec>
<displayname>/stream</displayname>
</row>
<row>
<mount>/live</mount>
<sid>2</sid>
<listenercount>0</listenercount>
<genre/>
<url/>
<title/>
<currentsong/>
<bitrate>0</bitrate>
<sourceconnected>0</sourceconnected>
<codec/>
<displayname>/live</displayname>
</row>
</mountpoints>
</data>
</response>
</centovacast>
So on the above I know how to pull the listeners for each mount individually using the following code.
$countlisteners->response->data->mountpoints->row[0]->listenercount;
That gives me the result for the first mount, and switching the 0 to a 1 gives me the second mount, so on and so forth.
What I need is for php that will count how many of those mounts exist, and assign each result to a variable I can then use to add together to get a grand total. Is there a way to do this?
What about doing something like this?
$countlisteners = simplexml_load_file('http://urltoxml.com');
foreach($countlisteners->response->data->mountpoints->row->listenercount as $result){
$total = $result;
echo $total;
}
You can use DOMDocument for extracting all mountpoint tags
<?php
$xml="Your xml document content here";
$dom = new DOMDocument;
$dom->loadXML($xml);
$books = $dom->getElementsByTagName('mountpoints');
foreach ($mountpoints as $mountpoints) {
echo $mountpoints->nodeValue;
//you can add your count variable here
//nodeValues can be assigned to varables
}
?>
I figured it out. So simplistic, yet hard to figure out.
$total = 0;
foreach($countlisteners->response->data->mountpoints->row as $result){
$total += $result->listenercount;
$items++;
}
echo $total;
You normally do that with Xpath. It's a query language for XML documents.
You're interested in all listenercount elements, the Xpath expression for these elements could be as simple as:
//listenercount
When you now use SimpleXML to parse the document, the following line of code gives you three SimpleXMLElements inside an array that represent the three values you want to create the sum of:
$array = simplexml_load_string($buffer)->xpath('//listenercount');
As you need the sum of the integer values of these three elements, it can be easily processed with array_map and array_sum:
$sum = array_sum(array_map('intval', $array));
And this gives you in $sum what you're looking for:
var_dump($sum); # int(62)
I hope this sheds you some light why it's often better to get the information you're looking for with an xpath query from the document instead of writing many lines of code to traverse the document "on your own".
The full example:
$buffer = <<<XML
<centovacast version="3.1.2" host="host.net">
<response type="success">
<message>Complete</message>
<data>
<status>
<mount>/stream</mount>
<sid>1</sid>
<listenercount>31</listenercount>
<genre>Blues</genre>
<url>http://www.websiteurl.com</url>
<title>Streams Name</title>
<currentsong>Artist Name - Track Name</currentsong>
<bitrate>128</bitrate>
<sourceconnected>1</sourceconnected>
<codec>audio/mpeg</codec>
<displayname>/stream</displayname>
<serverstate>1</serverstate>
<appstate>
<sctrans2>1</sctrans2>
</appstate>
<sourcestate>1</sourcestate>
<reseller/>
<useserver>1</useserver>
<ipaddress>11.11.111.111</ipaddress>
<port>8031</port>
<proxy>0</proxy>
<servertype>ShoutCast2</servertype>
<sourcetype>sctrans2</sourcetype>
</status>
<mountpoints>
<row>
<mount>/stream</mount>
<sid>1</sid>
<listenercount>31</listenercount>
<genre>Blues</genre>
<url>http://www.websiteurl.com</url>
<title>Stream Title Name</title>
<currentsong>Artist Name - Track Name</currentsong>
<bitrate>128</bitrate>
<sourceconnected>1</sourceconnected>
<codec>audio/mpeg</codec>
<displayname>/stream</displayname>
</row>
<row>
<mount>/live</mount>
<sid>2</sid>
<listenercount>0</listenercount>
<genre/>
<url/>
<title/>
<currentsong/>
<bitrate>0</bitrate>
<sourceconnected>0</sourceconnected>
<codec/>
<displayname>/live</displayname>
</row>
</mountpoints>
</data>
</response>
</centovacast>
XML;
$array = simplexml_load_string($buffer)->xpath('//listenercount');
$sum = array_sum(array_map('intval', $array));
var_dump($sum);

Hide XML declaration in files generated using PHP

I was tesing with a simple example of how to display XML in browser using PHP and found this example which works good
<?php
$xml = new DOMDocument("1.0");
$root = $xml->createElement("data");
$xml->appendChild($root);
$id = $xml->createElement("id");
$idText = $xml->createTextNode('1');
$id->appendChild($idText);
$title = $xml->createElement("title");
$titleText = $xml->createTextNode('Valid');
$title->appendChild($titleText);
$book = $xml->createElement("book");
$book->appendChild($id);
$book->appendChild($title);
$root->appendChild($book);
$xml->formatOutput = true;
echo "<xmp>". $xml->saveXML() ."</xmp>";
$xml->save("mybooks.xml") or die("Error");
?>
It produces the following output:
<?xml version="1.0"?>
<data>
<book>
<id>1</id>
<title>Valid</title>
</book>
</data>
Now I have got two questions regarding how the output should look like.
The first line in the xml file '', should not be displayed, that is it should be hidden
How can I display the TextNode in the next line. In total I am exepecting an output in this fashion
<data>
<book>
<id>1</id>
<title>
Valid
</title>
</book>
</data>
Is that possible to get the desired output, if so how can I accomplish that.
Thanks
To skip the XML declaration you can use the result of saveXML on the root node:
$xml_content = $xml->saveXML($root);
file_put_contents("mybooks.xml", $xml_content) or die("cannot save XML");
Please note that saveXML(node) has a different output from saveXML().
First question:
here is my post where all usable threads with answers are listed: How do you exclude the XML prolog from output?
Second question:
I don't know of any PHP function that outputs text nodes like that.
You could:
read xml using DomDocument and save each node as string
iterate trough nodes
detect text nodes and add new lines to xml string manually
At the end you would have the same XML with text node values in new line:
<node>
some text data
</node>

Categories