Finding the first entry of XML and displaying it - php

Im working on compiling a list using XML and PHP. I'm looking to find the first "destination tag" found in the RailVehicleStateClass for each train ID and echo it out. I tried doing a foreach loop to gather the destination tag but it just loops the same data for each train ID until the end of the xml file. Below is a snippet of the XML file, the full version has well over 700 entries and each train can have anywhere from 1 to 100+ railvehiclaes associated with it.
XML
<ScnLoader xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<trainList>
<TrainLoader>
<trainID>99991</trainID>
<TrainWasAI>false</TrainWasAI>
<DispatchTrainDirection>0</DispatchTrainDirection>
<ManuallyAppliedSpeedLimitMPH>2147483647</ManuallyAppliedSpeedLimitMPH>
<PreviousSignalInstruction>Clear</PreviousSignalInstruction>
<unitLoaderList>
<RailVehicleStateClass>
<destinationTag>TRC SVMS</destinationTag>
</RailVehicleStateClass>
<RailVehicleStateClass>
<destinationTag>PRC</destinationTag>
</RailVehicleStateClass>
</unitLoaderList>
</TrainLoader>
</trainList>
</ScnLoader>
PHP
<?php
$trains = simplexml_load_file("Auto%20Save%20World.xml") or die("Error: Cannot create object");
$totalUnitCount=0;
echo "<table>";
echo "<th>#</th>";
echo "<th>Train ID</th>";
echo "<th>Symbol</th>";
echo "<th>Direction</th>";
echo "<th>AI</th>";
foreach ($trains->xpath("//TrainLoader") as $train) {
$totalUnitCount = $totalUnitCount + 1;
foreach (array_slice($trains->xpath("//RailVehicleStateClass"),0,1) as $unit){
echo $unit->destinationTag;
}
echo "</td>";
echo "<td>";
echo $train->DispatchTrainDirection;
echo "</td>";
echo "<td>";
echo $train->TrainWasAI;
echo "</td>";
}
?>

Your xpath query to get the RailVehicleStateClass elements needs to be made relative to the current $train. You can do that using:
$train->xpath(".//RailVehicleStateClass")
Note the use of . in front of // to make the path relative to the current $train element.

First you need to use the foreach in the unitLoaderList, then you the xpath in the object you create from the foreach and add a . before the // in the query to only obtain what is inside the object.
I really don't understand what you want to accomplish with HTML table, but here is an example code that work with your file and multiple trains without HTML.
$trains = simplexml_load_file("train.xml") or die("Error: Cannot create object");
$totalUnitCount=0;
foreach ($trains->xpath("//trainList") as $train) {
$totalUnitCount = $totalUnitCount + 1;
foreach ($train->xpath(".//unitLoaderList") as $unit){
foreach ($unit->xpath(".//RailVehicleStateClass") as $railV){
echo $railV->destinationTag[0]; //Assuming all RailVehicle has at list one destination tags if not you has to another foreach with xpath of destinationTag and only get the first one
}
}
}

Related

How do I get this php foreach to parse through all of the <mod> entries?

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;
}

Extracting XML Data with Foreach Loops, Results Inconsistent

