我,嗯,似乎迷路了。
我相信我的问题是正确解析 PHP DOMDocument
类。
我有一个来自Excel的XML电子表格,其中包含不同列的标题。(它还具有多个工作表,以帮助最终用户组织数据。
我的最终目标是使用JavaScript在地图上做标记。
下面是 XML 文件的简化示例:注意:有些数据是字符串,有些是数字,有些是HTML。
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook>
<Worksheet ss:Name="data">
<Table>
<Row>
<Cell><Data ss:Type="String">lat</Data></Cell>
<Cell><Data ss:Type="String">lng</Data></Cell>
<Cell><Data ss:Type="String">boolean_1</Data></Cell>
<Cell><Data ss:Type="String">boolean_2</Data></Cell>
<Cell><Data ss:Type="String">Source_documents</Data></Cell>
<Cell><Data ss:Type="String">description</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="Number">35.032139998</Data></Cell>
<Cell><Data ss:Type="Number">-117.346952</Data></Cell>
<Cell><Data ss:Type="Number">1</Data></Cell>
<Cell><Data ss:Type="Number">0</Data></Cell>
<Cell><ss:Data ss:Type="String" xmlns="http://www.w3.org/TR/REC-html40"><Font html:Color="#000000">Copy here inside HTML </Font><I><Font html:Color="#000000">with more copy</Font></I></ss:Data></Cell>
<Cell><Data ss:Type="String">Copy here without HTML</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="Number">43.444</Data></Cell>
<Cell><Data ss:Type="Number">-112.005</Data></Cell>
<Cell><Data ss:Type="Number">1</Data></Cell>
<Cell><Data ss:Type="Number">1</Data></Cell>
<Cell><Data ss:Type="String">Diff Marker Src</Data></Cell>
<Cell><Data ss:Type="String">Diff Marker Desc</Data></Cell>
</Row>
</Table>
</Worksheet>
<Worksheet ss:Name="tags">
<Table>
<Row>
<Cell><Data ss:Type="String">tag_label</Data></Cell>
<Cell><Data ss:Type="String">tag_category</Data></Cell>
<Cell><Data ss:Type="String">tag_description</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">boolean_1</Data></Cell>
<Cell><Data ss:Type="String">tag_cat_A</Data></Cell>
<Cell><Data ss:Type="String">bool_1 desc</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">boolean_2</Data></Cell>
<Cell><Data ss:Type="String">tag_cat_B</Data></Cell>
<Cell><Data ss:Type="String">bool_2 desc</Data></Cell>
</Row>
</Table>
</Worksheet>
</Workbook>
我一直假设我需要将电子表格转换为 JSON 数组或结构更好的 XML 文档,我可以解析该文档以创建地图的标记。(JSON似乎更可取,以减少传输的数据)
如果这个假设是正确的,我希望有一个看起来像这样的结构:
array => {
data => {
[0] => {
lat => '35.032139998',
lng => '-117.346952',
booleans => {
boolean_1 => true
},
Source_documents => '<Font html:Color="#000000">Copy here inside HTML </Font><I><Font html:Color="#000000">with more copy</Font></I>',
'description' => 'Copy here without HTML'
},
[1] => {
lat => '43.444',
lng => '-112.005',
booleans => {
boolean_1 => true,
boolean_2 => true
},
Source_documents => 'Diff Marker Src',
'description' => 'Diff Marker Desc'
}
},
tags = {
'boolean_1' => {
tag_category => 'tag_cat_A',
'tag_description' => 'bool_1 desc'
},
'boolean_2' => {
tag_category => 'tag_cat_B',
'tag_description' => 'bool_2 desc'
}
}
}
我正在使用PHP,并尝试使用DOMDocument
类将XML转换为JSON。 SimpleXML对我来说工作得很好,直到加载了一个新的Excel文档,其中包括偶尔的HTML。
到目前为止,我有这个PHP代码:
function get_worksheet_table($file, $worksheet_name) {
$dom = new DOMDocument;
$dom->load($file);
// returns a new instance of class DOMNodeList
$worksheets = $dom->getElementsByTagName( 'Worksheet' );
foreach($worksheets as $worksheet) {
// check if right sheet
if( $worksheet->getAttribute('ss:Name') == $worksheet_name) {
// trying to get entire node, or childNodeList, or ... ?
// About here I am getting lost.
$nodes = $worksheet->getElementsByTagName('Table')->item(0);
$table = new DOMDocument;
$table->preserveWhiteSpace = false;
$table->formatOutput = true;
$table->createElement('Table');
/*
ITERATE THROUGH $nodes, ADD EACH CELL NODE'S CONTENTS
TO $table -- UNLESS IT HAS HTML, THEN USE DOMinnerHTML(node)
(DOMinnerHTML function @ http://php.net/manual/en/book.dom.php#89718)
*/
return $table;
}
}
return false;
}
$data = get_worksheet_table($file, 'data');
$tags = get_worksheet_table($file, 'tags');
从那里,我尝试从$data和$tags创建关联数组,然后输出一个大的 JSON 语句以传递给我的应用程序。
但这真的是一团糟,我,就像我说的,我迷路了。
问题:
- 这看起来我至少走在正确的轨道上吗?
- 如何正确访问节点?— 我似乎将所有子节点作为一个大文本值获取。
- 如何遍历 DOM 以在适当的情况下访问单元格的文本内容,并以字符串而不是子节点的形式访问
<data>
节点的任何子节点?
任何关于更好地理解如何解析 DOMDocument 类的指示将不胜感激。我一直在阅读文档,但它让我无法理解。
非常感谢您的时间。
经过更多的研究,我找到了一种方法来实现我想要的。我不会说这是最好的方法,从远处看。
但是,我能够:
- 解析从 Excel 生成的 XML 电子表格,按照我想要的结构;
- 将其输出为 JSON;和
- 在生成的输出中将任何文本样式保留为 HTML。
公平地说,我没有突破HTML的极限——例如,我们实际上只是弄乱了<b>
和<i>
标签。字体标签也进来了,我决定去掉它们。
如果有更干净、更优雅的方法可以做到这一点,我不会感到惊讶——我几乎是尽快从一个对象变成一个数组——我还应该注意到,就我而言,我正在处理一个相对较小的数据负载。YMMV 用于大型项目,但如果您正在阅读本文,那么我希望这会有所帮助。
下面是我从 XML 工作表表生成数据数组的函数:
/* array_from_worksheet_table()
* Generate an array from an XML Worksheet
* $file needs to be the full path to your file (e.g., '/Users/jeremy/www/cms/files/yourfile.xml')
* $worksheet_name = the name of the worksheet tab
*/
function array_from_worksheet_table($file, $worksheet_name) {
// https://stackoverflow.com/questions/7082401/avoid-domdocument-xml-warnings-in-php
$previous_errors = libxml_use_internal_errors(true);
$dom = new DOMDocument;
if( !$dom->load($file) ) {
foreach (libxml_get_errors() as $error) {
// print_r($error);
}
}
libxml_clear_errors();
libxml_use_internal_errors($previous_errors);
// returns a new instance of class DOMNodeList
$worksheets = $dom->getElementsByTagName( 'Worksheet' );
foreach($worksheets as $worksheet) {
if( $worksheet->getAttribute('ss:Name') == $worksheet_name) {
// When we get a DOMNodeList, if we want to access the first item, we have to
// then use ->item(0). Important once we want to access a deeper-level DOMNodeList
$rows = $worksheet->getElementsByTagName('Table')->item(0)->getElementsByTagName('Row');
$table = array();
// Get our headings.
// This assumes that the first row HAS our headings!
$headings = $rows->item(0)->getElementsByTagName('Cell');
// loop through table rows. Setting $i=1 instead of 0 means we skip the first row
for( $i = 1; $i < $rows->length; $i++ ) {
// this is our row of data
$cells = $rows->item($i)->getElementsByTagName('Cell');
// loop through each cell
for( $c = 0; $c < $cells->length; $c++ ) {
// check for data element in cell
$celldata = $cells->item($c)->getElementsByTagName('Data');
// If the cell has data, proceed
if( $celldata->length ) {
// Get HTML content of any strings
if( $celldata->item(0)->getAttribute('ss:Type')== 'String' ) {
// Does not work for PHP < 5.3.6
// If you HAVE PHP 5.3.6 then use function @ https://stackoverflow.com/questions/2087103/
// $value = xml_to_json::DOMinnerHTML( $celldata->item(0) );
// DOMNode::C14N canonicalizes nodes into strings
// This workaround is required for PHP < 5.3.6
$value = $celldata->item(0)->C14N();
// hack. remove tags like <ss:Data foo...> and </Data>
// Necessary because C14N leaves outer tags (saveHTML did not)
$value = preg_replace('/<([s/:]+)?Data([^>]+)?>/i', '', $value);
// Remove font tags from HTML. Bleah.
$value = preg_replace('/</?font([^>]+)?>/i', '', $value);
} else {
$value = $cells->item($c)->nodeValue;
}
// grab label from first row
$label = $headings->item($c)->nodeValue;
$table[$i][$label] = $value;
}
}
}
return $table;
}
}
return false;
}
这返回了一个工作表表的数组,然后我能够进一步操作该数组。
一项任务是重新组织生成的数组,以便我的布尔值都在子数组中。首先,我使用 remove_element_by_value($data, '0')
删除了所有零值(找到该函数 @ https://stackoverflow.com/a/4466181/156645)
然后我将数组键与tags
数组中的值进行比较,并将它们附加到每个子数组中,如下所示($long_codes
是我的标签值的简单数组):
if($data_array) {
foreach($data_array as $key => $array) {
foreach($array as $k => $val) {
if( in_array($k, $long_codes)) {
$data_array[$key]['Classify'][] = $k;
unset($data_array[$key][$k]);
}
}
}
}
输出刚好echo json_encode($the_big_array)
,而大阵列正好array('data' => $data_array, 'tags' => $tags_array)
。
希望对别人有帮助!