Comparing values of 2 xml files and echoing match - php

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.

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

Finding the first entry of XML and displaying it

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

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.

Parsing XML Attributes by name with PHP5

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

Better way to parse XML using PHP to create hierarchy with data table in it

I recently (2 weeks ago) started coding in PHP and today I ran into a problem and wondering if somebody can help/guide me.
I am getting xml data from a Web Service and want to render the data as show in below image
The fetched XML looks like this
<pricesheets>
<pricesheet>
<buyinggroupname>China</buyinggroupname>
<categoryname>Category B</categoryname>
<currency>USD</currency>
<discamt>39330.00</discamt>
<productdesc>Product B description</productdesc>
<prdouctId> Product B </productId>
</pricesheet>
<pricesheet>
<buyinggroupname>Asia</buyinggroupname>
<categoryname>Category A</categoryname>
<currency>USD</currency>
<discamt>39330.00</discamt>
<productdesc>Product A description</productdesc>
<prodouctId> Product A </productId>
</pricesheet>
</pricesheets>
The issue I am having is what's the best way to parse above XML so that I can render products based on 'buyinggroupname' and 'categoryname'. I can easily accomplish the collapse and expand feature once I know how to render the data.
Below is what I have done to achieve what I want. But I know for sure that my code is NOT efficient and scalable.
$xmldata; // XML return by the webservice
$data = simplexml_load_string($xmldata);
$category_A_items = '';
$category_B_items = '';
foreach ($data as $object) {
if($object->categoryname == 'Category A') { // Bad Idea : Hard coded category
$category_A_items .= '<tr><td>'.$object->prdouctId.'</td><td>'. $object->productdesc. '</td><td>'. $object->discamt. '</td></tr>';
}
elseif($object->CATEGORYNAME == 'Category B') { // Bad Idea : Hard coded category
$category_B_items .='<tr><td>'.$object->prdouctId. '</td><td>'. $object->productdesc. '</td><td>'. $object->discamt. '</td></tr>';
}
}
//Render Category A items in table
if(strlen($category_A_items) > 0) {
echo '<h3>CAD</h3>';
echo '<table><tr><th>Product Name</th><th>Description</th><th>Price</th></tr>';
echo $cadItems;
echo '</table>'. PHP_EOL;
}
//Render Category B items in table
if(strlen($category_B_items) > 0) {
echo '<h3>Breast Biopsy</h3>';
echo '<table><tr><th>Product Name</th><th>Description</th><th>Price</th></tr>';
echo $breastBiopsy;
echo '</table>'. PHP_EOL;
}
The above code only renders the data based on categories ( which are hard coded). Now what would be better way of doing the same so that I can render the data based on 'buyingroupname' and 'categoryname' without hard coding either of this two values in the php code.
Thanks in advance!
get an array of unique <categoryname>-nodes with xpath, then loop through it and select all <pricesheet>-nodes with that specific category, letting xpath do that job, again:
$xml = simplexml_load_string($x);
$cat = array_unique($xml->xpath("//categoryname"));
foreach ($cat as $c) {
echo "$c<br />";
foreach ($xml->xpath("//pricesheet[categoryname='$c']") as $p) {
echo $p->productId."<br />";
}
}
see a live-demo # http://codepad.viper-7.com/m9ruRU
Of course, you have to add code for creating tables...
Putting the strings in arrays with variable keys lets PHP keep them organized for you, and you don't have to know any category names at all. I used multidimensional arrays so that each buyingroup has its categories inside it. Then you loop through the arrays to make each table. Let me know if you need more explanation. I misunderstood your image the first time I saw it.
$xmldata; // XML return by the webservice
$data = simplexml_load_string($xmldata);
$buyinggroups = array();
foreach ($data as $object) {
if(isset($object->buyinggroupname) || isset($object->BUYINGGROUPNAME)) {
if(isset($object->buyinggroupname)) {
$name = $object->buyinggroupname;
} else {
$name = $object->BUYINGGROUPNAME;
}
}
if(isset($object->categoryname) || isset($object->CATEGORYNAME)) {
if(isset($object->categoryname)) {
$category = $object->categoryname;
} else {
$category = $object->CATEGORYNAME;
}
}
if(isset($category) && isset($name)) { //just making sure this row is OK
if(!isset($buyinggroups[$name])) {
$buyinggroups[$name] = array(); //initialize the outer array
}
if(!isset($buyinggroups[$name][$category])) {
$buyinggroups[$name][$category] = ''; //this is like your previous $category_A_items
}
$buyinggroups[$name][$category] .= '<tr><td>'.$object->productId.'</td><td>'. $object->productdesc. '</td><td>'. $object->discamt. '</td></tr>';
}
}
//Render all categories in lots of tables
//I am guessing at what HTML you want here; I don't think it's necessarily correct
echo '<table>'. PHP_EOL;
foreach($buyinggroups as $name=>$set) {
echo '<tr><th colspan="2">'.$name.'</th></tr>'. PHP_EOL;
echo '<tr><th> </th><td>';
foreach($set as $category=>$rows) {
echo '<table>';
echo '<tr><th><h3>'.$category.'</h3></th>'. PHP_EOL;
echo '<td><table><tr><th>Product Name</th><th>Description</th><th>Price</th></tr>';
echo $rows;
echo '</table>'. PHP_EOL;
}
echo '</td></tr>';
}
echo '</table>';
EDIT:
This can't possibly be beyond your ability to debug. You are getting everything you need in order to debug. PHP tells you the line number and the error. You google the error and find out what it means. Then you go to that line number and see how what's there corresponds to the thing you googled. In this case, I can tell you that "illegal offset type" means that you have an array key that is not a string or integer. On those lines in the error messages, you have the array keys $name and $category. Try var_dump($name) and var_dump($category) to find out what they actually are, or even var_dump($object) to find out how to get name and category out of the object.

Categories