3

我的任务是编写一些 XSLT 2.0 来将一个 XML 文档转换为另一个 XML 文档。我对 XSLT 比较陌生,但在我做这件事的日子里我学到了很多东西。在此期间,我不得不映射简单的值,即 002 -> TH 等。这对于少于 10 个值的小列表来说很好,我使用了 xsl:choose。但是,我需要将 300 多个值从一个列表映射到另一个列表,反之亦然。每个列表都有一个值和文本描述。这两个列表值并不总是直接映射,因此我可能必须比较文本描述并在必要时使用默认值。

我有两个解决这个问题的方法:

  1. 使用 xsl:choose: 如果任何一个列表发生更改,我认为这可能会很慢并且可能难以更新。

  2. 拥有一个 XML 文档与每个列表项之间的关系。我会使用 XPath 表达式来检索关联的值:这是我的首选解决方案,因为我相信它更易于维护且更易于更新。虽然我不确定它是否有效。

我应该使用什么解决方案,我的建议之一,还是有更好的方法来映射这些值?

4

2 回答 2

4

这是一个XSLT 2.0 解决方案

源 XML 文件:

<input>
  <data>001</data>
  <data>002</data>
  <data>005</data>
</input>

“映射”xml文件:

<map>
  <default>?-?-?</default>
    <input value="001">RZ</input>
    <input value="002">TH</input>
    <input value="003">SC</input>
</map>

XSLT 转换:

<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>

请注意以下几点

  1. 使用该document()函数访问“映射”xml 文档,该文档存储在单独的 XML 文件中。

  2. 使用<xsl:key/>和 XSLT 2.0key()函数来确定和访问每个对应的输出值。第三个参数指定必须访问和索引的 xml 文档。

于 2009-01-20T14:48:02.960 回答
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>
于 2009-01-20T00:43:35.427 回答