我整天都在为此作斗争:(
虽然我找到了类似问题的答案,但它们不会更新现有的 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
}
SimpleXML 对于您的任务来说太简单了。没有简单的方法来对节点进行重新排序。基本上,在排序例程之后,您必须重建<image>
节点,但是您内部有CDATA
,并且 SimpleXML 不能直接添加CDATA
值。
如果你想尝试这种方式,在这里你可以找到一个很酷的SimpleXML类扩展,它添加了CDATA
属性,而且这个解决方案也使用DOMDocument。
基本上,恕我直言,由于每个解决方案都需要 DOM,最好的方法是直接使用 DOMDocument,并最终在转换后使用 SimpleXML (重新)加载 XML:
$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();
IDEe演示
主例程将排序的节点作为子节点添加到现有的<images>
节点。请注意,没有必要预先删除旧子项:由于我们引用了相同的对象,因此实际上通过附加一个节点,我们将其从之前的位置删除。
如果要获取 SimpleXML 对象,可以在上述代码的末尾附加以下行:
$xml = simplexml_load_string( $dom->saveXML() );
考虑一个使用其<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和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>
在深入探讨之前,是否真的需要保存排序状态?就像在数据库中一样,您始终可以在检索项目时对项目进行排序,此处与您已经编写的代码相同。
也就是说,在您的情况下,"更新"意味着删除所有<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