如何将 translate() 应用于 XSLT 中的属性值?

How can I apply translate() to an attribute value in XSLT?

我有一个 returns XML 数据的函数,我想为它编写一个测试来检查它是否 returns 预期的结果。 XML 数据如下所示:

<Session Id="OF1qxev4OSZcIbsS">
  <User Id="10001">
    <LoginName>SelfTest</LoginName>
    <RealName>SelfTest User</RealName>
  </User>
  <StartTime>2015-11-13T13:01:59Z</StartTime>
  <EndTime>2015-11-13T15:01:59Z</EndTime>
</Session>

XML 包含的数据在每次请求时都不同,因此在与预期结果进行比较之前,我使用 XSLT 将变量数据(会话 ID 属性和 StartTime/EndTime 节点的内容)替换为一些固定模式。 XSL 文件如下所示:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  <xsl:output method="xml" encoding="utf-8" omit-xml-declaration="yes" indent="no"/>

  <xsl:variable name="uid-chars">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789</xsl:variable>
  <xsl:variable name="uid-mask" >IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII</xsl:variable>

  <xsl:variable name="timestamp-nums">0123456789</xsl:variable>
  <xsl:variable name="timestamp-mask">DDDDDDDDDD</xsl:variable>

  <xsl:template match="*">
    <xsl:copy>
      <!-- copy attributes -->
      <xsl:apply-templates select="@*"/>
      <!-- copy child elements -->
      <xsl:apply-templates select="*|text()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="text()">
    <!-- trim whitespace -->
    <xsl:value-of select="normalize-space(.)"/>
  </xsl:template>

  <!-- normalize uids -->
  <xsl:template match="@Id">
    <xsl:copy>
      <xsl:attribute name="Id">
        <xsl:value-of select="translate(., $uid-chars, $uid-mask)"/>
      </xsl:attribute>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="StartTime|EndTime">
    <xsl:copy>
      <xsl:apply-templates mode="timestamp"/>
    </xsl:copy>
  </xsl:template>

  <!-- normalize timestamps -->
  <xsl:template mode="timestamp" match="text()">
    <xsl:value-of select="translate(., $timestamp-nums, $timestamp-mask)"/>
  </xsl:template>

</xsl:stylesheet>

预期的输出如下所示:

<Session Id="IIIIIIIIIIIIIIII">
  <User Id="10001">
    <LoginName>SelfTest</LoginName>
    <RealName>SelfTest User</RealName>
  </User>
  <StartTime>DDDD-DD-DDTDD:DD:DDZ</StartTime>
  <EndTime>DDDD-DD-DDTDD:DD:DDZ</EndTime>
</Session>

不幸的是,当前 XSL 以上规范化仅适用于 StartTime/EndTime 值,但不适用于会话 ID:

<Session Id="OF1qxev4OSZcIbsS">
  <User Id="10001">
    <LoginName>SelfTest</LoginName>
    <RealName>SelfTest User</RealName>
  </User>
  <StartTime>DDDD-DD-DDTDD:DD:DDZ</StartTime>
  <EndTime>DDDD-DD-DDTDD:DD:DDZ</EndTime>
</Session>

将 translate() 函数应用于属性值的正确方法是什么?

注意:我在测试中使用了 MSXML6 的 XSLT 引擎。

您不应在与 Id 属性匹配的模板中使用 xsl:copy,因为那样只会按原样复制属性。您现有的 xsl:attribute 将被忽略,因为您无法将属性添加到此复制的属性上。

只需从模板中删除 xsl:copy,然后创建一个全新的属性

<xsl:template match="@Id">
   <xsl:attribute name="Id">
     <xsl:value-of select="translate(., $uid-chars, $uid-mask)"/>
   </xsl:attribute>
</xsl:template>

请注意,这将通过 User 元素的 Id 以及 Session 元素进行更新。如果您确实想将其限制为仅 Session 属性,则可以改用这两个模板:

<xsl:template match="Session/@Id">
   <xsl:attribute name="Id">
      <xsl:value-of select="translate(., $uid-chars, $uid-mask)"/>
   </xsl:attribute>
</xsl:template>

<xsl:template match="@Id">
    <xsl:copy />
</xsl:template>