PHP排序和更新节点

时间:2016-03-13 13:15:38

标签: php xml sorting

我一整天都在和这个作斗争:(

虽然我找到了类似问题的答案,但他们没有更新现有的XML,但他们创建了一个新的XML。

非常感谢任何帮助。

这是我正在加载的XML,并尝试仅对图像 - >图像节点进行排序:

<?xml version="1.0"?>
<stuff>
    <other_nodes>
    </other_nodes>
    <images>
        <image><sorted_number><![CDATA[1]]></sorted_number></image>
        <image><sorted_number><![CDATA[3]]></sorted_number></image>
        <image><sorted_number><![CDATA[2]]></sorted_number></image>
    </images>
</stuff>

//load the xml into a var
$theXML = //load the xml from the database

$imageNode = $theXML->images;

//sort the images into sorted order
$d = $imageNode;

// turn into array
$e = array();
foreach ($d->image as $image) {
        $e[] = $image;
}
// sort the array
usort($e, function($a, $b) {
        return $a->sorted_number - $b->sorted_number;
});



//now update the xml in the correct order

foreach ($e as $node) { 

//???unsure how to update the images node in my XML

}

3 个答案:

答案 0 :(得分:2)

对于您的任务,

SimpleXML 简单。没有简单的方法来重新排序节点。基本上,在排序例程之后,您必须重建<image>个节点,但内部有CDATA SimpleXML 不能直接添加CDATA

如果你想通过这种方式尝试,here你可以找到一个很酷的 SimpleXML 类扩展来添加CDATA属性,但是这个解决方案也使用 DOMDocument < / em>的

基本上,恕我直言,因为每个解决方案都需要 DOM ,最好的方法是直接使用 DOMDocument 并最终 - (重新)使用 SimpleXML <加载XML / em>转型后:

$dom = new DOMDocument();
$dom->loadXML( $xml, LIBXML_NOBLANKS );
$dom->formatOutput = True;

$images = $dom->getElementsByTagName( 'image' );

/* This is the same as your array conversion: */
$sorted = iterator_to_array( $images );

/* This is your sorting routine adapted to DOMDocument: */
usort( $sorted, function( $a, $b )
{
    return
    $a->getElementsByTagName('sorted_number')->item(0)->nodeValue
    -
    $b->getElementsByTagName('sorted_number')->item(0)->nodeValue;
});

/* This is the core loop to “replace” old nodes: */
foreach( $sorted as $node ) $images->item(0)->parentNode->appendChild( $node );

echo $dom->saveXML();

eval.in demo

主例程将已排序的节点添加为现有<images>节点的子节点。请注意,不需要预先移除旧子节点:因为我们引用相同的对象,所以通过附加节点实际上我们将其从之前的位置移除。

如果你想获得一个 SimpleXML 对象,在上面代码的末尾你可以追加这一行:

$xml = simplexml_load_string( $dom->saveXML() );

答案 1 :(得分:1)

考虑使用其<xsl:sort>的XSLT解决方案。作为信息,XSLT(其脚本是格式良好的XML文件)是一种声明性的,专用的编程语言(与SQL类型相同),专门用于操作XML文档和排序是一种操作。 XSLT通常用作样式表将XML内容呈现为HTML,实际上是一种语言。

大多数通用语言,包括PHP( xsl扩展),Python( lxml模块),Java( javax.xml ),Perl ( libxml ),C#( System.Xml )和VB( MSXML )维护XSLT 1.0处理器。各种外部可执行处理器如Xalan and Saxon(后者可运行XSLT 2.0和最近的3.0)也可用 - 当然PHP可以使用exec()调用。下面将XSLT嵌入为字符串变量,但可以非常容易地从外部.xsl或.xslt文件加载。

// Load the XML source and XSLT file
$doc = new DOMDocument();
$doc->loadXML($xml);

$xsl = new DOMDocument;    
$xslstr = '<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
           <xsl:output version="1.0" encoding="UTF-8" indent="yes"
                cdata-section-elements="sorted_number" />
           <xsl:strip-space elements="*"/>

           <!-- IDENTITY TRANSFORM (COPIES ALL CONTENT AS IS) -->
           <xsl:template match="@*|node()">
             <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
             </xsl:copy>
           </xsl:template>  

           <!-- SORT IMAGE CHILDREN IN EACH IMAGES NODE -->
           <xsl:template match="images">
             <xsl:copy>
                <xsl:apply-templates select="image">
                    <xsl:sort select="sorted_number" order="ascending" data-type="number"/>
                </xsl:apply-templates>
             </xsl:copy>
           </xsl:template>               
           </xsl:transform>';
$xsl->loadXML($xslstr);

// Configure the processor
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl); 

// Transform XML source
$newXml = $proc->transformToXML($doc);    
echo $newXml;

结果(通知<![CData[]]>被保留)

<?xml version="1.0" encoding="UTF-8"?>
<stuff>
  <other_nodes/>
  <images>
    <image>
      <sorted_number><![CDATA[1]]></sorted_number>
    </image>
    <image>
      <sorted_number><![CDATA[2]]></sorted_number>
    </image>
    <image>
      <sorted_number><![CDATA[3]]></sorted_number>
    </image>
  </images>
</stuff>

答案 2 :(得分:0)

在深入研究之前,真正需要保存排序状态吗?就像在数据库中一样,您总是可以在检索项目时对项目进行排序,这与您已编写的代码相同。

也就是说,在您的情况下,“更新”意味着删除所有<image>个节点并按顺序添加它们。

<强>更新
请参阅fusion3k的答案,没有必要删除节点,只需附加它们即可。我建议采用他的解决方案。

您正在使用SimpleXml,它不提供复制节点的方法。您将需要重新创建每个节点,子节点,属性。 您的XML看起来很简单,但我想这是一个示例,您的真实XML更复杂。然后使用DOM及其importNode()方法,它可以复制复杂节点,包括所有属性和子节点。

另一方面,SimpleXml对我来说感觉更容易,所以我将两者结合起来:

$xml = simplexml_load_string($x); // assume XML in $x

$images = $xml->xpath("/stuff/images/image");

usort($images, function ($a, $b){
    return strnatcmp($a->sorted_number, $b->sorted_number);
});

评论:

  • xpath()是将所有项目放入SimpleXml个对象数组的快捷方式。
  • $images现已排序,但我们无法删除原始节点,因为$images保存对这些节点的引用。

这就是我们需要将$images保存到新的临时文档的原因。

$tmp = new DOMDocument('1.0', 'utf-8');
$tmp->loadXML("<images />"); 

// add image to $tmp, then delete it from $xml
foreach($images as $image) { 
    $node = dom_import_simplexml($image); // make DOM from SimpleXml
    $node = $tmp->importNode($node, TRUE); // import and append in $tmp
    $tmp->getElementsByTagName("images")->item(0)->appendChild($node);
    unset($image[0]); // delete image from $xml
}  

评论:

  • 现在使用DOM,因为我可以使用importNode()
  • 复制节点
  • 此时,$tmp按所需顺序排列了所有<image>个节点,$xml没有。

要将节点从$tmp复制回$xml,我们需要将$xml导入DOM

$xml = dom_import_simplexml($xml)->ownerDocument;

foreach($tmp->getElementsByTagName('image') as $image) {
    $node = $xml->importNode($image, TRUE);
    $xml->getElementsByTagName("images")->item(0)->appendChild($node);
}  

// output...
echo $xml->saveXML();

在行动中看到它:https://eval.in/535800