Retrieving data from siblings to node that match - php

I'm iterating through a xml doc with SimpleXML. I have an array ($ids) with id's and I'm checking whether there's a match or not in the XML (Worksheet/Table/Row/Cell/Data). If it's a match I want to be able to get to the data from the following two siblings, but I can't figure out how.
from the php:
// $ids <---- array('8', '53', '38')
foreach ($thePositions->Worksheet->Table->Row as $row) {
if($row->Cell->Data == true) {
for ($i = 0; $i < count($ids); $i++) {
foreach($row->Cell->Data as $data) {
if ($data == $ids[$i]) {
echo 'match!';
/*
Tried $siblings = $data->xpath('preceding-sibling::* | following-sibling::*');
but doesn't seem to work in this case.
*/
}
}
}
}
}
the xml:
<?xml version="1.0"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<LastAuthor>Herpa Derp </LastAuthor>
<Created>2012-09-25T13:44:01Z</Created>
<LastSaved>2012-09-25T13:48:24Z</LastSaved>
<Version>14.0</Version>
</DocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
<AllowPNG/>
</OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>14060</WindowHeight>
<WindowWidth>25040</WindowWidth>
<WindowTopX>25540</WindowTopX>
<WindowTopY>4100</WindowTopY>
<Date1904/>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s62">
<Font ss:FontName="Courier" ss:Color="#000000"/>
</Style>
</Styles>
<Worksheet ss:Name="Workbook1.csv">
<Table ss:ExpandedColumnCount="5" ss:ExpandedRowCount="79" x:FullColumns="1"
x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="15">
<Column ss:Index="2" ss:AutoFitWidth="0" ss:Width="43"/>
<Column ss:AutoFitWidth="0" ss:Width="113"/>
<Column ss:Index="5" ss:AutoFitWidth="0" ss:Width="220"/>
<Row ss:Index="6">
<Cell ss:Index="3" ss:StyleID="s62"/>
</Row>
<Row>
<Cell ss:Index="3" ss:StyleID="s62"/>
</Row>
<Row>
<Cell ss:Index="3" ss:StyleID="s62"/>
</Row>
<Row>
<Cell ss:Index="2"><Data ss:Type="String">id</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">latitude</Data></Cell>
<Cell><Data ss:Type="String">longitude</Data></Cell>
</Row>
<Row>
<Cell ss:Index="2"><Data ss:Type="Number">8</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="Number">57.4999</Data></Cell> // to be saved to $latutude
<Cell><Data ss:Type="Number">15.8280</Data></Cell> // to be saved to $longitude
</Row>
<Row>
<Cell ss:Index="2"><Data ss:Type="Number">38</Data></Cell>
<Cell><Data ss:Type="Number">56.5659</Data></Cell>
<Cell><Data ss:Type="Number">16.1380</Data></Cell>
</Row>

The reason asking for siblings isn't working is that the <Data> elements aren't siblings; they're more like cousins - children of adjacent <Cell> elements.
For the same reason, you shouldn't be using foreach($row->Cell->Data as $data), as this is equivalent to foreach($row->Cell[0]->Data as $data), i.e. look at all <Data> children of the first <Cell> node. Since there will only ever be one <Data> element in a <Cell>, you might as well just write $data = $row->Cell[0]->Data - which in this case is fine, because the values you're looking for are at the beginning of rows.
What you actually need to do is loop over the <Cell>s: foreach($row->Cell as $cell) { $data = $cell->Data; /* ... */ }
You then have a couple of options for finding the adjacent cells, including XPath. A more "PHP-ish" way would be to use array indexes (siblings are indexed numerically in SimpleXML loop/array access):
foreach($row->Cell as $cell_index => $cell)
{
$data = $cell->Data;
if ($data == $ids[$i])
{
// Tip: always cast SimpleXML objects to string when you're done with their magic XMLiness
$latitudes[$i] = (string)$row->Cell[ $cell_index + 1 ]->Data;
$longitudes[$i] = (string)$row->Cell[ $cell_index + 2 ]->Data;
}
}
Alternatively, you could rely on your IDs always being in the first column, and the lat and long in the next two (this is a spreadsheet, after all!) and avoid the inner loop altogether:
if ( $row->Cell[0]->Data == $ids[$i] )
{
$latitudes[$i] = (string)$row->Cell[1]->Data;
$longitudes[$i] = (string)$row->Cell[2]->Data;
}

