我的任务是编写一些XSLT 2.0来将XML文档转换为另一个XML文档。我对XSLT比较陌生,但是在我做这个的时候我已经学到了很多东西。在此期间,我不得不映射简单的值,即002 - > TH等。这对于小于10个值的小列表来说很好,我使用了xsl:choose。但是,我需要将300个值从一个列表映射到另一个列表,反之亦然。每个列表都有一个值和文本描述。这两个列表值并不总是直接映射,因此我可能需要比较文本描述并在必要时使用默认值。
我有两个问题的解决方案:
使用xsl:choose:如果其中一个列表发生变化,我认为这可能很慢并且很难更新。
拥有一个XML文档,其中包含每个列表项之间的关系。我会使用XPath表达式来检索关联的值:这是我首选的解决方案,因为我相信它更易于维护且更易于更新。虽然我不确定它是否有效。
我应该使用什么解决方案,我的建议之一,还是有更好的方法来映射这些值?
答案 0 :(得分:4)
这是 XSLT 2.0解决方案。
<input>
<data>001</data>
<data>002</data>
<data>005</data>
</input>
<map>
<default>?-?-?</default>
<input value="001">RZ</input>
<input value="002">TH</input>
<input value="003">SC</input>
</map>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pmapFile"
select="'C:/temp/deleteMap.xml'" />
<xsl:variable name="vMap"
select="document($pmapFile)" />
<xsl:variable name="vDefault"
select="$vMap/*/default/text()" />
<xsl:key name="kInputByVal" match="input"
use="@value" />
<xsl:template match="/*">
<output>
<xsl:apply-templates/>
</output>
</xsl:template>
<xsl:template match="data">
<data>
<xsl:sequence select=
"(key('kInputByVal', ., $vMap)[1]/text(),
$vDefault
)[1]
"/>
</data>
</xsl:template>
</xsl:stylesheet>
<output>
<data>RZ</data>
<data>TH</data>
<data>?-?-?</data>
</output>
请注意以下:
使用document()
函数访问“映射”xml文档,该文档存储在单独的XML文件中。
使用<xsl:key/>
和XSLT 2.0 key()
函数来确定和访问每个相应的输出值。第三个参数指定必须访问和索引的xml文档。
答案 1 :(得分:2)
以下是使用<xsl:key>
并按照您的方法二执行操作的方法。
示例输入文件(data.xml):
<?xml version="1.0" encoding="utf-8"?>
<input>
<data>001</data>
<data>002</data>
<data>005</data>
</input>
示例映射文件(map.xml):
<?xml version="1.0" encoding="utf-8"?>
<map default="??">
<entry key="001">RZ</entry>
<entry key="002">TH</entry>
<entry key="003">SC</entry>
</map>
示例XSL样式表,解释如下:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:param name="map-file" select="string('map.xml')" />
<xsl:variable name="map-doc" select="document($map-file)" />
<xsl:variable name="default-value" select="$map-doc/map/@default" />
<xsl:key name="map" match="/map/entry" use="@key" />
<xsl:template match="/input">
<output>
<xsl:apply-templates select="data" />
</output>
</xsl:template>
<xsl:template match="data">
<xsl:variable name="raw-value" select="." />
<xsl:variable name="mapped-value">
<xsl:for-each select="$map-doc">
<xsl:value-of select="key('map', $raw-value)" />
</xsl:for-each>
</xsl:variable>
<data>
<xsl:choose>
<xsl:when test="$mapped-value = ''">
<xsl:value-of select="$default-value" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$mapped-value" />
</xsl:otherwise>
</xsl:choose>
</data>
</xsl:template>
</xsl:stylesheet>
这是做什么的:
document()
打开map.xml
,将生成的节点集保存为变量<xsl:key>
以对抗“地图”节点集<xsl:for-each>
用作循环,而是在调用key()
函数之前切换执行上下文的方法 - 否则key()
将对“数据”文档起作用并且不返回任何内容key()
函数的相应节点,将其保存在变量<xsl:apply-templates>
)整齐的<xsl:for-each>
技巧归功于Jeni Tennison,他在XSL邮件列表中描述了这项技术。一定要阅读帖子。
针对data.xml运行样式表的输出:
<?xml version="1.0" encoding="utf-8"?>
<output>
<data>RZ</data>
<data>TH</data>
<data>??</data>
</output>
所有这些都是XSLT 1.0。我确信存在一个更好/更优雅的版本,它利用了XSLT 2.0提供的优势,但不幸的是我对XSLT 2.0并不十分熟悉。也许其他人发布了更好的解决方案。
通过Dimitre Novatchev在评论中的暗示,我能够创建一个相当短且更优选的样式表:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:param name="map-file" select="string('map.xml')" />
<xsl:variable name="map-doc" select="document($map-file)" />
<xsl:variable name="default" select="$map-doc/map/default[1]" />
<xsl:key name="map" match="/map/entry" use="@key" />
<xsl:template match="/input">
<output>
<xsl:apply-templates select="data" />
</output>
</xsl:template>
<xsl:template match="data">
<xsl:variable name="raw-value" select="." />
<data>
<xsl:for-each select="$map-doc">
<xsl:value-of select="(key('map', $raw-value)|$default)[1]" />
</xsl:for-each>
</data>
</xsl:template>
</xsl:stylesheet>
但是,这个需要稍微不同的映射文件才能在XSLT 1.0中使用:
<?xml version="1.0" encoding="utf-8"?>
<map>
<entry key="001">RZ</entry>
<entry key="002">TH</entry>
<entry key="003">SC</entry>
<!-- default entry must be last in document -->
<default>??</default>
</map>