Add element after document has been produced - xslt

After having created an XHTML document using XSLT, I need to add an element (link:schemaRef).
The reason is that I am merging 2 XHTML document and it is only the merged document that should have the element I need to add. I reduced the length of the link just to fit the example better.
I cannot see that the result file has the added link.
Something obviously wrong in my code?
My code base:
<!-- Identity transform -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- Find and add element in document -->
<xsl:template match="/xhtml:html/xhtml:body/xhtml:div[1]/ix:header/ix:hidden/ix:references">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:element name="link:schemaRef">
<xsl:attribute name="xlink:type">simple</xsl:attribute>
<xsl:attribute name="xlink:href">http://example.org</xsl:attribute>
</xsl:element>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>

You still haven't explained how your input looks but if you want to perform two transformations within one stylesheet then you can use modes to separate them plus store the first transformation step's result in a variable you push to the second mode:
<xsl:mode name="m1" on-no-match="shallow-copy"/>
<xsl:variable name="intermediary-result">
<xsl:apply-templates mode="m1"/>
</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates select="$intermediary-result" mode="m2"/>
</xsl:template>
<xsl:mode name="m2" on-no-match="shallow-copy"/>
<xsl:template mode="m2" match="/xhtml:html/xhtml:body/xhtml:div[1]/ix:header/ix:hidden/ix:references">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:element name="link:schemaRef">
<xsl:attribute name="xlink:type">simple</xsl:attribute>
<xsl:attribute name="xlink:href">http://example.org</xsl:attribute>
</xsl:element>
<xsl:apply-templates mode="#current"/>
</xsl:copy>
</xsl:template>

Related

Combining two xml files

I want to combine two xsl files into one. I tried but I am failing to do so. Both of these xsl files work fine separately but when I combine them, it does not. I am not expert in xsl.
Here is the first xsl file:
<xsl:variable name="products" select="document('T01_product.xml')/products"/>
<xsl:strip-space elements="*"/>
<xsl:key name="product" match="product" use="pm-id" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="deleted-assignment/pm-id">
<xsl:copy-of select="."/>
<xsl:copy-of select="key('product', .)/art-num"/>
</xsl:template>
</xsl:stylesheet>
second xsl file is
<xsl:variable name="items" select="document('T01_item.xml')/items"/>
<xsl:strip-space elements="*"/>
<xsl:key name="item" match="item" use="pm-id" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="deleted-assignment/pm-id">
<xsl:copy-of select="."/>
<xsl:copy-of select="key('item', .)/art-num"/>
</xsl:template>
</xsl:stylesheet>
Thanks in advance
When you say you want to combine two stylesheets A and B, do you mean that you first want to apply transformation A, and then apply transformation B to the result? If that's what you want, then it might be better to keep them separate, and use some pipelining technology to organize the work flow (e.g. XProc but there are many other candidates).
It's also possible to do a single-stylesheet pipeline, especially in XSLT 2.0, using modes: put all the rules for each transformation into a separate mode, capture the result of each phase in a variable, and then apply-templates to the variable specifying the mode-name for the next phase.
If you want to combine the stylesheets in some other way, then you need to explain the requirement better.
You have not explained how your input looks and which result you want so this is a wild guess, perhaps you want to combine
<xsl:template match="deleted-assignment/pm-id">
<xsl:copy-of select="."/>
<xsl:copy-of select="key('product', .)/art-num"/>
</xsl:template>
and
<xsl:template match="deleted-assignment/pm-id">
<xsl:copy-of select="."/>
<xsl:copy-of select="key('item', .)/art-num"/>
</xsl:template>
to
<xsl:template match="deleted-assignment/pm-id">
<xsl:copy-of select="."/>
<xsl:copy-of select="key('product', .)/art-num"/>
<xsl:copy-of select="key('item', .)/art-num"/>
</xsl:template>

xslt filter variable node-set using input

I'm having trouble with, what I believe, is an inability to filter a variable node-set.
I have a variable which is populated with a node-set from a collection of documents and I'd like to echo the input and add the variable nodes if those nodes don't already exist in the input.
<xsl:variable name="views" select="collection('file:/C:/temp/?select=*.xml;recurse=yes')"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="#* | *"/>
<!-- having trouble with the filter here never works -->
<xsl:for-each select="$views/someElement[not(#name=(//someInputElement/#name))]">
<!-- copy stuff in -->
</xsl:for-each>
</xsl:copy>
</xsl:template>
note that there will be many instances of someInputElement with name attributes in the input.
when I run the xpath //someInputElement/#name against the target I get a listing as expected.
any advice most appreciated.
I believe I've found the answer... from a hint I found somewhere else today I've added a reference back to the root and used that in the filter.
<xsl:variable name="views" select="collection('file:/C:/temp/?select=*.xml;recurse=yes')"/>
<xsl:variable name="root" select="/" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="#* | *"/>
<!-- filter referring back to the root of the input -->
<xsl:for-each select="$views/someElement[not(#name=($root//someInputElement/#name))]">
<!-- copy stuff in -->
</xsl:for-each>
</xsl:copy>
</xsl:template>