It looks like in case of this XML, cells are always in same order so it can be done as follows:
$ids = array('8', '53', '38');
foreach ($xml->Worksheet->Table->Row as $row) {
$children = $row->children();
if (count($children) == 3 && in_array(((string) $children[0]->Data), $ids)) {
echo 'lat: ' . $children[1]->Data . ' lng: ' . $children[2]->Data . "\n";
}
}

You can do it entirely in XPath, without any loops, like:
//Row[Cell/Data[. = '8' or . = '53' or . = '38']]/following-sibling::*[position() <= 2]
That search all rows with the id in any data element and then takes the next two siblings.
Or
//Row[Cell[1]/Data[. = '8' or . = '53' or . = '38']]/following-sibling::*[position() <= 2]
if it is sure that the id is always in the first cell. (which also prevents errors due to an id being the same as a longitude/langitude)
Or
//Row[Cell[#ss:Index = "2"]/Data[. = '8' or . = '53' or . = '38']]/following-sibling::*[position() <= 2]
if the id is in the cell with index 2.
But in all cases, you need to initialize the namespaces correctly

An alternative approach if you have lots of IDs to match is to create a "hash" of all the rows based on their ID, and then look into that hash rather than looping through searching for matches.
// Initialise an empty array to use as the hash
$rows_by_id = array();
// Loop over all the rows in the spreadsheet
foreach ($thePositions->Worksheet->Table->Row as $row) {
// Skip rows with less than 3 cells
if ( count($row->Cell) < 3 ) {
continue;
}
// Take the ID from the first cell on the row
$id = (string)$row->Cell[0]->Data;
// Add this row to the hash, identified by it's ID
$rows_by_id[$id] = array(
'latitude' => (string)$row->Cell[1]->Data,
'longitude' => (string)$row->Cell[2]->Data
);
// Or if your IDs are not unique, and you want all matches:
// $rows_by_id[$id][] = array( ... )
}
foreach ( $ids as $required_id ) {
// Retrieve the results from the hash
if ( isset($rows_by_id[$required_id]) ) {
$matched_row = $rows_by_id[$required_id];
echo "ID $required_id has latitude {$matched_row['latitude']} and longitude {$matched_row['longitude']}.\n";
}
else {
echo "ID $required_id was not matched. :(\n";
}
// If you have non-unique matches, you'll need something like this:
// $all_matched_rows = $rows_by_id[$required_id]; ... foreach ( $all_matched_rows as $matched_row )
}

Related

How to break 'foreach' in php when reach a specific tag name?

