如何修复XSLT以按预期正确地将XML转换为HTML?

如何修复XSLT以按预期正确地将XML转换为HTML?,xml,xslt,Xml,Xslt,我正在使用XSLT将XML转换为HTML XML是: <PARA> 1050 <EMPH STYLE="min_max"> <EMPH HL="LOW">-50</EMPH> <EMPH HL="HIGH">+50</EMPH> </EMPH>  min <EMPH BOLD="0" HL="HIGH" ITAL="0" SMALLCA

我正在使用XSLT将XML转换为HTML

XML是:

<PARA>
    1050
    <EMPH STYLE="min_max">
        <EMPH HL="LOW">-50</EMPH>
        <EMPH HL="HIGH">+50</EMPH>
    </EMPH>
     min
    <EMPH BOLD="0" HL="HIGH" ITAL="0" SMALLCAPS="0">-1</EMPH>
</PARA>

1050
-50
+50
闵
-1
如何将输出正确呈现为:

1050-50+50分钟-1

目前我获得了
1050分钟-50+50-1

我的XSLT是:

<xsl:template match="PARA"> 
    <xsl:value-of select="text()"/>
    <xsl:choose>
        <xsl:when test="EMPH">
            <xsl:apply-templates select="EMPH"/>
        </xsl:when>
    </xsl:choose>
</xsl:template>

<xsl:template match="EMPH">
    <xsl:choose>
        <xsl:when test="@BOLD=1"><b><xsl:value-of select="."/></b></xsl:when>
        <xsl:when test="@HL='HIGH'"><sup><xsl:value-of select="."/></sup></xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="."/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

我发现这是可行的:

<xsl:template match="PARA">
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="EMPH">
    <xsl:choose>
        <xsl:when test="@BOLD=1"><b><xsl:value-of select="text()"/></b></xsl:when>
        <xsl:when test="@HL='HIGH'"><sup><xsl:value-of select="text()"/></sup></xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="."/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

我想知道是否有比
更好的方法来处理
EMPH
元素的多个属性,因为这种方法必须将所有组合作为唯一的
检查来处理


例如,我必须将“bold”、“superscript”和“bold and superscript”作为三个不同的子句进行检查。