Rename a batch of values with xslt

We have a program that uses xml to save configurations of our program. Someone decided to rename a couple of values in our database and these renames should now also be backwards compatible in the configurations of our customers.
An example of a configuration
<configuration>
<fruitToEat>yellow_curved_thing</fruitToEat> <!-- should now become banana -->
</configuration>
A simple match would be (not tested, just an example):
<xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match"/configuration/fruitToEat/text()">
<xsl:text>banana</xsl:text>
</xsl:template>
</xsl:template>
But this is just one example and I want to do this 150 times.
Is it possible to make an xsl that reads a simple text file or ini file that tells me how the 150 matches should look alike?
<xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- recreate this template 150 times from an ini file or something -->
<xsl:template match"/configuration/fruitToEat/text()[.='yellow_curved_thing']">
<xsl:text>banana</xsl:text>
</xsl:template>
</xsl:template>
An example of my mapping file could be simply:
yellow_curved_thing = banana
round_thing = tomato
round_dotted = strawberry
And I would simply want a small xslt that tells me:
<xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- recreate this template 150 times from an ini file or something -->
<xsl:template match"/configuration/fruitToEat/text()[.=$fileRow0]">
<xsl:text>$fileRow1</xsl:text>
</xsl:template>
</xsl:template>
So even if I think there is more complexity behind the curtain, may be this will help a little bit.
There are some possibilities to do this with xlst. Which would be best in long term depends on complexity in real life and how often you need to do this with different "mapping" information.
For your easy example you can put the "mapping" information into a xml file. This could be done by some script form ini file.
<mappings>
<mapping name="fruitToEat" >
<map from="yellow_curved_thing" to="banana" />
<map from="round_thing" to="tomato" />
<map from="round_dotted" to="strawberry" />
</mapping>
</mappings>
Than you can have a template which make use of this mapping information:
<xsl:variable name="fruitMapping"
select="document('fruitmapping.xml')//mapping[#name='fruitToEat']" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/configuration/fruitToEat/text()" >
<!-- find the entry in "ini file" -->
<xsl:variable name ="map" select="$fruitMapping/map[#from = current()]" />
<xsl:choose>
<xsl:when test="$map" >
<xsl:value-of select="$map/#to"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
But if this is a onetime job I would implement this "mapping" direct a template. Like this:
<xsl:template match="/configuration/fruitToEat/text()" >
<xsl:choose>
<xsl:when test=".='yellow_curved_thing'" >banana</xsl:when>
<xsl:when test=".='round_thing'" >tomato</xsl:when>
<xsl:when test=".='round_dotted'" >strawberry</xsl:when>
<xsl:otherwise>
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
It seems you wanted to create your XSLT dynamic XSLT on the basis of your configuration file which is also an XML document. Have a look this exmple considering this:
configuration.xml
<p>
<configuration>
<fruitToEat>yellow_curved_thing</fruitToEat>
<mapped>banana</mapped>
</configuration>
<configuration>
<fruitToEat>round_thing</fruitToEat>
<mapped>tomato</mapped>
</configuration>
</p>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:element name="xsl:stylesheet">
<xsl:attribute name="version">
<xsl:text>1.0</xsl:text>
</xsl:attribute>
<xsl:element name="xsl:template">
<xsl:attribute name="match">
<xsl:text>node()|#*</xsl:text>
</xsl:attribute>
<xsl:element name="xsl:copy">
<xsl:element name="xsl:apply-templates">
<xsl:attribute name="select">
<xsl:text>node()|#*</xsl:text>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:element>
<xsl:for-each select="//configuration">
<xsl:element name="xsl:template">
<xsl:attribute name="match">
<xsl:text>configuration/fruitToEat/text()[.=</xsl:text>
<xsl:text>'</xsl:text>
<xsl:value-of select="fruitToEat"/>
<xsl:text>']</xsl:text>
</xsl:attribute>
<xsl:element name="xsl:text">
<xsl:value-of select="mapped"/>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
output:
<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="configuration/fruitToEat/text()[.='yellow_curved_thing']">
<xsl:text>banana</xsl:text>
</xsl:template>
<xsl:template match="configuration/fruitToEat/text()[.='round_thing']">
<xsl:text>tomato</xsl:text>
</xsl:template>
</xsl:stylesheet>

Replacing the namespace in input xml

I have two types of input xml, one with namespace prefix and another one without prefix and i want to replace the namespaces.
Sample1
<v1:Library xmlns:v1="http://testlibrary" xmlns:v2="http://commonprice">
<v1:Books_details>
<v1:Name>test1</v1:Name>
<v1:title>test2</v1:title>
<v2:price xmlns="http://commonprice">12</v2:price>
</v1:Books_details>
</v1:Library>
Sample2
<Library xmlns="http://testlibrary">
<Books_details>
<Name>test1</Name>
<title>test2</title>
<price xmlns="http://commonprice">12</price>
</Books_details>
</Library>
I have written following XSLT to change the namespace from "http://testlibrary" to "http://newlibrary" and it works fine for sample1 but it doesn't work for the sample2. It gives wrong result. It also the change the namespace of the price element even though it doesn't have namespace to be replaced.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:param name="old_namespace"/>
<xsl:param name="new_namespace"/>
<xsl:template match="/">
<xsl:apply-templates select="#* | node()"/>
</xsl:template>
<xsl:template match="text() | comment() | processing-instruction()">
<xsl:copy>
<xsl:apply-templates select="text() | comment() | processing-instruction()"/>
</xsl:copy>
</xsl:template>
<!-- Template used to copy elements -->
<xsl:template match="*">
<xsl:variable name="name">
<xsl:choose>
<xsl:when test="contains(name(), ':')">
<xsl:value-of select="name()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="local-name()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{$name}" namespace="{$new_namespace}">
<!-- Copy all namespace through except for namespace to be changed -->
<xsl:for-each select="namespace::*">
<xsl:if test="string(.)!=$old_namespace">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Note: My first answer was wrong because in XSLT 1.0, you can't use variable references within a match pattern.
This style-sheet will take either Sample1 or Sample2 as input document and replace all occurrences of the old namespace, from the element names, with the new namespace. Note: you can change the xsl:variable for xsl:param.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="old_namespace" select="'http://testlibrary'" />
<xsl:variable name="new_namespace" select="'http://newlibrary'" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:choose>
<xsl:when test="namespace-uri()=$old_namespace">
<xsl:element name="{local-name()}" namespace="{$new_namespace}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Caveat
The above style-sheet will only change the namespace of the elements. It will not change the namespaces of attributes, nor will it remove extraneous namespaces nodes.
On a more specific solution
It is very unusual to require a general solution for an operation on a variable namespace. Namespaces, being what they are tend to be fixed and known. Consider carefully, if you really need a generalized solution. If you need a specific solution, meaning replacing occurrences of a specific namespace, then things get a lot easier, such as with this style-sheet...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:old="http://testlibrary"
xmlns:new="http://newlibrary">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="old:*">
<xsl:element name="{local-name()}" namespace="http://newlibrary">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
UPDATE
I just noticed Dimitre's solution to a very similar question here.

Optimisation <xsl:apply-templates/> for a set of tags

How it is possible to reduce this record?
<xsl:template match="BR">
<br/>
</xsl:template>
<xsl:template match="B">
<strong><xsl:apply-templates /></strong>
</xsl:template>
<xsl:template match="STRONG">
<strong><xsl:apply-templates /></strong>
</xsl:template>
<xsl:template match="I">
<em><xsl:apply-templates /></em>
</xsl:template>
<xsl:template match="EM">
<em><xsl:apply-templates /></em>
</xsl:template>
<xsl:template match="OL">
<ol><xsl:apply-templates /></ol>
</xsl:template>
<xsl:template match="UL">
<ul><xsl:apply-templates /></ul>
</xsl:template>
<xsl:template match="LI">
<li><xsl:apply-templates /></li>
</xsl:template>
<xsl:template match="SUB">
<sub><xsl:apply-templates /></sub>
</xsl:template>
<xsl:template match="SUP">
<sup><xsl:apply-templates /></sup>
</xsl:template>
<xsl:template match="NOBR">
<nobr><xsl:apply-templates /></nobr>
</xsl:template>
Maybe something like:
<xsl:template match="LI|SUB|...">
<xsl:element name="{translate(name(),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
I don't think, there's a tolower function in XSLT (at least not in 1.0)
If the elements to be created are not known in advance and only a few known elements needs to be processed in another, more specific way, here's a more dynamic solution:
<xsl:template match="*">
<xsl:element name="{translate(name(), $vUpper, $vLower)}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
where $vUpper and $vLower are defined as:
<xsl:variable name="vUpper" select=
"'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
"/>
<xsl:variable name="vLower" select=
"'abcdefghijklmnopqrstuvwxyz'
"/>
There must be templates matching the few known elements that should not be processed in the above way. These more specific templates will override the more general template above. For example:
<xsl:template match="specificName">
<!-- Specific processing here -->
</xsl:template>
Also, the generic template above, matching elements should be overriding the "identity rule" (template).