I'm trying to import a .xml table file to mysql using php, it worked fine, but i want to upgrade my code so i can recognize more variations of xml tables.
So basically the problem is, i got this code ( just a example, my real table is bigger) of .xml file that I'm trying to read:
...
<Table ss:StyleID="s62">
<Column ss:StyleID="s62"/>
<Column ss:StyleID="s62"/>
<Column ss:StyleID="s62"/>
<Row ss:AutoFitHeight="0">
<Cell ss:StyleID="s75"><Data ss:Type="String">Mercado</Data></Cell>
<Cell ss:StyleID="s75"><Data ss:Type="String">Segmento</Data></Cell>
<Cell ss:StyleID="s76"><Data ss:Type="String">Codigo do Projeto</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell ss:StyleID="s90"><Data ss:Type="String">Mineração</Data></Cell>
<Cell ss:StyleID="s90"><Data ss:Type="String">Portuário</Data></Cell>
<Cell ss:StyleID="s90"/>
</Row>
<Row ss:AutoFitHeight="0">
<Cell ss:StyleID="s90"><Data ss:Type="String">Portuário</Data></Cell>
<Cell ss:StyleID="s90"/>
<Cell ss:StyleID="s90"><Data ss:Type="String">Greenfield</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell ss:StyleID="s90"/>
<Cell ss:StyleID="s90"><Data ss:Type="String">Greenfield</Data></Cell>
<Cell ss:StyleID="s90"><Data ss:Type="String">Large CapEx>>maior que 500MBRL</Data></Cell>
</Row>
</Table>
<Worksheet ss:Name="cod">
<Table ss:StyleID="s62">
... ...
</Table>
...
Well, what i want to do is to get the row and data element using getElementByTagName, but i just want to get whats inside the first Table element, not the second, third and so on...
This is what I've tried:
$tabelas = $arquivo->getElementsByTagName("Table");
$rows = $arquivo->getElementsByTagName("Row");
$contRow = 1; (This is just to create a condition to jump the first row)
$contTabelas = TRUE;
foreach ($tabelas as $tabela) {
if ($contTabelas) {
foreach ($rows as $row) {
if ($contRow > 1) {
$Mercado = $row->getElementsByTagName("Data")->item(0)->nodeValue;
$Segmento = $row->getElementsByTagName("Data")->item(1)->nodeValue;
$CodigoDoProjeto = $row->getElementsByTagName("Data")->item(2)->nodeValue;
}
$contRow++;
}
$contTabelas = FALSE;
}
}
It seems that the "foreach($rows as $row)" is getting all the rows from the xml file, but i just want whats inside the "Table" tag. How can I do that??
P.S: I got another problem to solve later, there are a lot of row without an item (Data tag) inside, so i cant get those and the program just jumps to the next one, but i think the solution is just get the 'cell' tag instead 'data'.
That looks like an OpenXML spreadsheet if that is the case you should look for the namespace definitions. I expect you to find xmlns="urn:schemas-microsoft-com:office:spreadsheet" and xmlns::ss="urn:schemas-microsoft-com:office:spreadsheet".
This is the same namespace actually, but XML attributes do not have a default namespace, so they need an prefix/alias.
With that you can use Xpath expressions to fetch specific data from the document:
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('spreadsheet', 'urn:schemas-microsoft-com:office:spreadsheet');
$records = [];
$rows = $xpath->evaluate('((//spreadsheet:Table)[1]/spreadsheet:Row)[position() > 1]');
foreach ($rows as $row) {
$records[] = [
'Mercado' => $xpath->evaluate('string(spreadsheet:Cell[1])', $row),
'Segmento' => $xpath->evaluate('string(spreadsheet:Cell[2])', $row),
'CodigoDoProjeto' => $xpath->evaluate('string(spreadsheet:Cell[3])', $row)
];
}
var_dump($records);
Output:
array(3) {
[1]=>
array(3) {
["Mercado"]=>
string(11) "Mineração"
["Segmento"]=>
string(10) "Portuário"
["CodigoDoProjeto"]=>
string(0) ""
}
[2]=>
array(3) {
["Mercado"]=>
string(10) "Portuário"
["Segmento"]=>
string(0) ""
["CodigoDoProjeto"]=>
string(10) "Greenfield"
}
[3]=>
array(3) {
["Mercado"]=>
string(0) ""
["Segmento"]=>
string(10) "Greenfield"
["CodigoDoProjeto"]=>
string(30) "Large CapEx>>maior que 500MBRL"
}
}
//spreadsheet:Table fetch any Table, (//spreadsheet:Table)[1] limits this to the first, (//spreadsheet:Table)[1]/spreadsheet:Row returns the Row elements of the first Table.
spreadsheet:Cell[1] returns the first Cell and string(spreadsheet:Cell[1]) returns the text content of it. If it did not match a node, it will return an empty string.
You can access just the first table in the tables array by doing $tablas[0]. Now you don't even need a foreach loop.
<?php
$tabelas = $arquivo->getElementsByTagName("Table");
$tablea = $tabelas[0];
$rows = $tablea->getElementsByTagName("Row");
$contRow = 1;
foreach ($rows as $row) {
if ($contRow > 1) {
$Mercado = $row->getElementsByTagName("Data")->item(0)->nodeValue;
$Segmento = $row->getElementsByTagName("Data")->item(1)->nodeValue;
$CodigoDoProjeto = $row->getElementsByTagName("Data")->item(2)->nodeValue;
}
$contRow++;
}
?>

How to insert dynamic row in different html table position

