I have written an xslt that reads some xml file names and does some operations on them.
I use a for-each to work them one-by-one. I have each path inside a parameter $path.
But now I would like to output the result of applying an external stylesheet to those files. I would write something like
<div> <something like xsl-transform($extern-xslt,$path)> </div>
to have the result tree of the transformation inside the main html output.
It is possible?
You could import the external stylesheet to your main stylesheet with xsl:import, and then just apply templates to that external XML file, which you can load with the document function.
<div><xsl:apply-templates select="document($path)"/></div>
If the templates in the external stylesheet would collide with the templates in the main stylesheet, you can use a different mode for them.
You can use the document() XPath function to load an external XML file. This returns a node-set which can be parsed with a <xml:apply-templates> call. Including an external stylesheet can be accomplished by using an <xsl:include> tag.
<xsl:include href="$external"/>
<xsl:apply-templates select="document($path)"/>
See also the documentation for document()
I have written an xslt that reads
some xml file names and does some
operations on them. I use a for-each
to work them one-by-one. I have each
path inside a parameter $path.
But now I would like to output the
result of applying an external
stylesheet to those files
The solution consists of these ingredients:
Use the standard XSLT document() function to load and access the external XML document.
Import the external stylesheet, using an <xsl:import> instruction.
The templates in the external stylesheet must be in a special mode, not used by the primary stylesheet.
At the place where the result of the "external transformation" is wanted, issue <xsl:apply-templates> selecting the necessary nodes of the external document (usually the root node /, or the top element /*). The mode, specified on the <xsl:apply-templates> should be the same as the mode used in the external stylesheet.
Here is a small, simplified example (No external stylesheet is imported, the "external document" is embedded in the stylesheet, and no mode is used):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:my="my:my"
>
<!-- <xsl:import href="myExternal.xsl"/> -->
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<my:div>
<h2>Weather</h2>
<p >It will be raining today</p>
</my:div>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="insertContents">
<xsl:apply-templates select="document('')/*/my:div/*"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on this XML document:
<html>
<h1>Today's News </h1>
<insertContents/>
</html>
the desired result is produced:
<html>
<h1>Today's News </h1>
<h2 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">Weather</h2>
<p xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">It will be raining today</p>
</html>
Note that the extraneous namespaces above are only due to the simplifications of this example -- they will not be generated if the external XML document were in its own file.
Related
I'm trying to find all text that is not within the XML markup:
<transcript>
<text start="9.75" dur="5.94">welcome to about my property here you
can learn more about how your property</text>
<text start="15.69" dur="4.71">was assessed see the information impact
has on file and compare your property to</text>
<text start="20.4" dur="1.3">others in your neighborhood</text>
<text start="21.7" dur="5.32">interested in learning about market
trends in your municipality no problem</text>
<text start="105.79" dur="6.23">I have all of this and more about life property
. see your property assessment know more</text>
<text start="112.02" dur="0.11">about</text>
</transcript>
I am using the following regex pattern, but obviously it is not correct because it grabs all of the text between the opening and closing <transcript> tags:
<transcript>[\s\S]*?<\/transcript>
How can modify this regex pattern to select only the text that is not within any of the markup tags?
Use XSLT. XSLT is a language specifically designed to convert XML into another output format (back to valid XML again, or something else such as (X)HTML, plain text, or any other format – but preferably, based on plain text).
In this case the smallest XSLT necessary is just this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0" >
<xsl:output method="text" indent="no" />
<xsl:template match="text">
<!-- do NOTHING here! -->
</xsl:template>
</xsl:stylesheet>
This works because the default for processing a single XML tag is to recursively apply template matches to its containing tags, and plain text will always be copied. The only tag inside your <template> is <text>, and you process it by doing 'nothing' – i.e., by not copying its contents to the output. The line inside that template is just a comment.
All other "nodes", in XML terminology, are those without a surrounding tag and so are copied to the output.
Alternatively, if you have more types of tags than just <text> elements and you want to skip all of them, apply templates to / and transcript to process each and apply another to * (which will select all remaining tags not specified elsewhere) to not process them:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0" >
<xsl:output method="text" indent="no" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="transcript">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="*">
<!-- do NOTHING here! -->
</xsl:template>
</xsl:stylesheet>
Again, the plain untagged text will fall through and not get processed, so their contents will be copied to output.
Both XSLT stylesheets will output only I ha, the only part in your sample text that is not surrounded by tags.
Do you want to find
welcome to about my property here you can learn more about how your property
from
<text start="9.75" dur="5.94">welcome to about my property here you can learn more about how your property</text>
??
Than it will work.
(?<=>).+?(?=<)
I have a project where I need to send an email from JBOSS 6. I'm hoping that I can do it from a stylesheet. Is there a way to call the 'sendmail' service in JBOSS 6 from XSL? I'm just not sure if its possible or even how to do it. If its not possible, maybe I can have the stylesheet output some text into a file somewhere for powershell to watch and send mail from it?
EDIT: I have added code
Here is some code that I am trying to make generate a ".txt" file, but it is not being generated. There are no errors that I can see from the transformer.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exslt"
exclude-result-prefixes="exsl"
version="1.0">
<xsl:output method="text" indent="no"/>
<xsl:variable name="emailPID" select="attr[#tag='00100020']"/>
<xsl:variable name="emailPName" select="attr[#tag='00100010']"/>
<!-- overwritten by application with actual values -->
<xsl:param name="calling" select="'SAMPLE_MOD'"/>
<xsl:param name="called" select="'SERVER1'"/>
<xsl:param name="date" select="'20051206'"/>
<xsl:param name="time" select="'115600.000'"/>
<xsl:template match="/dataset">
<exsl:document href="c:\apps\foo.txt">
<xsl:copy-of select="$emailPID"/>
<xsl:copy-of select="$emailPName"/>
</exsl:document>
</xsl:template>
</xsl:stylesheet>
If my stylesheet is using XSLT1 can I use 'TWO' output methods in one file? One doing "method="xml"" for my applications' function and the other doing "method="text"" to generate a text file?
No, with pure XSLT 1.0 it is not possible to produce multiple output files.
You could use an extension function from EXSLT, see Dimitre Novatchev's answer here. Another solution to your problem is to write two separate XSLT stylesheets, one producing "text" output, the other "xml".
Moreover, the method attribut of the xsl:output element is unique and there cannot be both "xml" and "text" in an XSLT 1.0 stylesheet.
If you use XSLT 2.0, however, this functionality is covered by the xsl:result-document element. This element can be used multiple times and also has a method attribute.
I am using about a dozen XSLT files to provide a large number of output formats. At the moment the user has to know the extension of the file format being exported to e.g. RTF, HTML, TXT.
I would also like to use parameters to allow more options. If I can embed the metadata in the XSL file itself then I can pick up the details by scanning through the files.
Here is what I am thinking about. In this example the program would have to parse the comments for the required information.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Title: Export to Rich Text Format -->
<!-- Description: This Stylesheet converts to a Rich Text Format format which may be used in a word processor such as Word -->
<!-- FileFormat: RTF -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="CompanyName"/> <!-- Format:String, Description: Company name to be inserted in the footer -->
<xsl:param name="DateDue"/> <!-- Format:Date-yyyy-mm-dd, Description: Date Due -->
<xsl:param name="IncludePicture">true</xsl:param><!-- Format:Boolean, Description: Do you want to include a graphical representation? -->
<xsl:template match="/">
<!-- Stuff -->
</xsl:template>
</xsl:stylesheet>
Are there any standards out there? Do I need to butcher more than one (Dublin Core with a smattering of XML Schema)?
P.S. the project this is being applied to is Argumentative.
Here is what I am thinking about. In
this example the program would have to
parse the comments for the required
information.
You don't need to code the metadata within comments.
Metadata can be specified as part of the XSLT stylesheet using ordinary XML markup -- as rich in structure and meaning as we need.
Here is an example how to do that:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:meta="my:meta">
<xsl:output method="text"/>
<meta:metadata>
<title>Title: Export to Rich Text Format </title>
<description>
This Stylesheet converts to a Rich Text
Format format which may be used in a word processor
such as Word
</description>
<fileFormat>RTF</fileFormat>
<parameters>
<parameter name="CompanyName" format="xs:string"
Description="Company name to be inserted in the footer"/>
<parameter name="DateDue" format="xs:date"
Description="Date Due"/>
<parameter name="IncludePicture" format="xs:boolean"
Description="Do you want to include a graphical representation?"/>
</parameters>
</meta:metadata>
<xsl:param name="CompanyName"/>
<xsl:param name="DateDue"/>
<xsl:param name="IncludePicture" select="true"/>
<xsl:variable name="vMetadata" select=
"document('')/*/meta:metadata"/>
<xsl:template match="/">
This is a demo how we can access and use the metadats.
Metadata --> Description:
"<xsl:copy-of select="$vMetadata/description"/>"
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on any XML document (not used), the result is:
This is a demo how we can access and use the metadats.
Metadata --> Description:
"
This Stylesheet converts to a Rich Text
Format format which may be used in a word processor
such as Word
"
Do note:
Any element that is in a namespace (of course not the no-namespace and not the xsl namespace) can be specified at the global level of any xslt stylesheet.
Such elements can be accessed using the xslt function document().
Here's a simple case.
Here's my XML:
<?xml version="1.0" encoding="utf-8" ?>
<dogs>
<dog type="Labrador">
<Name>Doggy</Name>
</dog>
<dog type="Batard">
<Name>Unknown</Name>
</dog>
</dogs>
This XML is used with two Xslt. This is the common one:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="text"/>
<xsl:template match="dogs">
<xsl:text>First template
</xsl:text>
<xsl:apply-templates select="." mode="othertemplate" />
</xsl:template>
</xsl:stylesheet>
This is the child one:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:include href="transform.xslt"/>
<xsl:template match="dogs" mode="othertemplate">
<xsl:text> Other template</xsl:text>
</xsl:template>
</xsl:stylesheet>
The child includes the common one (called transform.xslt).
When I execute the child, I get the expected result:
First template
Other template
When I execute the common one, I get this strange results:
First template
Doggy
Unknown
The common one applies a template with the mode "othertemplate". This mode is only included, some times, in the child xslt.
I want that, if there's no template with mode "othertemplate", then nothing should be outputted.
I don't want to include a template with mode "othertemplate" with empty body for all xslt files that does not have to use this template mode...
What should I do?
Thanks
Built-in Template Rules in XSLT
The element contents and the extra whitespace appear because of XSLT's built-in template rules, also known as default templates. These rules are applied when there is no other matching template. The built-in template rules are
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()|#*">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="processing-instruction()|comment()"/>
Built-in template rules process root and element nodes recursively and copy text (and attribute values if attribute nodes are selected). The built-in template rule for processing instructions and comments is to do nothing. Namespace nodes are not processed by default. Note that <xsl:apply-templates/> is practically a shorthand for <xsl:apply-templates select="child::node()"/> so it will not select attribute or namespace nodes.
There is also a built-in template rule for every mode. These templates are like default templates for elements and root except they continue processing in the same mode.
<xsl:template match="*|/" mode="foobar">
<xsl:apply-templates mode="foobar"/>
</xsl:template>
Because your stylesheet doesn't have a template matching dogs with mode othertemplate this built-in template rule is applied which in this case results in processing all child nodes and eventually printing the text nodes. Indentation and line feeds between the source document's elements are also text nodes so they also get printed and cause the extra whitespace in the output.
Warning on non-terminating loops because of <xsl:apply-templates select="."/>
Typically apply-templates is used to process descendants. In your example code you selected the current node when calling apply-templates. This may result in non-terminating loop if the template itself is applied because of an apply-templates command inside it. Example below
<xsl:template match="foobar">
<!-- This is an infinite loop -->
<xsl:apply-templates select="."/>
</xsl:template>
By the way. On a general rule on combining stylesheets, think carefully which template you should run and which one you should import or include. (I have read that as a general practice, Michael Kay seems to recommend using <xsl:import> to import the general-case stylesheet into the special-case stylesheet, not the other way round.)
The built-in XSLT templates are defined and selected for every mode. So, the built-in template for text nodes is selected and (by definition) it outputs the text node.
To suppress this, you need to override thie built-in template for text nodes (also possibly for elements) in your desired mode with an empty template:
<xsl:template match="text()" mode="othertemplate"/>
Include the above in your imported stylesheet.
There's an XSL that includes another XSL:
<xsl:include href="registered.xsl"/>
That included file has a list of nodes:
<g:registered>
<node1/>
<node2/>
</g:registered>
Documentation says that "the children of the <xsl:stylesheet> element in this document replace the element in the including document", so I would think that, given the include directive has worked, I can select g:registered nodes like if they always belonged to the inluding document:
select="document('')/xsi:schema/g:registered"
That returns an empty nodeset though.
However, this:
select="document('registered.xsl')/xsi:schema/g:registered"
does select what is required, but that, as I suppose, means opening the included file for the second time which doesn't seem nice to me.
So how do I select those includes without opening the file second time?
EDIT
Requested document structure:
Included document:
<?xml version='1.0' encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:g="http://www.sample.com/ns">
<g:registered-templates>
<SampleTemplate/>
<WrongTemplate/>
</g:registered-templates>
<xsl:include href="Sample Template.xsl" />
<xsl:include href="Wrong Template.xsl" />
</xsl:stylesheet>
Including document:
<?xml version='1.0' encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:g="http://www.sample.com/ns">
<xsl:output method="text"/>
<xsl:include href="Label Registration.xsl"/>
<!-- How do I refer to just loaded inclusion without directing engine to the file again? -->
<xsl:variable name="template-names" select="document('Label Registration.xsl')/xsl:stylesheet/g:registered-templates"/>
<xsl:template match="Job">
<xsl:for-each select="WorkItem">
<xsl:apply-templates select="$template-names/*[local-name()=current()/#name]">
<xsl:with-param name="context" select="." />
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Selecting into your variable template-names queries the transformation source document - not your included stylesheet.
If you want to refer to g:registered-templates you have to point to the file like a second source document.
EDIT
I'm not really sure. but it looks like you want to create an element according to the attribute value.
In that case this post will be interesting for you.
<xsl:for-each select="WorkItem">
<xsl:element name="{Type}" >
<xsl:value-of select="current()/#name"/>
</xsl:element>
</xsl:for-each>
Ok, my understanding was wrong.
The document('') function opens the file anyway, so it has no advantages, performance-wise, over document('registered.xsl'). And since it queries the file, not the now-modified DOM model of current document, the result does not include my includes.
And it is not possible to query DOM model of the transformation template itself, as far as I'm concerned.