我一整天都在和这个作斗争:(
虽然我找到了类似问题的答案,但他们没有更新现有的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
}
答案 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