I want to echo following xml data in html table using php,
<type>Debit</type>
<item>Item-1</item>
<price>150</price>
<type>Debit</type>
<item>Item-2</item>
<price>250</price>
<type>Debit</type>
<item>Item-3</item>
<price>100</price>
type>Debit</type>
<item>Item-4</item>
<price>200</price>
............
...so on
<type>Credit</type>
<item>Item-50</item>
<price>200</price>
<type>Credit</type>
<item>Item-51</item>
<price>300</price>
<type>Credit</type>
<item>Item-60</item>
<price>100</price>
............
...so on
here is my Html table structure before inserting data
Now if you see my xml data, i want to dynamically insert debit items under Debit List and credit items in Credit List like the following sample:
I know how to add rows dynamically using insertRow(), getElementById("id").But in this case i need to maintain two different position [1 for debit, 1 for credit] so that i can insert them in correct position.Moreover rows are dynamic, so fixed id with fixed number of rows will not work i guess.How can i do this in php.Please share your idea to solve it in a vary simple way.Please let me know for any further information.Thanks
try this solution. If you use javascript, you use two tables and use append() element row in tbody.
<!DOCTYPE html>
<html>
<body>
<?php
$myXMLData =
"
<data>
<row>
<type>Debit</type>
<item>Item-1</item>
<price>150</price>
</row>
<row>
<type>Debit</type>
<item>Item-2</item>
<price>250</price>
</row>
<row>
<type>Debit</type>
<item>Item-3</item>
<price>100</price>
</row>
<row>
<type>Debit</type>
<item>Item-4</item>
<price>200</price>
</row>
<row>
<type>Credit</type>
<item>Item-50</item>
<price>200</price>
</row>
<row>
<type>Credit</type>
<item>Item-51</item>
<price>300</price>
</row>
<row>
<type>Credit</type>
<item>Item-60</item>
<price>100</price>
</row>
</data>";
$xml=simplexml_load_string($myXMLData) or die("Error: Cannot create object");
$debit = array();
$credit = array();
$debit_total = $credit_total = 0;
foreach ($xml as $row) {
if($row->type == 'Debit') {
$debit[] = array('item'=> (string)$row->item, 'price'=> (float)$row->price);
$debit_total += (float)$row->price;
} else {
$credit[] = array('item'=> (string)$row->item, 'price'=> (float)$row->price);
$credit_total += (float)$row->price;
}
}
?>
<table>
<tr>
<td colspan="2">Debit list<td>
</tr>
<?php
foreach ($debit as $key => $value) {
echo "<tr><td>{$value['item']}</td><td>{$value['price']}</td></tr>";
}
echo "<tr><td>Total</td><td>{$debit_total}</td></tr>";
echo "<tr><td colspan='2'></td></tr>";
echo "<tr><td colspan='2'>Credit list</td></tr>";
foreach ($credit as $key => $value) {
echo "<tr><td>{$value['item']}</td><td>{$value['price']}</td></tr>";
}
echo "<tr><td>Total</td><td>{$credit_total}</td></tr>";
echo "<tr><td colspan='2'></td></tr>";
?>
</table>
</body>
</html>

php xpath iterating through inner nodes, get values based on specific attributes

I have an XML document in following structure.
<rows pos="0" rec_count="200">
<row id="109">
<cell style="" fieldName="pid">104</cell>
<cell style="" fieldName="title">Mr.</cell>
<cell style="" fieldName="name">Vladimir</cell>
<cell style="" fieldName="projectcode">879</cell>
<cell style="" fieldName="clientstatus">1000000000</cell>
</row>
<row id="111">
<cell style="" fieldName="pid">105</cell>
<cell style="" fieldName="title">Mr.</cell>
<cell style="" fieldName="name">Barak</cell>
<cell style="" fieldName="projectcode">789/cell>
<cell style="" fieldName="clientstatus">1000000000</cell>
</row>
</rows>
Now I need to get value of each row, to an array, which each row element is an associative array where fieldName of above becomes key, element value is node value.
How can I do accomplish this with XPath?
I tried several methods; none worked for me yet.
Thanks #JeffreyBosboom , #Makyen for your reply.
The most successfull I came is as follows.
foreach ($xml->xpath('//row') as $node) {
foreach($node->cell as $cell) {
var_dump((string)$cell[0]);
}
}
With it I can get the node value. But I need to get the fieldName attribute extracted in each iteration
Do you mean something like this:
$arr = array();
$s = simplexml_load_string($xml);
//First we search all the <cell> nodes within <row> nodes
foreach($s->xpath("//row/cell") as $node)
{
//For all found nodes we retrieve its ID from ancestor (<row>)
//attribute named id
$id = $node->xpath("../#id");
$id = (int)$id[0];
if(!isset($arr[$id]))
{
$arr[$id] = array();
}
//Then we iterate through all attributes of found <cell> nodes
//to find the attribute named fieldName
foreach($node->attributes() as $key=>$att)
{
if($key=='fieldName')
{
$arr[$id][(string)$att] = (string)$node;
}
}
}
//In $arr array there will be desired output
echo "<pre>";
print_r($arr);
echo "</pre>";
?

