我正在使用PHP5,我需要以下列形式转换XML:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<anotherNode>some text</anotherNode
<item label="a">some text</item>
<item label="b">some text</item>
</item>
</list>
这样的事情:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<anotherNode>some text</anotherNode>
<list> <!-- opening new wrapper node-->
<item label="a">some text</item>
<item label="b">some text</item>
</list> <!-- closing new wrapper node-->
</item>
</list>
正如您在上面所看到的,我需要为任何未被'list'节点包装的'item'节点添加一个包装器节点。
将源xml转换为目标xml有哪些可能的解决方案?
更新:
注1:任何单个或一组<item>
节点如果尚未包装,则需要由<list>
节点包装。
注2:需要维护内容的顺序。
注3:
如果<item>
之前和之后有<anotherNode>
个节点。
它应该改变这个:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<item label="a">some text</item>
<item label="b">some text</item>
<anotherNode>some text</anotherNode>
<item label="c">some text</item>
<item label="d">some text</item>
</item>
</list>
进入这个:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<list> <!-- opening new wrapper node-->
<item label="a">some text</item>
<item label="b">some text</item>
</list> <!-- closing new wrapper node-->
<anotherNode>some text</anotherNode>
<list> <!-- opening new wrapper node-->
<item label="c">some text</item>
<item label="d">some text</item>
</list> <!-- closing new wrapper node-->
</item>
</list>
谢谢,
答案 0 :(得分:4)
此转化:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item/item[1]">
<list>
<xsl:apply-templates mode="copy"
select=".| following-sibling::item"/>
</list>
</xsl:template>
<xsl:template match="item" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="item/item[not(position()=1)]"/>
</xsl:stylesheet>
应用于提供的XML文档:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<anotherNode>some text</anotherNode>
<item label="a">some text</item>
<item label="b">some text</item>
</item>
</list>
生成想要的正确结果:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<anotherNode>some text</anotherNode>
<list>
<item label="a">some text</item>
<item label="b">some text</item>
</list>
</item>
</list>
请注意:
使用和覆盖身份规则。
对某些元素的压制。
使用不同的模式处理某些元素。
<强>更新强>:
OP增加了额外的要求:
“如果在item
之前和之后有anothernode
个元素,那么每个item
元素组必须包含在单独的list
中
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kfollnonitem" match="item"
use="generate-id(preceding-sibling::*[not(self::item)][1])"/>
<xsl:key name="kprecnonitem" match="item"
use="generate-id(following-sibling::*[not(self::item)][1])"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(self::list)]/item[1]">
<list>
<xsl:apply-templates mode="copy"
select="key('kprecnonitem',
generate-id(following-sibling::*[not(self::item)][1])
)"/>
</list>
</xsl:template>
<xsl:template match=
"*[not(self::list) and item]/*[not(self::item)]">
<xsl:call-template name="identity"/>
<list>
<xsl:apply-templates mode="copy"
select="key('kfollnonitem', generate-id())"/>
</list>
</xsl:template>
<xsl:template match="item" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="item/item[not(position()=1)]"/>
</xsl:stylesheet>
对XML文档执行此转换时:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<item label="a">some text</item>
<item label="b">some text</item>
<anotherNode>some text</anotherNode>
<item label="c">some text</item>
<item label="d">some text</item>
</item>
</list>
产生了想要的正确结果:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<list>
<item label="a">some text</item>
<item label="b">some text</item>
</list>
<anotherNode>some text</anotherNode>
<list>
<item label="c">some text</item>
<item label="d">some text</item>
</list>
</item>
</list>
答案 1 :(得分:3)
此样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()[1]" />
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]" />
</xsl:template>
<xsl:template match="*[not(self::list)]
/item[not(preceding-sibling::*[1][self::item])]">
<list>
<xsl:call-template name="identity"/>
</list>
<xsl:apply-templates select="following-sibling::node()
[not(self::item)][1]" />
</xsl:template>
<xsl:template match="*[not(self::list)]
/item[not(following-sibling::*[1][self::item])]">
<xsl:copy>
<xsl:apply-templates select="@*|node()[1]" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
输出:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<anotherNode>some text</anotherNode>
<list>
<item label="a">some text</item>
<item label="b">some text</item>
</list>
</item>
</list>
此外,此样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kItemByFirstSibling"
match="item[preceding-sibling::*[1][self::item]]"
use="generate-id(preceding-sibling::item
[not(preceding-sibling::*[1][self::item])][1])"/>
<xsl:template match="@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(self::list)]/item"/>
<xsl:template match="*[not(self::list)]
/item[not(preceding-sibling::*[1][self::item])]"
priority="1">
<list>
<xsl:for-each select=".|key('kItemByFirstSibling',generate-id())">
<xsl:call-template name="identity"/>
</xsl:for-each>
</list>
</xsl:template>
</xsl:stylesheet>
注意:第一个样式表使用最细粒度的横向(它将在第一个item
之后包装任何节点)。第二个样式表完全递归的身份转换。
编辑:使用新输入解决新的requeriment,两个样式表输出:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<list>
<item label="a">some text</item>
<item label="b">some text</item>
</list>
<anotherNode>some text</anotherNode>
<list>
<item label="c">some text</item>
<item label="d">some text</item>
</list>
</item>
</list>
答案 2 :(得分:0)
您未在原始问题中解决此问题,因此可能不需要。但是如果输入有多个需要被包装的<item>
元素序列,那么它们被其他兄弟元素彼此分开,例如:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<item label="a">some text</item>
<item label="b">some text</item>
<anotherNode>some text</anotherNode>
<item label="c">some text</item>
<item label="d">some text</item>
</item>
</list>
我相信,先前的答案会将<item>
个元素混为一谈,改变它们的顺序:
<list>
<item label="(1)">some text</item>
<item label="(2)">
<list> <!-- opening new wrapper node-->
<item label="a">some text</item>
<item label="b">some text</item>
<item label="c">some text</item>
<item label="d">some text</item>
</list> <!-- closing new wrapper node-->
<anotherNode>some text</anotherNode>
</item>
</list>
你想要吗,或者你想分开包装它们,像这样吗?
<list>
<item label="(1)">some text</item>
<item label="(2)">
<list> <!-- opening new wrapper node-->
<item label="a">some text</item>
<item label="b">some text</item>
</list> <!-- closing new wrapper node-->
<anotherNode>some text</anotherNode>
<list> <!-- opening new wrapper node-->
<item label="c">some text</item>
<item label="d">some text</item>
</list> <!-- closing new wrapper node-->
</item>
</list>
如果是后者,最简单的方法是使用XSLT 2.0 <xsl:for-each-group group-adjacent="name()" />
构造。我不知道PHP 5是否有XSLT 2.0可用,但是如果你可以使用这样的东西,请参阅this good article。