I am extracting XML data using DOMDocument and foreach loops. I am pulling certain attributes and node values from the XML document and creating variables with that data. I am then echoing the variables.
I have successfully completed this for the first portion of the XML data that lives between the <VehicleDescription tags. However, using the same logic with data within the <style> tags, I have been having issues. Specially, the created variables won't echo unless they are in the foreach loop. See the code below for clarification.
My php:
<?php
$vehiclexml = $_POST['vehiclexml'];
$xml = file_get_contents($vehiclexml);
$dom = new DOMDocument();
$dom->loadXML($xml);
//This foreach loop works perfectly, the variables echo below:
foreach ($dom->getElementsByTagName('VehicleDescription') as $vehicleDescription){
$year = $vehicleDescription->getAttribute('modelYear');
$make = $vehicleDescription->getAttribute('MakeName');
$model = $vehicleDescription->getAttribute('ModelName');
$trim = $vehicleDescription->getAttribute('StyleName');
$id = $vehicleDescription->getAttribute('id');
$BodyType = $vehicleDescription->getAttribute('altBodyType');
$drivetrain = $vehicleDescription->getAttribute('drivetrain');
}
//This foreach loop works; however, the variables don't echo below, the will only echo within the loop tags. How can I resolve this?
foreach ($dom->getElementsByTagName('style') as $style){
$displacement = $style->getElementsByTagName('displacement')->item(0)->nodeValue;
}
echo "<b>Year:</b> ".$year;
echo "<br>";
echo "<b>Make:</b> ".$make;
echo "<br>";
echo "<b>Model:</b> ".$model;
echo "<br>";
echo "<b>Trim:</b> ".$trim;
echo "<br>";
echo "<b>Drivetrain:</b> ".$drivetrain;
echo "<br>";
//Displacement will not echo
echo "<b>Displacement:</b> ".$displacement;
?>
Here is the XML file it is pulling from:
<VehicleDescription country="US" language="en" modelYear="2019" MakeName="Toyota" ModelName="RAV4" StyleName="LE" id="1111" altBodyType="SUV" drivetrain="AWD">
<style modelYear="2019" name="Toyota RAV4 LE" passDoors="4">
<make>Toyota</make>
<model>RAV4</model>
<style>LE</style>
<drivetrain>AWD</drivetrain>
<displacement>2.5 liter</displacement>
<cylinders>4-cylinder</cylinders>
<gears>8-speed</gears>
<transtype>automatic</transtype>
<horsepower>203</horsepower>
<torque>184</torque>
</style>
</VehicleDescription>
Any help or insight as to why variables from the first foreach loop echo but variables from the second don't would be greatly appreciated.
Thanks!
Just to post an alternative solution to the way you've fixed this.
As you've, there are a couple of <stlye> tags, this means that the foreach will attempt to use all style tags. But as you know that you are after the contents of the first tag only, you can drop the foreach loop and use the item() method...
$displacement = $dom->getElementsByTagName('style')->item(0)
->getElementsByTagName('displacement')->item(0)->nodeValue;
This also applies to how you fetch the data from the <VehicleDescription> tag. Drop the foreach and use
$vehicleDescription = $dom->getElementsByTagName('VehicleDescription')->item(0);
The error was within the XML document.
Within the <style> tags was another set of <style> tags. Changing the name of the second set solved this issue.

Comparing values of 2 xml files and echoing match