loops with fwrite for XML

function xmlParse() {
$fh = fopen('schools/' . $this->id . '/books/school_books.xml', 'a');
$xmlstr = "<?xml version='1.0' ?>\n" .
"<rows></rows>";
// create the SimpleXMLElement object with an empty <book> element
$xml = new SimpleXMLElement($xmlstr);
$x = 0;
// add some more child nodes
for ($i = 0; $i < sizeof($this->students); $i++) {
$this->students[$i]->getmyBooks();
for ($j = 0; $j < sizeof($this->students[$i]->myBooks); $j++) {
$row = $xml->addChild("row");
$row->addAttribute("id", $x);
$row->addChild("cell", $this->students[$i]->myBooks[$j]->owner);
$row->addChild("cell", $this->students[$i]->myBooks[$j]->title);
$row->addChild("cell", $this->students[$i]->myBooks[$j]->category);
$row->addChild("cell", $this->students[$i]->myBooks[$j]->price);
$row->addChild("cell", $this->students[$i]->myBooks[$j]->description);
$row->addChild("cell", "Test");
$x++;
fwrite($fh, $xml->asXML());
}
}
}
I know what the problem is: its fwrite($fh, $xml->asXML());
if I keep calling that within the loop it doesn't append it keeps starting the xml document from scratch and posting the tags again.
My Problem is that it keeps writing from all over again the xml tags... instead of just continuing with the xml. If i do it for just 1 student it works perfect, but instead when I try to loop through all my students it keeps printing the xml tags instead of continuing on to the next student.
<?xml version="1.0"?>
<rows>
<row id="0">
<cell>Owner</cell>
<cell>test</cell>
<cell>Math</cell>
<cell>11</cell>
<cell>test</cell>
<cell>Test</cell>
</row>
</rows>
<?xml version="1.0"?>
continues with the next one then it does xml tags again and again.
This is how its to look like for one student:
<rows>
<row id="0">
<cell>Owner</cell>
<cell>Calculus III</cell>
<cell>Math</cell>
<cell>82</cell>
<cell>This book is in great condition! Available asap.</cell>
<cell>Test</cell>
</row>
<row id="1">
<cell>Owner</cell>
<cell>Discrete Mathematics</cell>
<cell>Math</cell>
<cell>62</cell>
<cell>This book is in poor condition.</cell>
<cell>Test</cell>
</row>
<row id="2">
<cell>Owner</cell>
<cell>Calculus I</cell>
<cell>Math</cell>
<cell>12</cell>
<cell>Really good book.</cell>
<cell>Test</cell>
</row>
</rows>
You're really looking for file_put_contents($name, $contents). That function adds all of the content en masse, so you would call it once at the end of the loop.
The code might look like:
// after i >= sizeof($this->students)
file_put_contents('schools/' . $this->id . '/books/school_books.xml',
$xml->asXML());
fwrite on the other hand, appends a file every single time it is called. This means that it will add the contents of the XML to the file sizeof($this->students) times, which is what you're seeing right now.
By the way, depending on the size of sizeof($this->students), you may want to declare a local variable to cache that before you look, sizeof it will be called every time.
$studentSize = sizeof($this->students);
for ($i = 0; $i < $studentSize; $i++) {
On the other hand, you might want to change that to a foreach loop (can't describe how at the moment but I'll add that in later if I remember).

PHP creating XML is inserting extra tags and extraneous data. Help?

I'm trying to import XML data from an excel spreadsheet (saved as an 'XML Spreadsheet 2003'). The PHP script reads the excel data, and then transposes the data to an external XML file. This new file, in turn is read by a javscript to place markers on a google map using the google map API. I know this is inefficient, but the goal is to give the client a spreadsheet they can easily edit and upload, and have those changes reflected on the site without any intervention from me.
The issues are as follows:
The script duplicates the last excel
entry twice (creating 3 xml entries
of the same data)
The cells which contain numbers
have extra zeros placed in the
middle of the number (excel does this)
The header row of the excel file is
being included in the XML, despite my
attempt otherwise
The URL attribute of the XML
should be blank if it is not
provided, but the xml reads 'URL'
Screenshot of Excel showing how data is entered:
Correction, no images for noobs like me. Click for a screen shot: screenshot
Source of Excel XML spreadsheet (I included all of it in case I'm missing something):
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Author>Brandon</Author>
<LastAuthor>Brandon</LastAuthor>
<Created>2011-04-01T21:05:56Z</Created>
<Version>14.00</Version>
</DocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
<AllowPNG/>
</OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>7995</WindowHeight>
<WindowWidth>20115</WindowWidth>
<WindowTopX>240</WindowTopX>
<WindowTopY>75</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s62">
<Font ss:FontName="Arial" x:Family="Swiss" ss:Bold="1"/>
</Style>
</Styles>
<Worksheet ss:Name="Sheet1">
<Table ss:ExpandedColumnCount="9" ss:ExpandedRowCount="4" x:FullColumns="1"
x:FullRows="1" ss:DefaultRowHeight="15">
<Column ss:AutoFitWidth="0" ss:Width="86.25"/>
<Column ss:AutoFitWidth="0" ss:Width="128.25"/>
<Column ss:AutoFitWidth="0" ss:Width="156.75"/>
<Column ss:AutoFitWidth="0" ss:Width="112.5"/>
<Column ss:AutoFitWidth="0" ss:Width="103.5"/>
<Row ss:AutoFitHeight="0">
<Cell ss:StyleID="s62"><Data ss:Type="String">Lat</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">Long</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">Name</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">Phone</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">Address</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">City</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">State</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">Zip</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">URL</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="Number">39.769831000000003</Data></Cell>
<Cell><Data ss:Type="Number">-104.972657</Data></Cell>
<Cell><Data ss:Type="String">Generic Liquors</Data></Cell>
<Cell><Data ss:Type="String">555-123-5555</Data></Cell>
<Cell><Data ss:Type="String">42 Walnut St</Data></Cell>
<Cell><Data ss:Type="String">Denver</Data></Cell>
<Cell><Data ss:Type="String">CO</Data></Cell>
<Cell><Data ss:Type="Number">80207</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="Number">39.763903999999997</Data></Cell>
<Cell><Data ss:Type="Number">-104.966311</Data></Cell>
<Cell><Data ss:Type="String">Fancy Restaurant</Data></Cell>
<Cell><Data ss:Type="String">555-123-5556</Data></Cell>
<Cell><Data ss:Type="String">55 Colfax Drive</Data></Cell>
<Cell><Data ss:Type="String">Denver</Data></Cell>
<Cell><Data ss:Type="String">CO</Data></Cell>
<Cell><Data ss:Type="Number">80207</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="Number">39.759087000000001</Data></Cell>
<Cell><Data ss:Type="Number">-104.982963</Data></Cell>
<Cell><Data ss:Type="String">Even Fancier Restaurant</Data></Cell>
<Cell><Data ss:Type="String">555-123-5557</Data></Cell>
<Cell><Data ss:Type="String">1129 Boggio St</Data></Cell>
<Cell><Data ss:Type="String">Denver</Data></Cell>
<Cell><Data ss:Type="String">CO</Data></Cell>
<Cell><Data ss:Type="Number">87505</Data></Cell>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<Unsynced/>
<Selected/>
<Panes>
<Pane>
<Number>3</Number>
<ActiveRow>4</ActiveRow>
<ActiveCol>4</ActiveCol>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="Sheet2">
<Table ss:ExpandedColumnCount="1" ss:ExpandedRowCount="1" x:FullColumns="1"
x:FullRows="1" ss:DefaultRowHeight="15">
<Row ss:AutoFitHeight="0"/>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<Unsynced/>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="Sheet3">
<Table ss:ExpandedColumnCount="1" ss:ExpandedRowCount="1" x:FullColumns="1"
x:FullRows="1" ss:DefaultRowHeight="15">
<Row ss:AutoFitHeight="0"/>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<Unsynced/>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
</Workbook>
Excel XML source of just the relevant cell data:
<Row ss:AutoFitHeight="0">
<Cell ss:StyleID="s62"><Data ss:Type="String">Lat</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">Long</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">Name</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">Phone</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">Address</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">City</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">State</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">Zip</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">URL</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="Number">39.769831000000003</Data></Cell>
<Cell><Data ss:Type="Number">-104.972657</Data></Cell>
<Cell><Data ss:Type="String">Generic Liquors</Data></Cell>
<Cell><Data ss:Type="String">555-123-5555</Data></Cell>
<Cell><Data ss:Type="String">42 Walnut St</Data></Cell>
<Cell><Data ss:Type="String">Denver</Data></Cell>
<Cell><Data ss:Type="String">CO</Data></Cell>
<Cell><Data ss:Type="Number">80207</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="Number">39.763903999999997</Data></Cell>
<Cell><Data ss:Type="Number">-104.966311</Data></Cell>
<Cell><Data ss:Type="String">Fancy Restaurant</Data></Cell>
<Cell><Data ss:Type="String">555-123-5556</Data></Cell>
<Cell><Data ss:Type="String">55 Colfax Drive</Data></Cell>
<Cell><Data ss:Type="String">Denver</Data></Cell>
<Cell><Data ss:Type="String">CO</Data></Cell>
<Cell><Data ss:Type="Number">80207</Data></Cell>
</Row>
<Row ss:AutoFitHeight="0">
<Cell><Data ss:Type="Number">39.759087000000001</Data></Cell>
<Cell><Data ss:Type="Number">-104.982963</Data></Cell>
<Cell><Data ss:Type="String">Even Fancier Restaurant</Data></Cell>
<Cell><Data ss:Type="String">555-123-5557</Data></Cell>
<Cell><Data ss:Type="String">1129 Boggio St</Data></Cell>
<Cell><Data ss:Type="String">Denver</Data></Cell>
<Cell><Data ss:Type="String">CO</Data></Cell>
<Cell><Data ss:Type="Number">87505</Data></Cell>
</Row>
PHP Script:
<?php
//
// Read the Excel File
$data = array();
//format the array values
function add_marker( $lat, $long, $name, $phone, $address, $city, $state, $zip, $url )
{
global $data;
$data []= array(
'Lat' => $lat,
'Long' => $long,
'Name' => $name,
'Phone' => $phone,
'Address' => $address,
'City' => $city,
'State' => $state,
'Zip' => $zip,
'URL' => $url
);
}
// Load the spreadsheet
$dom = DOMDocument::load( '/home/public/xml/tour.xml' );
// Select XML tag in the spreadsheet to use
$rows = $dom->getElementsByTagName( 'Row' );
$first_row = true;
foreach ($rows as $row)
{
if ( !$first_row )
{
$lat = "";
$long = "";
$name = "";
$phone = "";
$address = "";
$city = "";
$state = "";
$zip = "";
$url = "";
$index = 1;
$cells = $row->getElementsByTagName( 'Cell' );
foreach( $cells as $cell )
{
$ind = $cell->getAttribute( 'Index' );
if ( $ind != null ) $index = $ind;
if ( $index == 1 ) $lat = $cell->nodeValue;
if ( $index == 2 ) $long = $cell->nodeValue;
if ( $index == 3 ) $name = $cell->nodeValue;
if ( $index == 4 ) $phone = $cell->nodeValue;
if ( $index == 5 ) $address = $cell->nodeValue;
if ( $index == 6 ) $city = $cell->nodeValue;
if ( $index == 7 ) $state = $cell->nodeValue;
if ( $index == 8 ) $zip = $cell->nodeValue;
if ( $index == 9 ) $url = $cell->nodeValue;
$index += 1;
}
add_marker( $lat, $long, $name, $phone, $address, $city, $state, $zip, $url );
}
$first_row = false;
}
//
// Write and save xml file
//
//create document
$doc = new DOMDocument('1.0', 'UTF-8');
//pretty formatting
$doc->formatOutput = true;
//create 'markers' root element
$root = $doc->createElement('markers');
$doc->appendChild($root);
//run through the array constructed from excel file
foreach( $data as $row )
{
//create individual marker element
$root_child = $doc->createElement('marker');
$root->appendChild($root_child);
//set attribute of lat
$root_attr1 = $doc->createAttribute('lat');
$root_child->appendChild($root_attr1);
//assign 'lat' attribute it's value from array
$root_text = $doc->createTextNode($row['Lat']);
$root_attr1->appendChild($root_text);
//set attribute of lng
$root_attr2= $doc->createAttribute('lng');
$root_child->appendChild($root_attr2);
//assign 'lng' attribute it's value from array
$root_text = $doc->createTextNode($row['Long']);
$root_attr2->appendChild($root_text);
//set attribute of name
$root_attr3= $doc->createAttribute('name');
$root_child->appendChild($root_attr3);
//assign 'name' attribute it's value from array
$root_text = $doc->createTextNode($row['Name']);
$root_attr3->appendChild($root_text);
//set attribute of phone
$root_attr4= $doc->createAttribute('phone');
$root_child->appendChild($root_attr4);
//assign 'phone' attribute it's value from array
$root_text = $doc->createTextNode($row['Phone']);
$root_attr4->appendChild($root_text);
//set attribute of address
$root_attr5= $doc->createAttribute('address');
$root_child->appendChild($root_attr5);
//assign 'address' attribute it's value from array
$root_text = $doc->createTextNode($row['Address']);
$root_attr5->appendChild($root_text);
//set attribute of city
$root_attr6= $doc->createAttribute('city');
$root_child->appendChild($root_attr6);
//assign 'city' attribute it's value from array
$root_text = $doc->createTextNode($row['City']);
$root_attr6->appendChild($root_text);
//set attribute of state
$root_attr7= $doc->createAttribute('state');
$root_child->appendChild($root_attr7);
//assign 'state' attribute it's value from array
$root_text = $doc->createTextNode($row['State']);
$root_attr7->appendChild($root_text);
//set attribute of zip
$root_attr8= $doc->createAttribute('zip');
$root_child->appendChild($root_attr8);
//assign 'zip' attribute it's value from array
$root_text = $doc->createTextNode($row['Zip']);
$root_attr8->appendChild($root_text);
//set attribute of url
$root_attr9= $doc->createAttribute('url');
$root_child->appendChild($root_attr9);
//assign 'name' attribute it's value from array
$root_text = $doc->createTextNode($row['URL']);
$root_attr9->appendChild($root_text);
}
$doc->save("/home/public/xml/markers.xml");//save("/home/public/xml/markers.xml");
?>
Output of the markers.xml file:
<markers>
<marker lat="Lat" lng="Long" name="Name" phone="Phone" address="Address" city="City" state="State" zip="Zip" url="URL"/>
<marker lat="39.769831000000003" lng="-104.972657" name="Generic Liquors" phone="555-123-5555" address="42 Walnut St" city="Denver" state="CO" zip="80207" url="URL"/>
<marker lat="39.763903999999997" lng="-104.966311" name="Fancy Restaurant" phone="555-123-5556" address="55 Colfax Drive" city="Denver" state="CO" zip="80207" url="URL"/>
<marker lat="39.759087000000001" lng="-104.982963" name="Even Fancier Restaurant" phone="555-123-5557" address="1129 Boggio St" city="Denver" state="CO" zip="87505" url="URL"/>
<marker lat="39.759087000000001" lng="-104.982963" name="Even Fancier Restaurant" phone="555-123-5557" address="1129 Boggio St" city="Denver" state="CO" zip="87505" url="URL"/>
<marker lat="39.759087000000001" lng="-104.982963" name="Even Fancier Restaurant" phone="555-123-5557" address="1129 Boggio St" city="Denver" state="CO" zip="87505" url="URL"/>
</markers>
Lots of extra numbers and extra marker tags in there. Ideas?
Problem #1:
The script duplicates the last excel entry twice (creating 3 xml entries of the same data)
Could be related to the fact that there is a blank row in the second worksheet. Restrict your code to the rows in the first worksheet only because you're still calling add_marker() even if there are no cells in the row. If you don't restrrict to just the first worksheet, then you can do
if ($index > 1)
add_marker( $lat, $long, $name, $phone, $address, $city, $state, $zip, $url );
instead of just
add_marker( $lat, $long, $name, $phone, $address, $city, $state, $zip, $url );
Problem #2:
The cells which contain numbers have extra zeros placed in the middle of the number (excel does this)
change the lines that extract this information to round the data to perhaps 6 decimal places
e.g. change
$lat = $cell->nodeValue;
to
$lat = round($cell->nodeValue,6);
Problem #3:
The header row of the excel file is being included in the XML, despite my attempt otherwise
Ensure that the header is actually row 1 of the first worksheet, and the code you've provided should work correctly. If the header is in row 2 or above, or if you have any previous worksheets in the workbook, then you'll have this problem.

Categories