How to apply more then one xsl-templates on one xm

2019-08-23 22:19发布

问题:

here is my problem:

I have HTML Document with CSS Styles. I need to move these styles into the elements in the body and remove them from style tag.

I made helper that makes xpath for each css style and value which needs to be put there. Then it generates XSL documents and applies it to the HTML.

The thing is sometimes there are multiple xsl-templates that needs to be applied to the same Node (same match). And just the first one is getting applied, others are ignored.

How i can apply them all?

Here is one example of the HTML:

<html lang="en">
<head>
   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=utf-8"/>
   <style type="text/css">
      .right-aligned { text-align: right !important; }
      .full-width { width: 100% !important; display: inline-block; }
   </style>
</head>
<body>
   <table class="header">
      <tr>
         <td class="icon">
            <img alt="Minor" src="Picture_SRC" />
         </td>
            <td>
               <div class="secondary full-width right-aligned">Blah: Test</div>
            </td>
         </tr>
      </table>
   </body>
</html>

And there is the XSL Transformations that needed to be done for this HTML:

<xsl:template match="//*[contains(@class,'right-aligned') and @style]">
   <xsl:attribute name="style">
      <xsl:value-of select="concat(., 'text-align: right !important;')"/>
   </xsl:attribute>
</xsl:template>
<xsl:template match="//*[contains(@class,'right-aligned') and not(@style)]">
   <xsl:copy>
      <xsl:attribute name="style">text-align: right !important;</xsl:attribute>
      <xsl:apply-templates select="@*| node()"/>
   </xsl:copy>
</xsl:template>
<xsl:template match="//*[contains(@class,'full-width') and @style]">
   <xsl:attribute name="style">
      <xsl:value-of select="concat(., 'width: 100% !important; display: inline-block;')"/>
    </xsl:attribute>
</xsl:template>
<xsl:template match="//*[contains(@class,'full-width') and not(@style)]">
   <xsl:copy>
      <xsl:attribute name="style">width: 100% !important; display: inline-block;</xsl:attribute>
      <xsl:apply-templates select="@*| node()"/>
   </xsl:copy>
</xsl:template>

Important thing: the HTML is not static and it changes ... so i can't make static XSL.

回答1:

Here is one way you could do it, by use of mode attributes, to apply each template one after another

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@class">
    <xsl:attribute name="style">
      <xsl:if test="@style">
        <xsl:value-of select="@style" />
        <xsl:text>;</xsl:text>
      </xsl:if>
      <xsl:apply-templates select="." mode="right-aligned" />
    </xsl:attribute>
  </xsl:template>

  <xsl:template match="@style" />

  <xsl:template match="@class" mode="right-aligned">
    <xsl:if test="contains(.,'right-aligned')">
      <xsl:text>text-align: right !important;</xsl:text>
    </xsl:if>
    <xsl:apply-templates select="." mode="full-width" />
  </xsl:template>

  <xsl:template match="@class" mode="full-width">
    <xsl:if test="contains(.,'full-width')">
      <xsl:text>width: 100% !important; display: inline-block;</xsl:text>
    </xsl:if>
  </xsl:template>  
</xsl:stylesheet>

If you could use XSLT 2.0, you could simplify this by use of priorities, instead of modes, and xsl:next-match to find the next matching template with a lower priority.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@class" priority="10">
    <xsl:attribute name="style">
      <xsl:if test="@style">
        <xsl:value-of select="@style" />
        <xsl:text>;</xsl:text>
      </xsl:if>
      <xsl:next-match />
    </xsl:attribute>
  </xsl:template>

  <xsl:template match="@style" />

  <xsl:template match="@class[contains(.,'right-aligned')]" priority="9">
    <xsl:text>text-align: right !important;</xsl:text>
    <xsl:next-match />
  </xsl:template>

  <xsl:template match="@class[contains(.,'full-width')]" priority="8">
    <xsl:text>width: 100% !important; display: inline-block;</xsl:text>
    <xsl:next-match />
   </xsl:template>  

  <xsl:template match="@class" /> <!-- Stop the built-in template applying -->
</xsl:stylesheet>


标签: html xml xslt