I'm trying to use 2 xml files to put data on to a list. One Xml file is not editable by me the 2nd is a reference database of sorts that I created. Both files have a common string for <rvXMLfilename> and if both xml file strings match I want to echo out a string from XML file 2. I attmepted to write this in my php script but I could not get it to work.
$railunit = simplexml_load_file('railUnitList.xml');
$orders = simplexml_load_file('train.xml');
foreach ($orders->xpath("//RailVehicleStateClass") as $traininfo):
$rvXMLfilename=$traininfo->rvXMLfilename;
$unitType=$traininfo->unitType;
$unitNumber=$traininfo->unitNumber;
$destinationTag=$traininfo->destinationTag;
$loadWeightUSTons=$traininfo->loadWeightUSTons;
echo "<tr>";
echo "<td>";
if($railunit->rvXMLfilename == $orders->rvXMLfilename){
echo $railunit->unitType;
}else{
echo "error!";
}
XML File 1
<?xml version="1.0"?>
<ScnLoader xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<trainList>
<TrainLoader>
<unitLoaderList>
<RailVehicleStateClass>
<rvXMLfilename>R8_SD40T-2_UP03.xml</rvXMLfilename>
<unitType>US_DieselEngine</unitType>
<loadWeightUSTons>14.2985783</loadWeightUSTons>
<destinationTag>MDTLA</destinationTag>
<unitNumber>8626</unitNumber>
</RailVehicleStateClass>
<RailVehicleStateClass>
<rvXMLfilename>R8_SD40T-2_UP01.xml</rvXMLfilename>
<unitType>US_DieselEngine</unitType>
<loadWeightUSTons>14.2985792</loadWeightUSTons>
<destinationTag>None</destinationTag>
<unitNumber>4401</unitNumber>
</RailVehicleStateClass>
<RailVehicleStateClass>
<rvXMLfilename>R8_SD40T-2_UP01.xml</rvXMLfilename>
<unitType>US_DieselEngine</unitType>
<loadWeightUSTons>14.2985783</loadWeightUSTons>
<destinationTag>None</destinationTag>
<unitNumber>4454</unitNumber>
</RailVehicleStateClass>
</unitLoaderList>
</TrainLoader>
</trainList>
</ScnLoader>
XML File 2
<railUnitList>
<railUnit>
<rvXMLfilename>R8_SD40T-2_UP01.xml</rvXMLfilename>
<unitType>SD40-2T Locomotive</unitType>
<reportingMark>UP</reportingMark>
<axelCount>6</axelCount>
<unitWeight></unitWeight>
<unitLength>71</unitLength>
</railUnit>
<railUnit>
<rvXMLfilename>R8_CoveredHopper_ACF2970_BN01.xml</rvXMLfilename>
<unitType>Covered Hopper</unitType>
<reportingMark>BN</reportingMark>
<axelCount>4</axelCount>
<unitWeight></unitWeight>
<unitLength>60</unitLength>
</railUnit>
<railUnit>
<rvXMLfilename>R8_C14Hopper_POTASH01.xml</rvXMLfilename>
<unitType>Covered Hopper</unitType>
<reportingMark>POTX</reportingMark>
<axelCount>4</axelCount>
<unitWeight></unitWeight>
<unitLength>60</unitLength>
</railUnit>
<railUnit>
<rvXMLfilename>Run8_Tank107BN01.xml</rvXMLfilename>
<unitType>Tank</unitType>
<reportingMark>BN</reportingMark>
<axelCount>4</axelCount>
<unitWeight></unitWeight>
<unitLength>59</unitLength>
</railUnit>
</railUnitList>
There are a couple of issues with your code. Firstly you cannot directly refer to the value of a SimpleXMLElement as e.g. $rvXMLfilename=$traininfo->rvXMLfilename; you need to cast it to the appropriate type. Secondly rvXMLfilename is not a child of $railunit. I think you probably want to iterate through the values in your $railunit, comparing each of them with the values you extract from the $orders XML. This code should achieve what you want:
foreach ($orders->xpath("//RailVehicleStateClass") as $traininfo) {
$rvXMLfilename=(string)$traininfo->rvXMLfilename;
$unitType=(string)$traininfo->unitType;
$unitNumber=(int)$traininfo->unitNumber;
$destinationTag=(string)$traininfo->destinationTag;
$loadWeightUSTons=(float)$traininfo->loadWeightUSTons;
echo "<tr>";
echo "<td>";
$message = "error!";
foreach ($railunit->railUnit as $ru) {
if((string)$ru->rvXMLfilename == $rvXMLfilename){
$message = (string)$ru->unitType;
}
}
echo $message;
}
Output (for your sample data)
<tr><td>error!<tr><td>SD40-2T Locomotive<tr><td>SD40-2T Locomotive
Again use the XPath query to get the values you're looking for. Stick all the filenames into an array, for easier comparison later.
<?php
$railunit = simplexml_load_file('railUnitList.xml');
$orders = simplexml_load_file('train.xml');
foreach ($railunit->railUnit as $railunit) {
$key = (string)$railunit->unitType;
$value = (string)$railunit->rvXMLfilename;
$filenames[$key] = $value;
}
foreach ($orders->xpath("//RailVehicleStateClass") as $traininfo) {
$rvXMLfilename=$traininfo->rvXMLfilename;
$unitType=$traininfo->unitType;
$unitNumber=$traininfo->unitNumber;
$destinationTag=$traininfo->destinationTag;
$loadWeightUSTons=$traininfo->loadWeightUSTons;
echo "<tr>";
echo "<td>";
if(in_array($traininfo->rvXMLfilename, $filenames)){
echo array_search($traininfo->rvXMLfilename, $filenames);
} else {
echo "error!";
}
}
Note also your use of $orders->rvXMLfilename in the original code. The PHP structure is the same as the XML structure, you can't reference an element in that fashion unless it's the direct child of the root element.

PHP Iterate Multiple Children from XML

