2016-11-16 26 views
1

有與內容類似以下內容的XML文件:如何對使用SimpleXML加載的XML文件的內容進行排序?

<FMPDSORESULT xmlns="http://www.filemaker.com"> 
<ERRORCODE>0</ERRORCODE> 
<DATABASE>My_Database</DATABASE> 
<LAYOUT/> 
<ROW MODID="1" RECORDID="1"> 
    <Name>John</Name> 
    <Age>19</Age> 
</ROW> 
<ROW MODID="2" RECORDID="2"> 
    <Name>Steve</Name> 
    <Age>25</Age> 
</ROW> 
<ROW MODID="3" RECORDID="3"> 
    <Name>Adam</Name> 
    <Age>45</Age> 
</ROW> 

我試圖使用array_multisort功能ROW標籤由Name變量的數值進行排序:

$xml = simplexml_load_file('xml1.xml'); 
$xml2 = sort_xml($xml ); 
print_r($xml2); 
function sort_xml($xml ) { 
    $sort_temp = array(); 
    foreach ($xml as $key => $node) { 
     $sort_temp[ $key ] = (string) $node->Name; 
} 
array_multisort($sort_temp, SORT_DESC, $xml); 
return $xml; 
} 

但代碼無法按預期工作。

回答

0

一些SimpleXMLElement方法返回數組,但大多數返回實現Iterator的SimpleXMLElement對象。 var_dump()只能以簡化表示形式顯示部分數據。但是它是一個對象結構,而不是嵌套數組。

如果我正確理解你,你想按Name孩子排序ROW元素。您可以使用xpath()方法獲取它們,但需要爲名稱空間註冊一個前綴。它返回一個SimpleXMLElement對象數組。該陣列可以使用usort排序。

$fResult = new SimpleXMLElement($xml); 
$fResult->registerXpathNamespace('fm', 'http://www.filemaker.com'); 
$rows = $fResult->xpath('//fm:ROW'); 

usort(
    $rows, 
    function(SimpleXMLElement $one, SimpleXMLElement $two) { 
    return strcasecmp($one->Name, $two->Name); 
    } 
); 

var_dump($rows); 

在DOM不會看起來非常不同,但DOMXpath::evaluate()返回DOMNodeList。您可以使用iterator_to_array將其轉換爲數組。

$document = new DOMDocument(); 
$document->loadXml($xml); 
$xpath = new DOMXpath($document); 
$xpath->registerNamespace('fm', 'http://www.filemaker.com'); 

$rows = iterator_to_array($xpath->evaluate('//fm:ROW')); 

usort(
    $rows, 
    function(DOMElement $one, DOMElement $two) use ($xpath) { 
    return strcasecmp(
     $xpath->evaluate('normalize-space(Name)', $one), 
     $xpath->evaluate('normalize-space(Name)', $two) 
    ); 
    } 
); 

var_dump($rows); 

DOM沒有神奇的方法來訪問子元素和值,Xpath可以用來獲取它們。 Xpath函數string()將第一個節點轉換爲字符串。如果節點列表爲空,它將返回一個空字符串。 normalize-space()做了一些。它用一個空格替換所有的空格組,並從字符串的開頭和結尾去掉它。

0

我會建議使用DOM擴展,因爲它是更靈活:

$doc = new DOMDocument(); 
$doc->preserveWhiteSpace = false; 
$doc->formatOutput = true; 
$doc->load('xml1.xml'); 

// Get the root node 
$root = $doc->getElementsByTagName('FMPDSORESULT'); 
if (!$root->length) 
    die('FMPDSORESULT node not found'); 
$root = $root[0]; 

// Pull the ROW tags from the document into an array. 
$rows = []; 
$nodes = $root->getElementsByTagName('ROW'); 
while ($row = $nodes->item(0)) { 
    $rows []= $root->removeChild($row); 
} 

// Sort the array of ROW tags 
usort($rows, function ($a, $b) { 
    $a_name = $a->getElementsByTagName('Name'); 
    $b_name = $b->getElementsByTagName('Name'); 

    return ($a_name->length && $b_name->length) ? 
    strcmp(trim($a_name[0]->textContent), trim($b_name[0]->textContent)) : 0; 
}); 

// Append ROW tags back into the document 
foreach ($rows as $row) { 
    $root->appendChild($row); 
} 

// Output the result 
echo $doc->saveXML(); 

輸出

<?xml version="1.0"?> 
<FMPDSORESULT xmlns="http://www.filemaker.com"> 
    <ERRORCODE>0</ERRORCODE> 
    <DATABASE>My_Database</DATABASE> 
    <LAYOUT/> 
    <ROW MODID="3" RECORDID="3"> 
    <Name>Adam</Name> 
    <Age>45</Age> 
    </ROW> 
    <ROW MODID="1" RECORDID="1"> 
    <Name>John</Name> 
    <Age>19</Age> 
    </ROW> 
    <ROW MODID="2" RECORDID="2"> 
    <Name>Steve</Name> 
    <Age>25</Age> 
    </ROW> 
</FMPDSORESULT> 

關於XPath的

可以使用DOMXPath甚至更​​多靈活的遍歷。但是,在我看來,在這個特定的問題中,使用DOMXPath不會帶來顯着的改進。無論如何,我會舉例說明完整性。

抓取行:

$xpath = new DOMXPath($doc); 
$xpath->registerNamespace('myns', 'http://www.filemaker.com'); 

$rows = []; 
foreach ($xpath->query('//myns:ROW') as $row) { 
    $rows []= $row->parentNode->removeChild($row); 
} 

追加行迴文檔:

$root = $xpath->evaluate('/myns:FMPDSORESULT')[0]; 
foreach ($rows as $row) { 
    $root->appendChild($row); 
} 
+0

你說的沒錯,DOM是很多更加靈活和強大。但是我建議在任何情況下都使用Xpath。 :-) – ThW

+0

@ThW,我喜歡XPath,但在這種特殊情況下,我不認爲它會顯着改善代碼。 –