2016-11-16 29 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,但在这种特殊情况下,我不认为它会显着改善代码。 –