我有一个xml文档,看起来像这样:
<p>
<c1 />
<c2 />
</p>
子元素c1和c2是可选的,但对于处理步骤,我需要它们存在。所以我试图创建一个xslt样式表来将它们添加为空元素(子元素的顺序无关紧要)。
这是我的样式表:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p[not(c1)]">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<c1 />
</xsl:copy>
</xsl:template>
<xsl:template match="p[not(c2)]">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<c2 />
</xsl:copy>
</xsl:template>
这样可以正常工作,只要缺少其中一个子元素即可。但如果两者都丢失,则只创建c1。如何防止这种情况并强制创建c1和c2(实际上,大约有10个孩子)?
感谢。 斯特
答案 0 :(得分:4)
不太喜欢xsl:if
方法。只要您不介意更改节点的顺序,这里可能会更好:
<!--generic copy rule-->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates" select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!--copy rules specific to p-->
<xsl:template match="p">
<xsl:copy>
<xsl:apply-templates select="not(self::c1|self::c2)"/>
<c1><xsl:apply-templates select="c1/*"/></c1>
<c2><xsl:apply-templates select="c2/*"/></c2>
</xsl:copy>
<xsl:template>
基本思想是明确生成c1和c2节点及其内容(如果存在),如果不存在则为空。
答案 1 :(得分:2)
我会这样做:
<xsl:template match="p">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<xsl:if test="not(c1)">
<c1 />
</xsl:if>
<xsl:if test="not(c2)">
<c2 />
</xsl:if>
</xsl:copy>
</xsl:template>
如果你有一个较长的可能子节点列表,你可以将它们放在一个变量中,而不是使用for-each
而不是单个if
:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:variable name="childrenFragment">
<c1/>
<c2/>
</xsl:variable>
<xsl:template match="p">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<xsl:variable name="this" select="."/>
<xsl:for-each select="msxsl:node-set($childrenFragment)/*">
<xsl:variable name="localName" select="local-name()"/>
<xsl:if test="not($this/*[local-name()=$localName])">
<xsl:element name="{$localName}"/>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
只需在childrenFragment
变量中添加所需的所有元素。
(msxsl:node-set
内容是特定于Microsoft的,如果您使用的是其他XSLT处理器,则需要稍微不同的内容)
答案 2 :(得分:2)
这是一个通用的XSLT 1.0解决方案(可以用于N个待添加元素),它不使用任何显式XSLT条件指令或任何xsl:element
指令:< / p>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vrtfNodesToAdd">
<c1/><c2/>
</xsl:variable>
<xsl:variable name="vNodesToAdd" select=
"ext:node-set($vrtfNodesToAdd)/*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<xsl:variable name="vCurrentP" select="."/>
<xsl:copy>
<xsl:apply-templates/>
<xsl:for-each select="$vNodesToAdd">
<xsl:copy-of select=
"self::node()[not($vCurrentP/*[name() = name(current())])]"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
对以下XML文档应用此转换时:
<t>
<p>
</p>
<p>
<c1/>
</p>
<p>
<c2/>
</p>
<p>
<c1/><c2/>
</p>
</t>
产生了想要的正确结果:
<t>
<p>
<c1 />
<c2 />
</p>
<p>
<c1 />
<c2 />
</p>
<p>
<c2 />
<c1 />
</p>
<p>
<c1 />
<c2 />
</p>
</t>