I'm trying to read an XML file in PHP which has multiple same-named nodes inside a parent. So far, I can read the first iteration of the XML node but then PHP moves to the next parent even though there's another child to be read. In this case, it's the <DEVICE> node and the child node of it which is <IPV4>.
Here's the XML (pass1.xml)
<LIST>
<COMPANY>Alpha
<DEVICE>Alpha1
<IPV4>1.2.3.4</IPV4>
</DEVICE>
<DEVICE>Alpha2
<IPV4>5.6.7.8</IPV4>
</DEVICE>
</COMPANY>
<COMPANY>Bravo
<DEVICE>Bravo1
<IPV4>1.3.5.7</IPV4>
</DEVICE>
</COMPANY>
</LIST>
As you can see, there are two devices listed under the "Alpha" company and one under the "Bravo" company. Here's my PHP (I've omitted the HTML tags):
$xml = simplexml_load_file("/var/www/xml/pass1.xml") or die ("Could not open file!");
// Get Companies
echo "Company List<br>";
foreach($xml->children() as $company) {
echo $company . "<br>";
}
echo "<br>Device List<br>";
// Get devices
foreach($xml->children() as $device) {
echo $device->DEVICE . "<br>";
}
echo "<br>IP List<br>";
// Get IP
foreach($xml->children() as $ipv4) {
echo $ipv4->DEVICE->IPV4 . "<br>";
}
The "Companies" are pulled properly but for the "Devices" I only get Alpha1 and Bravo1. Similarly, for the IP addresses I only get 1.2.3.4 and 1.3.5.7 which correspond with the devices which get pulled. I'm missing any information on Alpha2.
My ultimate goal is to have each Device and IPv4 iterated over from PHP and displayed. Can someone point me in the right direction?
I'm sure there's a better way to do this, but this code explains why yours wasn't working correctly.
// Get Companies
echo "Company List<br>";
foreach($xml->children() as $company) {
echo $company . "<br>";
}
echo "<br>Device List<br>";
// Get devices
foreach($xml->children() as $company) {
foreach ($company->children() as $device) {
echo $device . "<br>";
}
}
echo "<br>IP List<br>";
// Get IP
foreach($xml->children() as $company) {
foreach ($company->children() as $device) {
foreach ($device->children() as $IPV4) {
echo $IPV4 . "<br>";
}
}
}
The problem with your code is that in each loop you're trying to pull different information, but your code is actually only pulling the first child repeatedly.
So here
// Get devices
foreach($xml->children() as $device) {
echo $device->DEVICE . "<br>";
}
You're attempting to get each DEVICE from the XML file, but in actuality you're pulling all of the FIRST children from $xml which are COMPANY tags, and the two FIRST COMPANY tags are Alpha and Bravo. and then with EACH of those two company tags you're echoing a SINGLE DEVICE tag.
So you're pulling a SINGLE DEVICE tag from Alpha and a SINGLE DEVICE tag from Bravo. So the solution was to add another layer to your foreach, where after it loops through the two COMPANY tags, it then loops through the next layer of children of the COMPANY tags, which are the DEVICE tags.
Then for the IPV4 you do the same, but add another loop layer.

Getting name spaces out of XML from simplexml_load_file in php

I am trying to parse this YouTube XML using simplexml_load_file in php.
The XML feed can be found here:
https://www.youtube.com/feeds/videos.xml?playlist_id=PL1mm1FfX5EHRjGyoBpEXBRIGAmCNt8pBT
Below in php I am trying to iterate through the media groups nested inside each entry node.
<?php
$xmlFeed=simplexml_load_file('https://www.youtube.com/feeds/videos.xml?playlist_id=PL1mm1FfX5EHRjGyoBpEXBRIGAmCNt8pBT')
or die("Cannot load YouTube video feed, please try again later.");
foreach ($xmlFeed->entry->children('media', true)->group as $video) {
echo $video->title;
echo $video->description;
echo $video->thumbnail->getNameSpaces(true);
}
?>
Title and description print just fine. But I'm trying to get at the thumbnail URL found in this namespace:
<media:thumbnail url="https://i1.ytimg.com/vi/HEYQXVGnwXc/hqdefault.jpg" width="480" height="360"/>
I've tried all 3 of the following:
echo $video->thumbnail->getNameSpaces(true);
echo $video->thumbnail->getNameSpaces(true)['url'];
echo $video->thumbnail->getNameSpaces(true)->url;
None return the url. The first returns Array and the last two are blank. What am I missing?
Several things: first, you have to use the attributes() function since there is no child of thumbnail. Secondly, you don't need to declare getNameSpaces(true) since the namespace prefix media is done in the for loop. Finally, you do not iterate across all media:group. Right now, you will return only the first set of xml values, not both from each <entry> node. Therefore, you need to add an outer loop -one that iterates across the frequency of <entry> nodes.
$attr = 'url';
for($i = 0; $i < sizeof($xmlFeed->entry); $i++) {
foreach ($xmlFeed->entry[$i]->children('media', true)->group as $video) {
echo $video->title."\n";
echo $video->description."\n";
echo $video->thumbnail->attributes()->$attr."\n";
}
}
XPATH Alternative
Even further, you could have handled your needs in XPath by simply registering the media namespace and querying to exact locations, iterating of course across each set:
$xmlFeed->registerXPathNamespace('media', 'http://search.yahoo.com/mrss/');
// ARRAYS TO HOLD XML VALUES
$videos = $xmlFeed->xpath('//media:group');
$title = $xmlFeed->xpath('//media:group/media:title');
$description = $xmlFeed->xpath('//media:group/media:description');
$url = $xmlFeed->xpath('//media:group/media:thumbnail/#url');
// ITERATING THROUGH EACH ARRAY
for($i = 0; $i < sizeof($videos); $i++) {
echo $title[$i]."\n";
echo $description[$i]."\n";
echo $url[$i]."\n";
}

Categories