错误的属性值转换?
Wrong attribute value transformation?
输入xml文件:
<a key="777">
<desc key="1" attr1="# {../@key}" attr2="{@key} #" attr3="555" title="{@attr2}">
<b attr3="444">
<slot key="2" attr3="333" attr4="{../../@attr3}" title="{../../@attr3}+{../@attr3}-{@attr3}" >
<val key="3">
<fix key="4" title11="{../../@key} {../@key}" title="{@key}+{@title11}" >
<c></c>
</fix>
</val>
<numb key="5" title12="z{@key}z in {@key}" title="{@title12}+{@key}" titlew="{@title}+{@title12}">
</numb>
</slot>
</b>
</desc>
</a>
大括号指示属性的 XPath,您必须沿此 XPath 替换属性的值,例如,代替 {@key} 和 {../@key},值当前节点的关键属性被替换。 XSLT1.0 转换后的文件应该是这样的:
<a key="777">
<desc key="1" attr1="# 777" attr2="1 #" attr3="555" title="1 #">
<b attr3="444">
<slot key="2" attr3="333" attr4="555" title="555+444-333">
<val key="3">
<fix key="4" title11="2 3" title="4+2 3">
<c />
</fix>
</val>
<numb key="5" title12="z5z in 5" title="z5z in 5+5" titlew="z5z in 5+5+z5z in 5" />
</slot>
</b>
</desc>
</a>
现在这是 XSLT1.0 转换文件:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{name()}">
<xsl:call-template name="expand">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:attribute>
</xsl:template>
<xsl:template name="expand">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '{@')">
<xsl:value-of select="substring-before($text, '{')"/>
<xsl:variable name="name" select="substring-before(substring-after($text, '{@'), '}')" />
<xsl:call-template name="expand">
<xsl:with-param name="text" select="../@*[name()=$name]"/>
</xsl:call-template>
<xsl:call-template name="expand">
<xsl:with-param name="text" select="substring-after($text, '}')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($text, '{../@') ">
<xsl:value-of select="substring-before($text, '{')"/>
<xsl:variable name="name" select="substring-before(substring-after($text, '{../@'), '}')" />
<xsl:call-template name="expand">
<xsl:with-param name="text" select="../../@*[name()=$name]"/>
</xsl:call-template>
<xsl:call-template name="expand">
<xsl:with-param name="text" select="substring-after($text, '}')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($text, '{../../@') ">
<xsl:value-of select="substring-before($text, '{')"/>
<xsl:variable name="name" select="substring-before(substring-after($text, '{../../@'), '}')" />
<xsl:call-template name="expand">
<xsl:with-param name="text" select="../../../@*[name()=$name]"/>
</xsl:call-template>
<xsl:call-template name="expand">
<xsl:with-param name="text" select="substring-after($text, '}')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Xml 转换后的文件:
<a key="777">
<desc key="1" attr1="# 777" attr2="1 #" attr3="555" title="1 #">
<b attr3="444">
<slot key="2" attr3="333" attr4="555" title="333+333-333">
<val key="3">
<fix key="4" title11="3 3" title="4+3 3">
<c />
</fix>
</val>
<numb key="5" title12="z5z in 5" title="z5z in 5+5" titlew="z5z in 5+5+z5z in 5" />
</slot>
</b>
</desc>
</a>
错误是如果在输入文件节点的属性值中指定了相同的属性名,比如在title属性的Slot标签中花括号中三次@attr3,那么就是相同的值替换花括号:
<slot key="2" ... title="333+333-333">
应该是这样的:
<slot key="2" ... title="555+444-333">
哪里错了?
在纯 XSLT 1.0 中没有真正好的解决方案。
如果输入 XML 中的路径表达式仅限于一些预定义模式(如您的示例中所示),您可以通过以下方式获得预期结果:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{name()}">
<xsl:call-template name="expand">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:attribute>
</xsl:template>
<xsl:template name="expand">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '{') ">
<!-- text before reference -->
<xsl:value-of select="substring-before($text, '{')"/>
<!-- reference -->
<xsl:call-template name="evaluate">
<xsl:with-param name="path" select="substring-before(substring-after($text, '{'), '}')"/>
</xsl:call-template>
<!-- recursive call with text after reference -->
<xsl:call-template name="expand">
<xsl:with-param name="text" select="substring-after($text, '}')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="evaluate">
<xsl:param name="path"/>
<xsl:variable name="name" select="substring-after($path, '@')"/>
<xsl:call-template name="expand">
<xsl:with-param name="text">
<xsl:choose>
<xsl:when test="starts-with($path, '../../@')">
<xsl:value-of select="../../../@*[name()=$name]"/>
</xsl:when>
<xsl:when test="starts-with($path, '../@')">
<xsl:value-of select="../../@*[name()=$name]"/>
</xsl:when>
<xsl:when test="starts-with($path, '@')">
<xsl:value-of select="../@*[name()=$name]"/>
</xsl:when>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
更好的解决方案是升级到支持 XSLT 3.0 或其他动态评估机制的处理器 - 如 中所建议。
输入xml文件:
<a key="777">
<desc key="1" attr1="# {../@key}" attr2="{@key} #" attr3="555" title="{@attr2}">
<b attr3="444">
<slot key="2" attr3="333" attr4="{../../@attr3}" title="{../../@attr3}+{../@attr3}-{@attr3}" >
<val key="3">
<fix key="4" title11="{../../@key} {../@key}" title="{@key}+{@title11}" >
<c></c>
</fix>
</val>
<numb key="5" title12="z{@key}z in {@key}" title="{@title12}+{@key}" titlew="{@title}+{@title12}">
</numb>
</slot>
</b>
</desc>
</a>
大括号指示属性的 XPath,您必须沿此 XPath 替换属性的值,例如,代替 {@key} 和 {../@key},值当前节点的关键属性被替换。 XSLT1.0 转换后的文件应该是这样的:
<a key="777">
<desc key="1" attr1="# 777" attr2="1 #" attr3="555" title="1 #">
<b attr3="444">
<slot key="2" attr3="333" attr4="555" title="555+444-333">
<val key="3">
<fix key="4" title11="2 3" title="4+2 3">
<c />
</fix>
</val>
<numb key="5" title12="z5z in 5" title="z5z in 5+5" titlew="z5z in 5+5+z5z in 5" />
</slot>
</b>
</desc>
</a>
现在这是 XSLT1.0 转换文件:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{name()}">
<xsl:call-template name="expand">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:attribute>
</xsl:template>
<xsl:template name="expand">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '{@')">
<xsl:value-of select="substring-before($text, '{')"/>
<xsl:variable name="name" select="substring-before(substring-after($text, '{@'), '}')" />
<xsl:call-template name="expand">
<xsl:with-param name="text" select="../@*[name()=$name]"/>
</xsl:call-template>
<xsl:call-template name="expand">
<xsl:with-param name="text" select="substring-after($text, '}')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($text, '{../@') ">
<xsl:value-of select="substring-before($text, '{')"/>
<xsl:variable name="name" select="substring-before(substring-after($text, '{../@'), '}')" />
<xsl:call-template name="expand">
<xsl:with-param name="text" select="../../@*[name()=$name]"/>
</xsl:call-template>
<xsl:call-template name="expand">
<xsl:with-param name="text" select="substring-after($text, '}')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($text, '{../../@') ">
<xsl:value-of select="substring-before($text, '{')"/>
<xsl:variable name="name" select="substring-before(substring-after($text, '{../../@'), '}')" />
<xsl:call-template name="expand">
<xsl:with-param name="text" select="../../../@*[name()=$name]"/>
</xsl:call-template>
<xsl:call-template name="expand">
<xsl:with-param name="text" select="substring-after($text, '}')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Xml 转换后的文件:
<a key="777">
<desc key="1" attr1="# 777" attr2="1 #" attr3="555" title="1 #">
<b attr3="444">
<slot key="2" attr3="333" attr4="555" title="333+333-333">
<val key="3">
<fix key="4" title11="3 3" title="4+3 3">
<c />
</fix>
</val>
<numb key="5" title12="z5z in 5" title="z5z in 5+5" titlew="z5z in 5+5+z5z in 5" />
</slot>
</b>
</desc>
</a>
错误是如果在输入文件节点的属性值中指定了相同的属性名,比如在title属性的Slot标签中花括号中三次@attr3,那么就是相同的值替换花括号:
<slot key="2" ... title="333+333-333">
应该是这样的:
<slot key="2" ... title="555+444-333">
哪里错了?
在纯 XSLT 1.0 中没有真正好的解决方案。
如果输入 XML 中的路径表达式仅限于一些预定义模式(如您的示例中所示),您可以通过以下方式获得预期结果:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{name()}">
<xsl:call-template name="expand">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:attribute>
</xsl:template>
<xsl:template name="expand">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '{') ">
<!-- text before reference -->
<xsl:value-of select="substring-before($text, '{')"/>
<!-- reference -->
<xsl:call-template name="evaluate">
<xsl:with-param name="path" select="substring-before(substring-after($text, '{'), '}')"/>
</xsl:call-template>
<!-- recursive call with text after reference -->
<xsl:call-template name="expand">
<xsl:with-param name="text" select="substring-after($text, '}')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="evaluate">
<xsl:param name="path"/>
<xsl:variable name="name" select="substring-after($path, '@')"/>
<xsl:call-template name="expand">
<xsl:with-param name="text">
<xsl:choose>
<xsl:when test="starts-with($path, '../../@')">
<xsl:value-of select="../../../@*[name()=$name]"/>
</xsl:when>
<xsl:when test="starts-with($path, '../@')">
<xsl:value-of select="../../@*[name()=$name]"/>
</xsl:when>
<xsl:when test="starts-with($path, '@')">
<xsl:value-of select="../@*[name()=$name]"/>
</xsl:when>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
更好的解决方案是升级到支持 XSLT 3.0 或其他动态评估机制的处理器 - 如