我将尝试使用递归模板处理属性,并在映射中保留从属性名称到HTML元素的映射(在自Saxon 9.8以来支持的XSLT中,您可以使用XPath 3.1映射,但在XSLT 2中,如果您需要使用相当旧的Saxon版本,您当然可以定义一些包含映射名称的XML结构的变量,例如

由于我猜SMALLCAPPS不能用简单的属性到元素名称映射进行转换,因此可能还需要编写更多的模板以允许更大的灵活性,下面使用一个基本模板进行递归处理,其他描述属性转换的更专门的模板用
调用它s至要素:

  <xsl:param name="att-map" as="map(xs:string, xs:string)"
    select="map { 'BOLD' : 'b', 'HL' : 'sup', 'ITAL' : 'i' }"/>

  <xsl:template match="PARA">
      <p>
          <xsl:apply-templates/>
      </p>
  </xsl:template>

  <xsl:template match="EMPH[@STYLE = 'min_max']/EMPH" priority="5">
      <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="@BOLD[. = 1] | @ITAL[. = 1] | @HL[. = 'HIGH']" mode="attributes-to-elements">
      <xsl:element name="{$att-map(local-name())}">
          <xsl:next-match/>
      </xsl:element>
  </xsl:template>

  <xsl:template match="@SMALLCAPS[. = 1]" mode="attributes-to-elements">
      <span style="font-variant: small-caps">
          <xsl:next-match/>
      </span>
  </xsl:template>

  <xsl:template match="@*" mode="attributes-to-elements">
      <xsl:param name="remaining-atts" tunnel="yes"/>
      <xsl:choose>
          <xsl:when test="$remaining-atts">
              <xsl:apply-templates select="head($remaining-atts)" mode="#current">
                  <xsl:with-param name="remaining-atts" tunnel="yes" select="tail($remaining-atts)"/>
              </xsl:apply-templates>
          </xsl:when>
          <xsl:otherwise>
              <xsl:apply-templates select="../node()"/>
          </xsl:otherwise>
      </xsl:choose>      
  </xsl:template>

  <xsl:template match="EMPH[@*[. = ('1', 'HIGH')]]">
      <xsl:apply-templates select="head(@*[. = ('1', 'HIGH')])" mode="attributes-to-elements">
          <xsl:with-param name="remaining-atts" tunnel="yes" select="tail(@*[. = ('1', 'HIGH')])"/>
      </xsl:apply-templates>
  </xsl:template>



最后,进行两步转换可能会更简单,第一步是规范化输入,您必须去除不指示某些特殊HTML样式或包装的任何属性,并将属性转换为规范化元素,然后第二步可以更轻松地使用基于元素的标准
应用模板
,以简化将嵌套输入转换为嵌套HTML。

那么确切的规则是什么呢?为什么
+50
没有转换为
+50
?您使用哪个版本的XSLT,哪个XSLT处理器?@MartinHonnen是的,根据我的假设,您是正确的,如果
HL=“HIGH”
是上标,那么
HL=“LOW”
应该用于下标。然后输出应该类似于
1010-50+50min-1
。但我不是为
STYLE=“min\u max”这样做的
。我正在使用Saxonica提供的xslt版本2.0和Saxon 9.1.0.8J。感谢您提供了一个漂亮的答案!我正在尝试为我的Saxon 9.1版本降级。我已经定义了映射,但现在我需要弄清楚如何迭代找到的每个属性以应用映射。我没有
head()
tail()
函数。@KevinM,正如我所说,
head($seq)
$seq[1]
子序列($seq,1,1)
tail($seq)
$seq[position()gt 1]
子序列($seq,2)
。至于映射,假设您有,例如
,我已经为XSLT 2解释了“降级”。
  <xsl:param name="att-map" as="map(xs:string, xs:string)"
    select="map { 'BOLD' : 'b', 'HL' : 'sup', 'ITAL' : 'i' }"/>

  <xsl:template match="PARA">
      <p>
          <xsl:apply-templates/>
      </p>
  </xsl:template>

  <xsl:template match="EMPH[@STYLE = 'min_max']/EMPH" priority="5">
      <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="@BOLD[. = 1] | @ITAL[. = 1] | @HL[. = 'HIGH']">
      <xsl:param name="atts"/>
      <xsl:element name="{$att-map(local-name())}">
          <xsl:choose>
              <xsl:when test="$atts">
                  <xsl:apply-templates select="head($atts)">
                      <xsl:with-param name="atts" select="tail($atts)"/>
                  </xsl:apply-templates>
              </xsl:when>
              <xsl:otherwise>
                  <xsl:apply-templates select="../node()"/>
              </xsl:otherwise>
          </xsl:choose>
      </xsl:element>
  </xsl:template>

  <xsl:template match="EMPH[@*[. = ('1', 'HIGH')]]">
      <xsl:apply-templates select="head(@*[. = ('1', 'HIGH')])">
          <xsl:with-param name="atts" select="tail(@*[. = ('1', 'HIGH')])"/>
      </xsl:apply-templates>
  </xsl:template>

  <xsl:mode on-no-match="text-only-copy"/>
  <xsl:param name="att-map" as="map(xs:string, xs:string)"
    select="map { 'BOLD' : 'b', 'HL' : 'sup', 'ITAL' : 'i' }"/>

  <xsl:template match="PARA">
      <p>
          <xsl:apply-templates/>
      </p>
  </xsl:template>

  <xsl:template match="EMPH[@STYLE = 'min_max']/EMPH" priority="5">
      <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="@BOLD[. = 1] | @ITAL[. = 1] | @HL[. = 'HIGH']" mode="attributes-to-elements">
      <xsl:element name="{$att-map(local-name())}">
          <xsl:next-match/>
      </xsl:element>
  </xsl:template>

  <xsl:template match="@SMALLCAPS[. = 1]" mode="attributes-to-elements">
      <span style="font-variant: small-caps">
          <xsl:next-match/>
      </span>
  </xsl:template>

  <xsl:template match="@*" mode="attributes-to-elements">
      <xsl:param name="remaining-atts" tunnel="yes"/>
      <xsl:choose>
          <xsl:when test="$remaining-atts">
              <xsl:apply-templates select="head($remaining-atts)" mode="#current">
                  <xsl:with-param name="remaining-atts" tunnel="yes" select="tail($remaining-atts)"/>
              </xsl:apply-templates>
          </xsl:when>
          <xsl:otherwise>
              <xsl:apply-templates select="../node()"/>
          </xsl:otherwise>
      </xsl:choose>      
  </xsl:template>

  <xsl:template match="EMPH[@*[. = ('1', 'HIGH')]]">
      <xsl:apply-templates select="head(@*[. = ('1', 'HIGH')])" mode="attributes-to-elements">
          <xsl:with-param name="remaining-atts" tunnel="yes" select="tail(@*[. = ('1', 'HIGH')])"/>
      </xsl:apply-templates>
  </xsl:template>