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.
Related
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>
I have an input xml file that have multiple <b> elements, i have to wrap all <b> that are appearing continuously. i have written an xsl for this. it is better way to do this? also there are extra space generated during transformation after wrapping elements.
Sample Input XML
<chap>
<p><b>The</b> <b>Attorney</b> General <b>(Fees)</b><b>a</b><b>b</b><b>c</b> Determination 2012 No 110 commenced on 1 <b>July</b> <b>2012</b> <b>and</b> was repealed on <b>1</b> <b>July</b> <b>2013</b>. The Determination is yet to be amended by:</p>
</chap>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:guru="Self"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="b[preceding-sibling::node()[1][self::b]]
|b[preceding-sibling::node()[1][normalize-space(.) = '']][preceding-sibling::node()[2][self::b]]"/>
<xsl:template match="b">
<xsl:copy>
<xsl:apply-templates/>
<xsl:if test="following-sibling::node()[1][self::b]">
<xsl:copy-of select="guru:wrap(following-sibling::node()[1], '')"/>
</xsl:if>
<xsl:if test="following-sibling::node()[1][normalize-space(.) = '']/following-sibling::node()[1][self::b]">
<xsl:copy-of select="guru:wrap(following-sibling::node()[2], following-sibling::node()[1])"/>
</xsl:if>
</xsl:copy>
</xsl:template>
<xsl:function name="guru:wrap">
<xsl:param name="b_data"/>
<xsl:param name="space"/>
<xsl:value-of select="$space"/>
<xsl:value-of select="$b_data"/>
<xsl:if test="$b_data/following-sibling::node()[1][self::b]">
<xsl:copy-of select="guru:wrap($b_data/following-sibling::node()[1], '')"/>
</xsl:if>
<xsl:if test="$b_data/following-sibling::node()[1][normalize-space(.) = '']/following-sibling::node()[1][self::b]">
<xsl:copy-of select="guru:wrap($b_data/following-sibling::node()[2], $b_data/following-sibling::node()[1])"/>
</xsl:if>
</xsl:function>
</xsl:stylesheet>
Output
<chap>
<p><b>The Attorney</b> General <b>(Fees)abc</b> Determination 2012 No 110 commenced on 1 <b>July 2012 and</b> was repealed on <b>1 July 2013</b> . The Determination is yet to be amended by:</p>
</chap>
Desired Output
<chap>
<p><b>The Attorney</b> General <b>(Fees)abc</b> Determination 2012 No 110 commenced on 1 <b>July 2012 and</b> was repealed on <b>1 July 2013</b>. The Determination is yet to be amended by:</p>
</chap>
Thanks in Advance.
I would suggest to use for-each-group group-adjacent="self::b or self::text()[. = ' ']":
<xsl:template match="p">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:for-each-group select="node()" group-adjacent="self::b or self::text()[. = ' ']">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<b>
<xsl:apply-templates select="current-group()/node() | current-group()[self::text()]"/>
</b>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
https://xsltfiddle.liberty-development.net/gWmuiHU/1 shows the result, it uses XSLT 3 but for XSLT 2 you simply have to remove the xsl:mode and keep the identity template
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
you have in your code.
I want to transform below XML using XSLT. This XML elements has dots to represent hierarchy
<UsrEmployee>
<Code>70068579</Code>
<Initials>F</Initials>
<FirstName>Koichi</FirstName>
<Prefix></Prefix>
<LastName>Nakamura</LastName>
<PropertyRef>70068579</PropertyRef>
<SpaceRef.Code>001</SpaceRef.Code>
<SpaceRef.FloorRef.Code>01</SpaceRef.FloorRef.Code>
<SpaceRef.FloorRef.PropertyRef>70068579</SpaceRef.FloorRef.PropertyRef>
<SpaceRef.propertyRef>70068579</SpaceRef.propertyRef>
</UsrEmployee>
The above XML I want to transform as below XML, in the source XML element names can be any thing, number of dots(depth) is not known(not fixed). I want to create XSLT which would transform any generic XML of any size to hierarchical structure
<UsrEmployee>
<Code>70068579</Code>
<Initials>F</Initials>
<FirstName>Koichi</FirstName>
<Prefix></Prefix>
<LastName>Nakamura</LastName>
<SpaceRef>
<Code>001</Code>
<propertyRef>70068579</propertyRef>
<FloorRef>
<Code>01</Code>
<PropertyRef>70068579</PropertyRef>
</FloorRef>
</SpaceRef>
<PropertyRef>70068579</PropertyRef>
</UsrEmployee>
Can someone help me on this
Just create matching templates for your "dotted" elements and apply them.
Use select to control the "catch-all" identity template (the last template in the xslt)
Of course this may be an oversimplified solution and a more complex transformation will be needed if you have an arbitrary hierarchical structure where selecting your templates like this is not practicable.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="UsrEmployee">
<xsl:element name="UsrEmployee">
<xsl:apply-templates select="Code | Initials | FirstName | Prefix | LastName | PropertyRef" />
<xsl:element name="SpaceRef">
<xsl:apply-templates select="SpaceRef.Code | SpaceRef.propertyRef" />
<xsl:element name="FloorRef">
<xsl:apply-templates select="SpaceRef.FloorRef.Code | SpaceRef.FloorRef.PropertyRef" />
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="SpaceRef.Code">
<xsl:element name="Code">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
<xsl:template match="SpaceRef.propertyRef">
<xsl:element name="propertyRef">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
<xsl:template match="SpaceRef.FloorRef.Code">
<xsl:element name="Code">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
<xsl:template match="SpaceRef.FloorRef.PropertyRef">
<xsl:element name="PropertyRef">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
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>
i'm working the first time with xslt and i really don't understand why this xsl don't copy attributes from the source xml. Perhaps someone can give me a hint??
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="rpl" select="document('ParamInvoice.xml')"/>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="* | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="vInvoiceElement" select="$rpl/StoraInvoice/*[name()=name(current())]"/>
<xsl:copy>
<xsl:if test="$vInvoiceElement/Attribute">
<xsl:call-template name="AttributeErzeugen">
<xsl:with-param name="pAttr" select="$vInvoiceElement/Attribute"/>
</xsl:call-template>
</xsl:if>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template name="AttributeErzeugen">
<xsl:param name="pAttr"/>
<xsl:for-each select="$pAttr">
<xsl:attribute name="{#name}"><xsl:value-of select="."/></xsl:attribute>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Instead of <xsl:copy>, you should use <xsl:copy-of>. The difference between both is that copy copies the element only (without attributes and child elements) and copy-of copies the entire elements (attributes, childs, etc).
Check http://www.w3schools.com/xsl/xsl_w3celementref.asp