如何在XSLT中为条件样式嵌套元素?

时间:2019-09-20 14:57:18

标签: c# asp.net-core xslt

问题

如何利用XSL提供由运行的代码确定的条件格式(例如,表行之间的增加或减少)?

背景

编辑: 这是一个背景服务实现,使用ASP.NET Core作为长期运行的服务的主机运行,该服务对外部资源的运行状况进行审核。这可能是.NET Core缺少.NET Framework中存在的某些功能的问题,因此我认为值得一提

这个想法之所以开始,是因为我一直在寻找一种方法来制作一个简单的HTML模板,可以以一种简单而有效的方式将我的C#数据对象映射到该模板,而无需处理StringBuilder。在这种希望中,我发现this StackOverflow response表示XSLT是一个很好的解决方案,而无需执行完整的MVC方法。

这就是一切的开始(为简洁起见):

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="html" encoding="utf-8"/>
  <xsl:template match='/Email'>
    <html>
      <head>
        <style>
          <xsl:value-of select="Styles"/>
        </style>
      </head>
      <body>
        <h1>Total</h1>
        <table id="tblTotal" class="clsPositive">
          <tr>
            <th>Audit Date</th>
            <th>Source 1</th>
            <th>Source 2</th>
            ...
          </tr>
          <xsl:for-each select="Total/ArrayOfElement/AuditElement">
            <xsl:sort select="ReportDate" order="descending"/>
            <tr>
              <td><xsl:value-of select="ReportDate"/></td>
              <td><xsl:value-of select="Source1CountFormatted"/> (<xsl:value-of select="Source1GrowthFormatted"/>)</td>
              <td><xsl:value-of select="Source2CountFormatted"/> (<xsl:value-of select="Source2GrowthFormatted"/>)</td>
              ...
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

有了这个,我有了一个想法,可以根据表示的数据类型有条件地格式化增长率(您可以在表的class属性中看到它的开头,我用来确定是增加还是减少)是正面还是负面)。但是,当我尝试执行此操作时,Visual Studio告诉我XSL格式错误:

<td><xsl:value-of select="Source1CountFormatted"/> (<i class="<xsl:value-of select="Source1GrowthCss"/>"><xsl:value-of select="Source1GrowthFormatted"/> </i>)</td>

我还尝试了一种方法,其中“ ... GrowthFormatted”属性将返回其内联样式,但是在解析为要转换的XML树时,所有HTML都会立即转义。在来到这里之前,我最后的尝试是使用xsl:text方法,并且将disable-output-escaping:“ yes”。这导致了XSL的怪异(请注意两种不同的方法来查看它是否完全可以工作):

<td><xsl:value-of select="Source1CountFormatted"/> (<xsl:text disable-output-escaping="yes">&lt;</xsl:text>i class=<xsl:text disable-output-escaping="yes">&quot;</xsl:text><xsl:value-of select="Source1GrowthCssClass"/><xsl:text disable-output-escaping="yes">&quot;&gt;</xsl:text><xsl:value-of select="Source1GrowthFormatted"/><xsl:text disable-output-escaping="yes">&lt;</xsl:text>/i<xsl:text disable-output-escaping="yes">&gt;</xsl:text>)</td>
<td><xsl:value-of select="Source2CountFormatted"/> (<xsl:text disable-output-escaping="yes">&lt;i class=&quot;</xsl:text><xsl:value-of select="Source2GrowthCssClass"/><xsl:text disable-output-escaping="yes">&quot;&gt;</xsl:text><xsl:value-of select="Source2GrowthFormatted"/><xsl:text disable-output-escaping="yes">&lt;/i&gt;</xsl:text>)</td>

你们中间的鹰眼甚至可能注意到我使用了<xsl:text disable-output-escaping="yes">&lt;</xsl:text>的{​​{3}}提供的教科书示例,您可能会问,结果是什么?好吧,Visual Studio没错,因为嵌套元素不能与XSL一起很好地工作,并引发了以下异常:

XslLoadException: XSLT compile error. An error occurred at , (66, 84). ---> System.Xml.XmlException: '<', hexadecimal value 0x3C is an invalid attribute character. Line 66, poisition 84.

对于极其冗长的方法以及超级简单的方法,它们都导致生成以下HTML:

  ...
  <tr>
    <td>2019-09-19 12:30:40Z</td>
    <td>35,991,744 (&lt;i class="clsDecrease"&gt;0.00%&lt;/i&gt;)</td>
    <td>755,412 (&lt;i class="clsDecrease"&gt;0.00%&lt;/i&gt;)</td>
  ...

1 个答案:

答案 0 :(得分:1)

这里的语法是错误的:

<i class="<xsl:value-of select="Source1GrowthCss"/>">

要动态设置属性值,可以使用xsl:attribute,如下所示:

<i>
   <xsl:attribute name="class">
      <xsl:value-of select="Source1GrowthCss"/>
   </xsl:attribute>
</i>

但是这很漫长。理想的方法是使用“属性值模板”,它更干净

<i class="{Source1GrowthCss}">

在这里,花括号表示要对表达式求值,而不是按字面意思输出。