I have a number of vendor records which contain multiple addresses e.g.
<vendor>
<addresses>
<address primary="yes">
<line1 />
<city />
<state />
....
</address>
<address primary="no">
<line1 />
<city />
<state />
....
</address>
</addresses>
</vendor>
Some required elements are missing -- preventing updating of the records. Can xmlstarlet can be used to add an element with a default value if it is missing?
Here's a simple example. I'll use xmllint --auto for the xml source. Then we'll add an <add-me> element as a child of <info> if it doesn't exist using the identity transform pattern.
Source xml:
xmllint --auto
<?xml version="1.0"?>
<info>abc</info>
Add the missing element:
xmllint --auto | xsltproc add-missing.xsl -
<?xml version="1.0"?>
<info><add-me>some stuff</add-me>abc</info>
add-missing.xsl:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="info">
<xsl:copy>
<xsl:if test="not(add-me)">
<add-me>some stuff</add-me>
</xsl:if>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Another XSLT w/xmlstarlet option is to use a variable that contains the required elements (with or without default values) and treat is as a node set (using the supported exsl:node-set() function).
You can then iterate over the node set to see if an element with the same name already exists. If it does, use it. Otherwise use the default.
Example...
XML Input (input.xml)
<vendor>
<addresses>
<address primary="yes">
<line1>address 1 line1</line1>
<state>address 1 state1</state>
</address>
<address primary="no">
<line1>address 2 line1</line1>
<city>address 2 city</city>
<state>address 2 state</state>
</address>
</addresses>
</vendor>
XSLT 1.0 (so.xsl)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="req_elems">
<req>
<line1/>
<city/>
<state/>
<country/>
</req>
</xsl:variable>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="address">
<xsl:variable name="ctx" select="."/>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:for-each select="exsl:node-set($req_elems)/req/*">
<xsl:choose>
<xsl:when test="$ctx/*[local-name()=local-name(current())]">
<xsl:apply-templates select="$ctx/*[local-name()=local-name(current())]"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
XML Output
<vendor>
<addresses>
<address primary="yes">
<line1>address 1 line1</line1>
<city/>
<state>address 1 state1</state>
<country/>
</address>
<address primary="no">
<line1>address 2 line1</line1>
<city>address 2 city</city>
<state>address 2 state</state>
<country/>
</address>
</addresses>
</vendor>
Note: This only works if the only allowed elements in address are the same as $req_elements. For example, if you have an element named "foo" in address, it will be dropped from the output.
Related
I am using following xslt
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.test.com/services/test/test/v1">
<xsl:output method="xml" encoding="UTF-8"
omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<xsl:variable name="mytree">
<xsl:call-template name="myvariable">
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="count($mytree/foos/foo) > 1">
<xsl:copy-of select="$mytree"/>
</xsl:when>
<xsl:otherwise>
<error>test</error>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="myvariable">
<foos>
<foo>bar1</foo>
<foo>bar2</foo>
<foo>bar3</foo>
<foo>bar4</foo>
</foos>
</xsl:template>
</xsl:stylesheet>
When i use above xslt it should be following output
<foos xmlns="http://www.test.com/services/test/test/v1">
<foo>bar1</foo>
<foo>bar2</foo>
<foo>bar3</foo>
<foo>bar4</foo>
</foos>
but it is
<error xmlns="http://www.test.com/services/test/test/v1">test</error>
when i remove the xmlns="http://www.test.com/services/test/test/v1" output is proper. Not sure what is happening?
Well, with any XML, whether constructed inside of your XSLT or read from a source, if you have elements in a certain namespace, then, to select them with XPath in XSLT, in XSLT 2 you have two options, either use xpath-default-namespace="http://www.test.com/services/test/test/v1" (e.g. <xsl:when test="count($mytree/foos/foo) > 1" xpath-default-namespace="http://www.test.com/services/test/test/v1">) or bind the namespace to a prefix (e.g. <xsl:when xmlns:v1="http://www.test.com/services/test/test/v1" test="count($mytree/v1:foos/v1:foo) > 1">).
You can use these approaches on an ancestor element, for instance the root element of the stylesheet, if it does not interfere with other selections you want to make.
You have to specify qualified element names in your XPath expression to address the foos and foo elements in your default namespace http://www.test.com/services/test/test/v1:
Register the default namespace once more with a namespace prefix (e.g. myns): xmlns:myns="http://www.test.com/services/test/test/v1"
Use that namepace prefix in your XPath expressions to address nodes in that namespace (e.g. myns:foos/myns:foo).
Add exclude-result-prefixes="myns" to suppress the myns prefix in your result document.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.test.com/services/test/test/v1"
xmlns:myns="http://www.test.com/services/test/test/v1"
exclude-result-prefixes="myns">
…
<xsl:template match="/">
…
<xsl:choose>
<xsl:when test="count($mytree/myns:foos/myns:foo) > 1">
<xsl:copy-of select="$mytree"/>
</xsl:when>
<xsl:otherwise>
<error>test</error>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
…
</xsl:stylesheet>
see XSLT Fiddle
If you only had an XSLT 1.0 processor at hand, you would need the EXSLT node-set function to access the $mytree variables from the result tree:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.test.com/services/test/test/v1"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl"
xmlns:myns="http://www.test.com/services/test/test/v1"
exclude-result-prefixes="myns">
…
<xsl:template match="/">
…
<xsl:choose>
<xsl:when test="count(exsl:node-set($mytree)/myns:foos/myns:foo) > 1">
<xsl:copy-of select="$mytree"/>
</xsl:when>
<xsl:otherwise>
<error>test</error>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
…
</xsl:stylesheet>
Use code for remove namespace
<xsl:template match="#*[namespace-uri() = 'http://www.test.com/services/test/test/v1']"/>
I want to extract short lemmas out of text for some explanatory notes. That is, if the text is too long it should output only the first and the last word. This works:
<?xml version="1.0" encoding="UTF-8"?>
<lemma>
<a><b>I</b> can what I can and <b><c>what</c></b> I can't I can</a>
</lemma>
when this xslt is applied
<?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"
version="2.0">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<!-- Identity template : copy all text nodes, elements and attributes -->
<xsl:template match="#*|node()">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="lemma">
<xsl:value-of select="."/>
<xsl:choose>
<xsl:when test="string-length(normalize-space(a)) > 20">
<xsl:value-of select="tokenize(a,' ')[1]"/>
<xsl:text> […] </xsl:text>
<xsl:value-of select="tokenize(a,' ')[last()]"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="a"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
produces the desired output:
I can what I can and what I can't I can
I […] can
Unfortunately whenever two child elements are immediately adjacent the space in between is coded as child-node named „space“. The above solution doesn't work with:
<lemma>
<a><b>I</b><space/><b>can</b> what I can and what I can't I can</a>
</lemma>
I tried to have the single space-special character processed before, but that doesn't work (and I know why), I just don't know how to do it better. It would work with two XLST-runs, I suppose.
<?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"
version="2.0">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<!-- Identity template : copy all text nodes, elements and attributes -->
<xsl:template match="#*|node()">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="space">
</xsl:template>
<xsl:template match="lemma">
<xsl:apply-templates select="space"/>
<xsl:value-of select="."/>
<xsl:choose>
<xsl:when test="string-length(normalize-space(a)) > 20">
<xsl:value-of select="tokenize(a,' ')[1]"/>
<xsl:text> […] </xsl:text>
<xsl:value-of select="tokenize(a,' ')[last()]"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="a"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output:
Ican what I can and what I can't I can
Ican […] can
You could do an xsl:apply-templates to process a and save it in a variable...
XML Input
<doc>
<lemma>
<a><b>I</b> can what I can and <b><c>what</c></b> I can't I can</a>
</lemma>
<lemma>
<a><b>I</b><space/><b>can</b> what I can and what I can't I can</a>
</lemma>
</doc>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="space">
<xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="lemma">
<xsl:variable name="a">
<xsl:apply-templates select="a"/>
</xsl:variable>
<xsl:variable name="norm" select="normalize-space($a)"/>
<xsl:variable name="tokens" select="tokenize($norm,'\s')"/>
<xsl:copy>
<result>
<xsl:value-of select="$norm"/>
</result>
<result>
<xsl:value-of select="
if (string-length($norm) > 20) then
concat($tokens[1],' […] ', $tokens[last()])
else $norm"/>
</result>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
XML Output
<doc>
<lemma>
<result>I can what I can and what I can't I can</result>
<result>I […] can</result>
</lemma>
<lemma>
<result>I can what I can and what I can't I can</result>
<result>I […] can</result>
</lemma>
</doc>
I have a list of order lines with each one product on them. The products in may form a self-referencing hierarchy. I need to order the lines in such a way that all products that have no parent or whose parent is missing from the order are at the top, followed by their children. No child may be above its parent in the end result.
So how can i order the following xml:
<order>
<line><product code="3" parent="1"/></line>
<line><product code="2" parent="1"/></line>
<line><product code="6" parent="X"/></line>
<line><product code="1" /></line>
<line><product code="4" parent="2"/></line>
</order>
Into this:
<order>
<line><product code="6" parent="X"/></line>
<line><product code="1" /></line>
<line><product code="2" parent="1"/></line>
<line><product code="3" parent="1"/></line>
<line><product code="4" parent="2"/></line>
</order>
Note that the order within a specific level is not important, as long as the child node follows at some point after it's parent.
I have a solution which works for hierarchies that do not exceed a predefined depth:
<order>
<xsl:variable name="level-0"
select="/order/line[ not(product/#parent=../line/product/#code) ]"/>
<xsl:for-each select="$level-0">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:variable name="level-1"
select="/order/line[ product/#parent=$level-0/product/#code ]"/>
<xsl:for-each select="$level-1">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:variable name="level-2"
select="/order/line[ product/#parent=$level-1/product/#code ]"/>
<xsl:for-each select="$level-2">
<xsl:copy-of select="."/>
</xsl:for-each>
</order>
The above sample xslt will work for hierarchies with a maximum depth of 3 levels and is easily extended to more, but how can i generalize this and have the xslt sort arbitrary levels of depth correctly?
To start with, you could define a couple of keys to help you look up the line elements by either their code or parent attribute
<xsl:key name="products-by-parent" match="line" use="product/#parent" />
<xsl:key name="products-by-code" match="line" use="product/#code" />
You would start off by selecting the line elements with no parent, using a key to do this check:
<xsl:apply-templates select="line[not(key('products-by-code', product/#parent))]"/>
Then, within the template that matches the line element, you would just copy the element, and then select its "children" like so, using the other key
<xsl:apply-templates select="key('products-by-parent', product/#code)"/>
This would be a recursive call, so it would recursively look for its children until no more are found.
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="products-by-parent" match="line" use="product/#parent"/>
<xsl:key name="products-by-code" match="line" use="product/#code"/>
<xsl:template match="order">
<xsl:copy>
<xsl:apply-templates select="line[not(key('products-by-code', product/#parent))]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="line">
<xsl:call-template name="identity"/>
<xsl:apply-templates select="key('products-by-parent', product/#code)"/>
</xsl:template>
<xsl:template match="#*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Do note the use of the XSLT identity transform to copy the existing nodes in the XML.
Very interesting problem. I would do this in two passes: first, nest the elements according to their hierarchy. Then output the elements, sorted by the count of their ancestors.
XSLT 1.0 (+ EXSLT node-set() function):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="product-by-code" match="product" use="#code" />
<!-- first pass -->
<xsl:variable name="nested">
<xsl:apply-templates select="/order/line/product[not(key('product-by-code', #parent))]" mode="nest"/>
</xsl:variable>
<xsl:template match="product" mode="nest">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="../../line/product[#parent=current()/#code]" mode="nest"/>
</xsl:copy>
</xsl:template>
<!-- output -->
<xsl:template match="/order">
<xsl:copy>
<xsl:for-each select="exsl:node-set($nested)//product">
<xsl:sort select="count(ancestor::*)" data-type="number" order="ascending"/>
<line><product><xsl:copy-of select="#*"/></product></line>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your input, the result is:
<?xml version="1.0" encoding="UTF-8"?>
<order>
<line>
<product code="6" parent="X"/>
</line>
<line>
<product code="1"/>
</line>
<line>
<product code="3" parent="1"/>
</line>
<line>
<product code="2" parent="1"/>
</line>
<line>
<product code="4" parent="2"/>
</line>
</order>
This still leaves the issue of the existing/missing parent X - I will try to address that later.
i have some requirement, where i need to replace the references in the same xml file.
limitation is to use xslt 1.0 only.
below is my sample input xml.
<org>
<depts>
<dept>
<deptId>1009</deptId>
<deptName>IT</deptName>
<deptAccessCode>IT-1009</deptAccessCode>
</dept>
<dept>
<deptId>2344</deptId>
<deptName>BPO</deptName>
<deptAccessCode>BP-2344</deptAccessCode>
</dept>
</depts>
<employees>
<employee>
<name>abc</name>
<dept>
<REFERENCE>
<LocationXPath>/org/depts/dept[2]</LocationXPath>
</REFERENCE>
</dept>
<employee>
</employees>
</org>
now i want to replace the node REFERENCE with actual data at the XPath /org/depts/dept[2].
so the output xml should be like below.
<org>
<depts>
<dept>
<deptId>1009</deptId>
<deptName>IT</deptName>
<deptAccessCode>IT-1009</deptAccessCode>
</dept>
<dept>
<deptId>2344</deptId>
<deptName>BPO</deptName>
<deptAccessCode>BP-2344</deptAccessCode>
</dept>
</depts>
<employees>
<employee>
<name>abc</name>
<dept>
<deptId>2344</deptId>
<deptName>BPO</deptName>
<deptAccessCode>BP-2344</deptAccessCode>
</dept>
<employee>
</employees>
</org>
i have several REFERENCE nodes in different elements referencing to different xpaths across the xml tree, which i need to replace them with actual data.
<someWhereInTheXmlTree>
<sometag>
<REFERENCE>
<LocationXPath>some/reference[1]/to/a/node[3]/in/the[4]/same/xml</LocationXPath>
</REFERENCE>
</sometag>
<someWhereInTheXmlTree>
...
<ffff>
<bbbb>
<REFERENCE>
<LocationXPath>abc/xyz[1]/node[4]/element</LocationXPath>
</REFERENCE>
</bbbb>
<ffff>
please help me on this.
Thanks in advance for the help.
So far i have implemented one XSLT to replace the references but now i am facing unwanted empty name spaces.
Here is my XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tib="http://www.tibco.com/bw/xslt/custom-functions"
xmlns="http://www.realestate.org/residential/2010/schemas" >
<xsl:output omit-xml-declaration="no" indent="yes" method = "xml" />
<xsl:strip-space elements="*"/>
<xsl:param name="myxml" />
<xsl:template match="node()|#*">
<xsl:param name="isNodeToReplace"><xsl:call-template name="ReferenceCheck" /></xsl:param>
<xsl:choose>
<xsl:when test="$isNodeToReplace='true'">
<xsl:call-template name="replaceWithData">
<xsl:with-param name="ref"><xsl:value-of select="." /></xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="ReferenceCheck">
<xsl:choose>
<xsl:when test="name(child::*[1])='REFERENCE' and name(child::*[1]//child::*[1])='LocationXPath'">true</xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="replaceWithData">
<xsl:param name="ref" />
<xsl:copy-of select="tib:evaluate($myxml,$ref)" />
</xsl:template>
</xsl:stylesheet>
in the above XSLT i am passing the entire xml (same xml, which is being processed) as a parameter $myxml
Below is my sample input XML --this is just a snippet of xml,The actual xml file which i am dealing with is too large and contains so complex tree structure.How ever this sample xml is suffice enough to produce my problem.
Input file
<?xml version="1.0" encoding="UTF-8"?>
<org xmlns="http://www.realestate.org/residential/2010/schemas">
<depts>
<dept>
<deptId>1</deptId>
<deptName>health</deptName>
<deptAccessCode>HL007845</deptAccessCode>
</dept>
</depts>
<employees>
<employee>
<name>TOM</name>
<dept>
<REFERENCE>
<LocationXPath>/org/depts/dept[1]</LocationXPath>
</REFERENCE>
</dept>
</employee>
</employees>
</org>
my output file
<?xml version="1.0" encoding="UTF-8"?>
<org xmlns="http://www.realestate.org/residential/2010/schemas">
<depts>
<dept>
<deptId>1</deptId>
<deptName>health</deptName>
<deptAccessCode>HL007845</deptAccessCode>
</dept>
</depts>
<employees>
<employee>
<name>TOM</name>
<dept xmlns="">
<deptId>1</deptId>
<deptName>health</deptName>
<deptAccessCode>HL007845</deptAccessCode>
</dept>
</employee>
</employees>
</org>
Where as Expected output
<?xml version="1.0" encoding="UTF-8"?>
<org xmlns="http://www.realestate.org/residential/2010/schemas">
<depts>
<dept>
<deptId>1</deptId>
<deptName>health</deptName>
<deptAccessCode>HL007845</deptAccessCode>
</dept>
</depts>
<employees>
<employee>
<name>TOM</name>
<dept>
<deptId>1</deptId>
<deptName>health</deptName>
<deptAccessCode>HL007845</deptAccessCode>
</dept>
</employee>
</employees>
</org>
so i am getting unwanted empty name space in << dept xmlns="">> in the replaced root element.
Hope this could clearly explain my problem
Thanks in Advance
ultimately i have found the solution at the link below to remove the unwanted empty name spaces.
http://social.msdn.microsoft.com/forums/en-US/xmlandnetfx/thread/0de59291-ef3a-4a4c-9ca5-17923b16a504
Here is the new XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tib="http://www.tibco.com/bw/xslt/custom-functions"
xmlns="http://www.realestate.org/residential/2010/schemas" >
<xsl:output omit-xml-declaration="no" indent="yes" method = "xml" />
<xsl:strip-space elements="*"/>
<xsl:param name="myxml" />
<xsl:template match="node()|#*">
<xsl:param name="isNodeToReplace"><xsl:call-template name="ReferenceCheck" /></xsl:param>
<xsl:choose>
<xsl:when test="$isNodeToReplace='true'">
<xsl:call-template name="replaceWithData">
<xsl:with-param name="ref"><xsl:value-of select="." /></xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="ReferenceCheck">
<xsl:choose>
<xsl:when test="name(child::*[1])='REFERENCE' and name(child::*[1]//child::*[1])='LocationXPath'">true</xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="replaceWithData">
<xsl:param name="ref" />
<xsl:apply-templates select="tib:evaluate($myxml,$ref)" mode="move-to-namespace">
<xsl:with-param name="namespace" select="'http://www.realestate.org/residential/2010/schemas'" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*" mode="move-to-namespace">
<xsl:param name="namespace" />
<xsl:element name="{local-name()}" namespace="{$namespace}">
<xsl:copy-of select="#*" />
<xsl:apply-templates select="node()" mode="move-to-namespace">
<xsl:with-param name="namespace" select="$namespace"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<xsl:template match="text() | comment() | processing-instruction()" mode="move-to-namespace">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
if any xslt expert refines it further to avoid any unnecessary instruction with proper explanation its very glad.
Thanks in advance
In general:
Not posiible in pure XSLT 1.0.
Not possible in pure XSLT 2.0
May be possible in pure XSLT 3.0 -- read about the
<xsl:evaluate> instruction.
In XSLT 1.0 you may be lucky if your XSLT processor implements the EXSLT dyn:evaluate() extension function (a few do).
Otherwize, you will have to write an extension function to select the
nodes and return them back.
If there are restrictions on the syntax of the XPath expressions, then it may be possible to implement a pure XSLT 1.0 solution.
This is not possible in pure XSLT 1.0. You have to use extension function, e.g. Xalan evaluate expression
I would like to select a node and modify its attributes and child-nodes using an
xsl:script function. In addition, templates matching child-nodes of that node should
STILL perform their job (after script is done processing the node).
Can it be done using XSLT?
Can you please provide an example / skeleton for such a transformation?
Yes, it can be done. I don't seem to see what the problem is because the XML (or whatever output) of an XSL script is buffered independently from its input.
This is illustrated in the following example whereby a simple XSL script copies an input XML document mostly as-is, changing a few things:
the root element name and attribute
flattening by removing the element from the hierarchy
dropping the results/date element
rename the item's 'source' attribute 'origin'
change the item's 'level' attribute value
rename the FirstName and LastName elements of the item elements
Sample input
<?xml version="1.0" encoding="ISO-8859-1"?>
<MyRoot version="1.2">
<results>
<info>Alpha Bravo</info>
<author>Employee No 321</author>
<date/>
<item source="www" level="6" cost="33">
<FirstName>Jack</FirstName>
<LastName>Frost</LastName>
<Date>1998-10-30</Date>
<Organization>Lemon growers association</Organization>
</item>
<item source="db-11" level="1" cost="65" qry="routine 21">
<FirstName>Mike</FirstName>
<LastName>Black</LastName>
<Date>2006-10-30</Date>
<Organization>Ford Motor Company</Organization>
</item>
</results>
</MyRoot>
Output produced
<?xml version="1.0" encoding="utf-16"?>
<MyNewRoot version="0.1">
<author>Employee No 321</author>
<info>Alpha Bravo</info>
<item cost="33" origin="www" level="77">
<GivenName>Jack</GivenName>
<FamilyName>Frost</FamilyName>
<Date>1998-10-30</Date>
<Organization>Lemon growers association</Organization>
</item>
<item cost="65" qry="routine 21" origin="db-11" level="77">
<GivenName>Mike</GivenName>
<FamilyName>Black</FamilyName>
<Date>2006-10-30</Date>
<Organization>Ford Motor Company</Organization>
</item>
</MyNewRoot>
XSL script
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="#default">
<xsl:template match="MyRoot">
<xsl:call-template name="MainTemplate">
</xsl:call-template>
</xsl:template>
<xsl:template name="MainTemplate">
<MyNewRoot version="0.1">
<xsl:copy-of select="results/author" />
<xsl:copy-of select="results/info" />
<xsl:for-each select="results/item">
<xsl:call-template name="FixItemElement"/>
</xsl:for-each>
</MyNewRoot>
</xsl:template>
<xsl:template name="FixItemElement">
<xsl:copy>
<xsl:copy-of select="#*[not(name()='source' or name()='level')]" />
<xsl:attribute name="origin">
<xsl:value-of select="#source"/>
</xsl:attribute>
<xsl:attribute name="level">
<xsl:value-of select="77"/>
</xsl:attribute>
<xsl:for-each select="descendant::*">
<xsl:choose>
<xsl:when test="local-name(.) = 'FirstName'">
<GivenName>
<xsl:value-of select="."/>
</GivenName>
</xsl:when>
<xsl:when test="local-name(.) = 'LastName'">
<FamilyName>
<xsl:value-of select="."/>
</FamilyName>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:copy>
</xsl:template>