Reputation: 391
I'm editing a large Microsoft InfoPath document. InfoPath uses XSLT internally for form layout. The GUI is very cumbersome, and I want to speed up the process by editing the internal stylesheets using XSLT. So here's an interesting XSLT problem: how do you apply one XSLT stylesheet to another? Here's a start:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="select">
...
<xsl:template>
</xsl:stylesheet>
That is, I copy everything, but change the <select> elements. In the "select" template, I want a chunk of XSLT. However, I don't want that XSLT to be processed. But I do want it to have the XSLT namespace, so that the output still works as a stylsheet.
I guess I could set the namespace URI of the XSLT chunk to anything, and then change it to the XSLT namespace URI afterwards, but that takes an extra step. Is there a better way to do this?
Upvotes: 2
Views: 272
Reputation: 122414
This is the purpose of <xsl:namespace-alias>
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xslout="urn:dummy"
exclude-result-prefixes="xslout">
<xsl:namespace-alias stylesheet-prefix="xslout" result-prefix="xsl" />
<xsl:output method="xml" indent="yes"/>
<!-- ... -->
<xsl:template match="select">
<xslout:value-of select="{@valueXpath}" />
<xsl:template>
</xsl:stylesheet>
Here, any xslout:
elements in the stylesheet will become xsl:
in the output, and any attribute value templates in such elements will be processed by this stylesheet (so in the above a <select valueXpath="@foo" />
would generate an <xsl:value-of select="@foo" />
as output). If you want to put an AVT in the output you have to double the braces
<xslout:element name="{{@elementName}}" />
If you already have chunks of <xsl:...>
stuff that you don't want to rewrite then you can use a different prefix, e.g.
<?xml version="1.0" encoding="utf-8"?>
<sty:stylesheet xmlns:sty="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xsl="urn:dummy"
exclude-result-prefixes="xsl">
<sty:namespace-alias stylesheet-prefix="xsl" result-prefix="sty" />
<sty:template match="select">
<xsl:value-of select="{@valueXpath}" />
<sty:template>
though the note about AVTs still stands.
A possibly simpler alternative if you just want to copy a block of XSLT code into the select
element verbatim (rather than generating it based on the select
element's contents) would be to put that XSLT code in another file and use the document
function
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="select">
<xsl:copy-of select="document('to-insert.xml')/*/node()" />
<xsl:template>
</xsl:stylesheet>
where to-insert.xml
contains something like
<data xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:for-each select="*">
<!-- ... -->
</xsl:for-each>
</data>
Upvotes: 2
Reputation: 1920
For outputting XSLT elements to the result XML document you cannot use the elements as they are, because as you said they are going to be processed as XSLT. However you can use the element <xsl:element> to output XSLT elements.
For example,
<xsl:template match="select">
<xsl:element name="xsl:apply-templates">
<xsl:attribute name="select">*</xsl:attribute>
</xsl:element>
<xsl:template>
That would output for each select element,
<xsl:apply-templates select="*" />
You can use <xsl:element> and <xsl:attribute> to build the XSLT code that you want to be outputted.
UPDATE: according to your scenario you have a large XSLT code which needs to be outputted inside the select template, so it would be time consuming rewriting all the code with <xsl:element> and <xsl:attribute>. But you do not need to spend that much time, because you can write an XSL template to convert the XSLT which goes inside the template matching select into the XSL code written with <xsl:element> and <xsl:attribute>.
The following code would do that:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:myname="http://www.myname.co.uk/def">
<xsl:output method="xml" indent="no"/>
<!-- Copy by default all elements which do not belong to the xsl namespace -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<!-- Match elements which their namespace match the namespace matched with xsl -->
<xsl:template match="xsl:*">
<!-- Transform all the elements to xsl:element name="xsl:element" nodes -->
<xsl:element name="xsl:element">
<xsl:attribute name="name">
<xsl:value-of select="name()" />
</xsl:attribute>
<xsl:apply-templates select="@*" mode="xsl-attr" />
<xsl:apply-templates select="node()" />
</xsl:element>
</xsl:template>
<!-- Transform all the attributes to xsl:element name="xsl:attribute" -->
<xsl:template match="@*" mode="xsl-attr">
<xsl:element name="xsl:attribute">
<xsl:attribute name="name">
<xsl:value-of select="local-name()" />
</xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
So you can copy and paste the code that you get from that transformation into the template matching the select elements.
Upvotes: 2