Question

Lets say I have written a simple xslt which transforms from one message to another is there any way I can automatically generate the inverse?

Original XSLT

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
      <xsl:template match="/">
      <hello>
         <xsl:for-each select="/hello/greeting">        
        <H1>
              <xsl:value-of select="."/>
        </H1>
       </xsl:for-each>
       </hello>
      </xsl:template>
    </xsl:stylesheet>

Desired XSLT

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
   <hello>
   <xsl:for-each select="/hello/H1">        
      <greeting>
          <xsl:value-of select="."/>
      </greeting>
   </xsl:for-each>
   </hello>
  </xsl:template>
</xsl:stylesheet>
Était-ce utile?

La solution

No, there isn't a generic way to reverse an XSLT. In fact, I'd say the majority of XSLTs are not reversible. It is possible, however, to design your example above so that it can be run in the forward direction or reverse direction by changing a parameter value:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:exslt="http://exslt.org/common">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:param name="direction" select="'forward'" />

  <xsl:variable name="mappingNF">
    <map from="greeting" to="H1" />
    <map from="pleasantry" to="H2" />
  </xsl:variable>
  <xsl:variable name="mapping" select="exslt:node-set($mappingNF)" />

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

  <xsl:template match="*" mode="copy">
    <xsl:call-template name="Copy" />
  </xsl:template>

  <xsl:template match="*">
    <xsl:variable name="mappingItem"
                  select="$mapping/*[$direction = 'forward' and @from = local-name(current()) or
                                 $direction = 'reverse' and @to = local-name(current())]" />

    <xsl:apply-templates select="current()[$mappingItem]" mode="rename">
      <xsl:with-param name="mappingItem" select="$mappingItem" />
    </xsl:apply-templates>
    <xsl:apply-templates select="current()[not($mappingItem)]" mode="copy" />
  </xsl:template>

  <xsl:template match="*" mode="rename">
    <xsl:param name="mappingItem" />

    <xsl:element name="{$mappingItem/@to[$direction = 'forward'] |
                        $mappingItem/@from[$direction = 'reverse']}">
      <xsl:apply-templates select ="@* | node()" />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

The mappingNF indicates the correspondence between node names, and this can be augmented with as many maps as needed. The direction of the conversion can be switched back and forth by changing the value of the direction parameter between "forward" and "reverse".

Autres conseils

Perhaps there's an interesting exercise for a computer science student to identify whether there's a class of XSLT transformations that is reversible. This means of course that the transformation would have to be lossless. The most obvious candidates are transformations that do nothing other than rename the elements, but even then I think it would be quite hard (without knowledge of the source document vocabulary and structure) to prove that the stylesheet doesn't map two different input names to the same output name. So I think it's going to end up being a rather small set.

In practice to be useful one would also want to consider transformations that add redundant information, for example an HTML header, and perhaps transformations where two input elements map to the same output element but with different attributes (say div class="X" where X enables the input element name to be reconstituted).

I do not think there is a way to automatically generate one for reverse direction

But, you can reuse most of the stuff in current XSL if what template does is the same

In your example it would be

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
  <hello>
     <xsl:for-each select="/hello/H1 | /hello/greeting">       
    <H1>
          <xsl:value-of select="."/>
    </H1>
   </xsl:for-each>
   </hello>
  </xsl:template>
</xsl:stylesheet> 
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top