Dynamic XSL file - xslt

I have 3 XSL files which have paths in them to something like C:\templates\Test\file.pdf
This path isn't always going to be the same and rather than having it hard coded in the XSL, I'd like it so that the path C:\templates\test\ is replaced with a tag [BASEPATH] and when I read in the xsl file into the XSLTransform object (yes I know it's been deprecated, I may move over to the XSLCompiledTransform at the same time), I'd like the tag [BASEPATH] to be replaced with the absolute file path of the web folder (or Server.MapPath("~") seeing as it is in .net)
I thought I may be able to make an XSLLoader aspx page which takes the name of the XSL file through the querystring and then returns the XSL file via xml content-type. When I try this, I get a 503 error though so I'm not sure if you can pass urls like this into the XSLTransform.Load method.
Any ideas what to do?

Have you looked at XSL parameters?
<xsl:param name="basepath" select="'C:\Users\Graeme\'" />
<xsl:value-of select="document(concat($basepath, 'test.pdf'))" />
Then, most decent XSLT engines have a way to set a root level parameter from outside.

Related

Can I force the XSLT collection function to treat all files as XSL?

I'm currently building an XSLT stylesheet used to document other XSLT stylesheets in a series of folders and sub-folders. My code pulls out specific details about variables, functions, etc and renders it in an output format. The sheets being read are created by a 3rd party product. Most of them have an XSL extension but some of them are proprietary extensions. I have some files with a DTCBS extension but they are just XSL stylesheets.
I'm currently loading the content of these files into a variable using the XSLT function "collection" as follows:
<xsl:variable name="Collection" select="collection(concat('file:///', encode-for-uri(replace($filePath, '\\', '/')),'?select=*.(xsl|dtcbs|xml);recurse=yes'))" as="node()*"/>
The variable works just fine if I use XSL|XML. But if I include the DTCBS extension, the variable blows up citing "the supplied value is xs:base64Binary".
If I manually put the xml declaration line at the top of my DTCBS file, the variable works fine. Those DTCBS files are auto-generated without the declaration line so I can't fix that, nor can I manually edit them each time I want to run my documenter code.
From what I can tell, because it's not an XSL extension, and the XML declaration line isn't present, the XSLT parser thinks it's base64 when it isn't.
I'm using Saxon as my XSLT parser and the Saxon documentation says it uses file extensions and http headers to detect the file type.
Does anyone know if there is a way to force collection() to treat every file as an XSL?
Tried adding the XML declaration line in the DTCBS file. This did correct the issue but I can't do this in all cases as I am trying to automate the entire thing.
I also renamed the DTCBS extension to XSL and the problem went away as well.
As well as Martin's suggestion, you can register content types with the Saxon configuration:
processor.getUnderlyingConfiguration()
.registerFileExtension("dtcbs", "application/xml");
This has been available since Saxon 9.7.
Try to add e.g. content-type=application/xml e.g. '?select=*.(xsl|dtcbs|xml);recurse=yes;content-type=application/xml'.

find other files in same directory as XML file

I'm trying to get content from another XML file in the same directory as my XML file. However, I don't know how to get the uri of the source XML. The XSLT keeps relating to its own directory.
How can I get the URI of the source XML?
I would recommend document-uri() rather than base-uri(). It will usually be the same, but base-uri() is affected by the xml:base attribute and by use of XML external entities, while document-uri() is not.
You can use the base-uri() function:
<!-- your external XML -->
<xsl:variable name="doc" select="document('http://www.xyz.com./path/your-doc.xml')"/>
<!-- the base URI of your external XML -->
<xsl:variable name="doc-base-uri" select="base-uri($doc)"/>
I found an answer that worked for me.
I got it using base-uri(.).

Include user control .ascx into xslt

I've created .ascx user control and I'm trying to find a way for including it into xslt rendering. How can I do this? I'm doing it for Sitecore. I thought maybe create a placeholder, but placeholders cannot be defined in renderings. I appreciate any help you can provide.
It's not possible to include ASCX file into xslt file because: XSLT transforms XML to HTML or to XML or to plain text but not to ASP.NET pages.
You can include xslt file into ascx but not ascx into xslt file. The best way is to change your xslt file into ascx file, and to include there with placeholders or directly .
I'd suggest to avoid using XSLT.
They seem pretty easy to use, but it's really hard to refactor the code.
Well, it's not possible to call user controls(.ascx) directly from XSLT files. However depending upon what you want to achieve, you can call .net methods, called XSLT extension methods, from XSLT file. For instance, you may need to write code similar to below to call custom .Net GetData() method.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:customObject="urn:yourNamespace">
<new-data>
<xsl:value-of select="customObject:GetData()"/>
</new-data>
...
Of course the type needs to be registered before it can be used. Type registration can be done into web.config or dynamically by calling AddExtensionObjectMethod of XSLTArgumentList class.
Sitecore offers XSLT extension controls too and unlike extension methods, it isn't a .net feature. XSL extension controls are XML elements in XSL renderings that correspond to .NET classes. For example, the XSL extension control corresponds to the
Sitecore.Web.UI.XslControls.Text .NET class. It will be consumed something like this in XSLT file:
XSL extension controls are standalone elements in the XSL code.
To register a custom type, add following to element in web.config:
<extension mode="on" type="NamespaceName.ClassName, AssemblyName" namespace="http://www.w3.org/1999/XSL/Transform" singleInstance="true"/>
Reference: http://sdn.sitecore.net/upload/sitecore6/64/presentation_component_xsl_reference_sc62-64-a4.pdf

how to use wicket XsltTransformerBehavior when I have multiple .xsl files

I'm working part of a project that needs to display a .xml file with .xsl files.
Here's part of the code:
FILE_PATH = "myxsl.xsl"
...
Label content = new Label("content",xmlContent);
content.setEscapeModelStrings(false);
content.add(new XsltTransformerBehavior(FILE_PATH));
add(content);
...
Currently the page works if I use only one .xsl file. However, because the .xsl files I need to deal with can be really long, they are separated into several components.
for example, I will have
mymain.xsl, head.xsl, tables.xsl
the mymain.xsl has inclusion of the other like this
<xsl:include href="head.xsl"/>
<xsl:include href="tables.xsl"/>
I tried to set FILE_PATH to mymain.xsl, but it didn't work. The program can find mymain.xsl but cannot compile the stylesheet because it cannot find head.xsl and tables.xsl
I've been searching for a long time but still have no clue how to do this. Really appreciate any help. Thanks in advance.
I looked at the documentation of XsltTransformerBehavior, and it looks like it doesn't expose the functionality to set a custom URI resolver (which is basically what you want to do).
However, in the case that no-one else has a better solution, I can propose a workaround: Write an XSLT that merges your XSLT files into one file. See here for an XSLT that does this. If you're using Maven as build system, you can do this automatically in the generate-resources phase using the transform goal of the Maven XML Plugin. You can then associate the XsltTransformerBehavior with the single-file XSLT which will be available at run-time.

XSLT workflow with variable number of source files

I have a bunch of XML files with a fixed, country-based naming schema: report_en.xml, report_de.xml, report_fr.xml, etc. Now I want to write an XSLT style sheet that reads each of these files via the document() XPath function, extracts some values and generates one XML files with a summary. My question is: How can I iterate over the source files without knowing the exact names of the files I will process?
At the moment I'm planning to generate an auxiliary XML file that holds all the file names and use the auxiliary XML file in my stylesheet to iterate. The the file list will be generated with a small PHP or bash script. Are there better alternatives?
I am aware of XProc, but investing much time into it is not an option for me at the moment. Maybe someone can post an XProc solution. Preferably the solution includes workflow steps where the reports are downloaded as HTML and tidied up :)
I will be using Saxon as my XSLT processor, so if there are Saxon-specific extensions I can use, these would also be OK.
You can use the standard XPath 2.x collection() function, as implemented in Saxon 9.x
The Saxon implementation allows a search pattern to be used in the string-Uri argument of the function, thus you may be able to specify after the path of the directory a pattern for any filename starting with report_ then having two other characters, then ending with .xml.
Example:
This XPath expression:
collection('file:///c:/?select=report_*.xml')
selects the document nodes of every XML document that resides in c:\ in a file with name starting with report_ then having a 0 or more characters, then ending with .xml.
The answer by Dimitre looks like the quickest solution in your case. But since you asked, here an XProc alternative:
<p:declare-step version="1.0" xmlns:p="http://www.w3.org/ns/xproc" xmlns:c="http://www.w3.org/ns/xproc-step" exclude-inline-prefixes="#all" name="main">
<!-- create context for p:variable with base-uri pointing to the location of this file -->
<p:input port="source"><p:inline><x/></p:inline></p:input>
<!-- any params passed in from outside get passed through to p:xslt automatically! -->
<p:input port="parameters" kind="parameter"/>
<!-- configuration options for steering input and output -->
<p:option name="input-dir" select="'./'"/>
<p:option name="input-filter" select="'^report_.*\.xml$'"/>
<p:option name="output-dir" select="'./'"/>
<!-- resolve any path to base uri of this file, to make sure they are absolute -->
<p:variable name="abs-input-dir" select="resolve-uri($input-dir, base-uri(/))"/>
<p:variable name="abs-output-dir" select="resolve-uri($output-dir, base-uri(/))"/>
<!-- first step: get list of all files in input-dir -->
<p:directory-list>
<p:with-option name="path" select="$abs-input-dir"/>
</p:directory-list>
<!-- iterate over each file to load it -->
<p:for-each>
<p:iteration-source select="//c:file[matches(#name, $input-filter)]"/>
<p:load>
<p:with-option name="href" select="resolve-uri(/c:file/#name, $abs-input-dir)"/>
</p:load>
</p:for-each>
<!-- wrap all files in a reports element to be able to hand it in to the xslt as a single input document -->
<p:wrap-sequence wrapper="reports"/>
<!-- apply the xslt (stylesheet is loaded below) -->
<p:xslt>
<p:input port="stylesheet">
<p:pipe step="style" port="result"/>
</p:input>
</p:xslt>
<!-- store the result in the output dir -->
<p:store>
<p:with-option name="href" select="resolve-uri('merged-reports.xml', $abs-output-dir)"/>
</p:store>
<!-- loading of the stylesheet.. -->
<p:load href="process-reports.xsl" name="style"/>
</p:declare-step>
Store the above as process-reports.xpl for instance. You can run it with XMLCalabash (http://xmlcalabash.com/download/). You can run it like this:
java -jar calabash.jar process-reports.xpl input-dir=./ output-dir=./
The above code assumes a process-reports.xsl that takes one documents that wraps all reports, and does a bit of processing on it. You could do processing in pure XProc as well, but you might prefer it this way.
You could also move the p:xslt step up to within the p:for-each (below the p:load), that would cause the xslt to be applied to each report individually.
Good luck!