【问题标题】:How to sort content of an XML file loaded with SimpleXML?如何对使用 SimpleXML 加载的 XML 文件的内容进行排序?
【发布时间】:2017-03-30 10:19:57
【问题描述】:

有一个 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 函数按Name 标记的值对ROW 标记进行排序:

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

但代码没有按预期工作。

【问题讨论】:

    标签: php xml sorting


    【解决方案1】:

    我建议使用 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);
    }
    

    【讨论】:

    • 你说得对,DOM 更加灵活和强大。但我建议在任何情况下都使用 Xpath。 :-)
    • @ThW,我喜欢 XPath,但在这种特殊情况下,我认为它不会显着改进代码。
    【解决方案2】:

    一些 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() 做得更多。它将所有空白组替换为单个空格,并将其从字符串的开头和结尾剥离。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-07
      相关资源
      最近更新 更多