When trying to parse an XML document in PHP, nothing is returned.
The XML document im trying to use:
http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml
The code I have tried:
$player = simplexml_load_file('http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml');
foreach ($player->PlayerName as $playerInfo) {
echo $playerInfo['firstName'];
}
I have also tried:
$player = simplexml_load_file('http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml');
echo "Name: " . $player->PlayerName[0]['firstName'];
What do I need to change for the attributes to show?
You might try to print_r the whole data youself and finally find what you need:
var_dump($player->Player->PlayerName->Attrib['value']->__toString())
//⇒ string(7) "Daniele"
To list all "values" (firstname, lastname,...) you need list all children and their attributes:
$xml = simplexml_load_file('http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml');
foreach ($xml as $player) {
foreach ($player->PlayerName->children() as $attrib) {
echo $attrib['name'] . ': ' . $attrib['value'] . PHP_EOL;
}
}
Output:
firstName: Daniele
lastName: Viola
commonName: Viola D.
commentaryName:
This does not work, since you are trying to access an attribute and not a node value.
You might also run into problems, because the xml is not "valid" for simple xml. See my blogpost about the issues with parsing xml with php here http://dracoblue.net/dev/gotchas-when-parsing-xml-html-with-php/
If you use my Craur ( https://github.com/DracoBlue/Craur ) library instead, it will look like this:
$xml_string = file_get_contents('http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml');
$craur = Craur::createFromXml($xml_string);
echo $craur->get('Player.PlayerName.Attrib#value'); // works since the first attrib entry is the name
If you want to be sure about the attribute (or select another one) use:
$xml_string = file_get_contents('http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml');
$craur = Craur::createFromXml($xml_string);
foreach ($craur->get('Player.PlayerName.Attrib[]') as $attribute)
{
if ($attribute->get('#name') == 'firstName')
{
echo $attribute->get('#value');
}
}
Related
I have an XML child which contains a bunch of URLs which are delimited by '|'
ie.
<ImageURL>https://example.com/example.jpg|https://example.com/example2.jpg</ImageURL>
I'm trying to write a PHP function which will take each URL and split it into it's own child element. So it should look like below:
<ImageURL>
<Image1>https://example.com/example.jpg</Image1>
<Image2>https://example.com/example2.jpg</Image2>
..etc
</ImageURL>
The data is passed into the function as $value which is the contents of that ImageURL. Not entirely sure where to start though. Any assistance would be appreciated!
function split_images($value){
...
}
It's a bit messy but it works.
<?php
$xml = '<ImageURL>https://example.com/example.jpg|https://example.com/example2.jpg</ImageURL>';
//the first `explode` removes the parent tags <ImageURL> so that we can get the child url inside as an array
$childUrl = explode('|',explode('>',$xml)[1]);
//I used `var_dump` to make sure that I only get child image `src`
var_dump($childUrl); //output = array(2) ['https://example.com/example.jpg','https://example.com/example2.jpg']
//then we created a concatinated parent tag `<imageURL>`
$imgUrl = '<ImageURL>';
foreach($childUrl as $key => $value){
//while using a foreach loop to assign the child url as '<Image></Image>' src
$imgUrl .= '<Image' . ($key + 1) . '> ' . $value . '</Image' . ($key + 1) . '>';
}
$imgUrl .= '<ImageURL>';
//I used `var_dump` again to test if I get the results you are looking for...
var_dump($imgUrl); //output = string(132) "<ImageURL><Image1>https://example.com/example.jpg</Image1><Image2> https://example.com/example2.jpg</ImageURL</Image2><ImageURL>"
As you can see, the results are in a string format, you can convert then into json or anything you are looking for.
Check it out here PHP Sandbox
If it is not what you are looking for, hopefully it will give you some general idea.
Not that difficult with DOM:
$xml = <<<'XML'
<ImageURL>https://example.com/example.jpg|https://example.com/example2.jpg</ImageURL>
XML;
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
// iterate the ImgeURL elements
foreach ($xpath->evaluate('//ImageURL') as $imageURL) {
// read the content and separate the URLs
$urls = explode('|', $imageURL->textContent);
// remove the content from the node
$imageURL->textContent = '';
// iterate the urls
foreach ($urls as $index => $url) {
// add a new Image node and set the url as content
$imageURL
->appendChild($document->createElement('Image'))
->textContent = $url;
}
}
$document->formatOutput = TRUE;
echo $document->saveXML();
Numbered (and dynamic) XML are an anti pattern. You should not do this, however you could add the $index to the element name.
I need to parse the "name" and "version" attributes from all of the "<mod>" tag entries.
Thanks to this page I was only able to parse the first "<mod>" tag from the xml.
I'm no programmer, so I have no clue how to go on.
This is my xml file.
These are my testing php files.
$xmlfile = simplexml_load_file("packa.xml");
foreach ($xmlfile->children() as $mods) {
echo $mods->mod['name']."</br>";
echo $mods->mod['version'];
}
had this output.
</br></br>Just Enough Items</br>4.15.0.293
And
foreach ($xmlfile->children() as $mods) {
echo $mods->mod['name']."
</br>".$mods->mod['version'];
}
had this output
</br>
</br>Just Enough Items
</br>4.15.0.293
You can try something along these lines, using xpath:
$result = $xmlfile->xpath('//mods//mod');
foreach($result as $mod){
$nam = $mod->xpath('.//#name');
$ver = $mod->xpath('.//#version');
echo implode ( $nam). "</br>" .implode( $ver );
echo "<br>";
}
Output:
Just Enough Items
4.15.0.293
MTLib
3.0.6
CraftTweaker2
1.12-4.1.20
Mod Tweaker
4.0.18
etc.
You are using the wrong level in the XML in the foreach() loop, this is only looping over the <mods> elements - and there is only 1. You need to specify that you want to loop over the <mod> elements and then just access the attributes as you already do...
foreach ($xmlfile->mods->mod as $mod) {
echo $mod['name'] . "/" . $mod['version'] . "</br>" . PHP_EOL;
}
I'm not so sure about the title, will try to explain in the next lines.
I have an xml file like this :
<CAR park="3" id="1" bay="0">
<SITE_ID>0</SITE_ID>
<SITE_NAME>Car Seller 1</SITE_NAME>
. . .
</CAR>
I am sucessfully iterating through my xml to get all the data.
But, I want to be able to filter by bays. I want to do something like
$xml = simplexml_load_file('myfile.xml');
$x = 1;
foreach($xml as $car) {
if($car->bay == '0'){
echo $car->SITE_ID;
$x++;
}
}
You can use XPath to fetch only the bay 0 cars...
$bay0 = $xml->xpath('//CAR[#bay="0"]');
foreach ( $bay0 as $car ) {
echo $car->SITE_ID.PHP_EOL;
}
The XPath statement is simply - any CAR element that has an attribute bay with the value 0 in it.
In case you need to access attributes in other cases, with SimpleXML - you access them as though they are array elements, so it would be $car['bay'] in the code you had above.
I am parsing through an XML document and getting the values of nested tags using asXML(). This works fine, but I would like to move this data into a MySQL database whose columns match the tags of the file. So essentially how do I get the tags that asXML() is pulling text from?
This way I can eventually do something like: INSERT INTO db.table (TheXMLTag) VALUES ('XMLTagText');
This is my code as of now:
$xml = simplexml_load_file($target_file) or die ("Error: Cannot create object");
foreach ($xml->Message->SettlementReport->SettlementData as $main ){
$value = $main->asXML();
echo '<pre>'; echo $value; echo '</pre>';
}
foreach ($xml->Message->SettlementReport->Order as $main ){
$value = $main->asXML();
echo '<pre>'; echo $value; echo '</pre>';
}
This is what my file looks like to give you an idea (So essentially how do I get the tags within [SettlementData], [0], [Fulfillment], [Item], etc. ?):
I would like to move this data into a MySQL database whose columns match the tags of the file.
Your problem is two folded.
The first part of the problem is to do the introspection on the database structure. That is, obtain all table names and obtain the column names of these. Most modern databases offer this functionality, so does MySQL. In MySQL those are the INFORMATION_SCHEMA Tables. You can query them as if those were normal database tables. I generally recommend PDO for that in PHP, mysqli is naturally doing the job perfectly as well.
The second part is parsing the XML data and mapping it's data onto the database tables (you use SimpleXMLElement for that in your question so I related to it specifically). For that you first of all need to find out how you would like to map the data from the XML onto the database. An XML file does not have a 2D structure like a relational database table, but it has a tree structure.
For example (if I read your question right) you identify Message->SettlementReport->SettlementData as the first "table". For that specific example it is easy as the <SettlementData> only has child-elements that could represent a column name (the element name) and value (the text-content). For that it is easy:
header('Content-Type: text/plain; charset=utf-8');
$table = $xml->Message->SettlementReport->SettlementData;
foreach ($table as $name => $value ) {
echo $name, ': ', $value, "\n";
}
As you can see, specifying the key assignment in the foreach clause will give you the element name with SimpleXMLElement. Alternatively, the SimpleXMLElement::getName() method does the same (just an example which does the same just with slightly different code):
header('Content-Type: text/plain; charset=utf-8');
$table = $xml->Message->SettlementReport->SettlementData;
foreach ($table as $value) {
$name = $value->getName();
echo $name, ': ', $value, "\n";
}
In this case you benefit from the fact that the Iterator provided in the foreach of the SimpleXMLElement you access via $xml->...->SettlementData traverses all child-elements.
A more generic concept would be Xpath here. So bear with me presenting you a third example which - again - does a similar output:
header('Content-Type: text/plain; charset=utf-8');
$rows = $xml->xpath('/*/Message/SettlementReport/SettlementData');
foreach ($rows as $row) {
foreach ($row as $column) {
$name = $column->getName();
$value = (string) $column;
echo $name, ': ', $value, "\n";
}
}
However, as mentioned earlier, mapping a tree-structure (N-Depth) onto a 2D-structure (a database table) might now always be that straight forward.
If you're looking what could be an outcome (there will most often be data-loss or data-duplication) a more complex PHP example is given in a previous Q&A:
How excel reads XML file?
PHP XML to dynamic table
Please note: As the matter of fact such mappings on it's own can be complex, the questions and answers inherit from that complexity. This first of all means those might not be easy to read but also - perhaps more prominently - might just not apply to your question. Those are merely to broaden your view and provide and some examples for certain scenarios.
I hope this is helpful, please provide any feedback in form of comments below. Your problem might or might not be less problematic, so this hopefully helps you to decide how/where to go on.
I tried with SimpleXML but it skips text data. However, using the Document Object Model extension works.
This returns an array where each element is an array with 2 keys: tag and text, returned in the order in which the tree is walked.
<?php
// recursive, pass by reference (spare memory ? meh...)
// can skip non tag elements (removes lots of empty elements)
function tagData(&$node, $skipNonTag=false) {
// get function name, allows to rename function without too much work
$self = __FUNCTION__;
// init
$out = array();
$innerXML = '';
// get document
$doc = $node->nodeName == '#document'
? $node
: $node->ownerDocument;
// current tag
// we use a reference to innerXML to fill it later to keep the tree order
// without ref, this would go after the loop, children would appear first
// not really important but we never know
if(!(mb_substr($node->nodeName,0,1) == '#' && $skipNonTag)) {
$out[] = array(
'tag' => $node->nodeName,
'text' => &$innerXML,
);
}
// build current innerXML and process children
// check for children
if($node->hasChildNodes()) {
// process children
foreach($node->childNodes as $child) {
// build current innerXML
$innerXML .= $doc->saveXML($child);
// repeat process with children
$out = array_merge($out, $self($child, $skipNonTag));
}
}
// return current + children
return $out;
}
$xml = new DOMDocument();
$xml->load($target_file) or die ("Error: Cannot load xml");
$tags = tagData($xml, true);
//print_r($tags);
?>
I'm struggling big time understanding how to use the DOMElement object in PHP. I found this code, but I'm not really sure it's applicable to me:
$dom = new DOMDocument();
$dom->loadHTML("index.php");
$div = $dom->getElementsByTagName('div');
foreach ($div->attributes as $attr) {
$name = $attr->nodeName;
$value = $attr->nodeValue;
echo "Attribute '$name' :: '$value'<br />";
}
Basically what I need is to search the DOM for an element with a particular id, after which point I need to extract a non-standard attribute (i.e. one that I made up and put on with JS) so I can see the value of that. The reason is I need one piece from the $_GET and one piece that is in the HTML based from a redirect. If someone could just explain how I use DOMDocument for this purpose, that would be helpful. I'm really struggling understanding what's going on and how to properly implement it, because I clearly am not doing it right.
EDIT (Where I'm at based on comment):
This is my code lines 4-26 for reference:
<div id="column_profile">
<?php
require_once($_SERVER["DOCUMENT_ROOT"] . "/peripheral/profile.php");
$searchResults = isset($_GET["s"]) ? performSearch($_GET["s"]) : "";
$dom = new DOMDocument();
$dom->load("index.php");
$divs = $dom->getElementsByTagName('div');
foreach ($divs as $div) {
foreach ($div->attributes as $attr) {
$name = $attr->nodeName;
$value = $attr->nodeValue;
echo "Attribute '$name' :: '$value'<br />";
}
}
$div = $dom->getElementById('currentLocation');
$attr = $div->getAttribute('srckey');
echo "<h1>{$attr}</a>";
?>
</div>
<div id="column_main">
Here is the error message I'm getting:
Warning: DOMDocument::load() [domdocument.load]: Extra content at the end of the document in ../public_html/index.php, line: 26 in ../public_html/index.php on line 10
Fatal error: Call to a member function getAttribute() on a non-object in ../public_html/index.php on line 21
getElementsByTagName returns you a list of elements, so first you need to loop through the elements, then through their attributes.
$divs = $dom->getElementsByTagName('div');
foreach ($divs as $div) {
foreach ($div->attributes as $attr) {
$name = $attr->nodeName;
$value = $attr->nodeValue;
echo "Attribute '$name' :: '$value'<br />";
}
}
In your case, you said you needed a specific ID. Those are supposed to be unique, so to do that, you can use (note getElementById might not work unless you call $dom->validate() first):
$div = $dom->getElementById('divID');
Then to get your attribute:
$attr = $div->getAttribute('customAttr');
EDIT: $dom->loadHTML just reads the contents of the file, it doesn't execute them. index.php won't be ran this way. You might have to do something like:
$dom->loadHTML(file_get_contents('http://localhost/index.php'))
You won't have access to the HTML if the redirect is from an external server. Let me put it this way: the DOM does not exist at the point you are trying to parse it. What you can do is pass the text to a DOM parser and then manipulate the elements that way. Or the better way would be to add it as another GET variable.
EDIT: Are you also aware that the client can change the HTML and have it pass whatever they want? (Using a tool like Firebug)