XSLT Error - element not declared - xslt

I need to transform the following xml
<node1 xmlns:ns1="namespace1">
<node2 xmlns:ns2="namespace2">
<node3...>
<node4...>
</node2>
</node1>
To
<NewNode2 xmlns:ns2="namespace2">
<node3...>
<node4...>
</NewNode2>
I use this XSLT
<?xml version="1.0" encoding="utf-16" ?>
<xsl:stylesheet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:ns1="namespace1"
xmlns:ns2="namespace2">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="/" />
</xsl:template>
<xsl:template match="/" >
<NewNode2>
<xsl:copy-of select="//*[local-name()='node2']" />
</NewNode2 >
</xsl:template>
</xsl:stylesheet>
But this throws error in visual studio -
input validation error - element 'namespace1:node1' not declared
and element 'namespace2:node2' not declared

Your goal van be achieved with the following XSLT:
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="namespace1"
xmlns:ns2="namespace2"
xmlns="namespace2">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="#*|*">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ns1:node1">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="ns2:node2">
<ns2:NewNode2>
<xsl:apply-templates />
</ns2:NewNode2>
</xsl:template>
</xsl:stylesheet>
The statement <xsl-templates select="/" /> inside <xsl:stylesheet match="/"> causes an infinite loop, since the matching template for the called 'root' is the template itself, which is calling the root.
The template <xsl template match="#*|*> belongs in almost all stylesheets, because this copies the content of all elements which are not otherwise specified (most applicable selection rule).
The other two templates specify specific behaviour for ns1:node1 (do not output any information at this level, but continue the template matching process for all further levels) and for ns2:node2 (create ns2:NewNode2 and continue to include all other availble information inside).

Related

XSLT to replace a namespace and also add a new (unused) namespace

I want to replace the namespace of the following XML Document
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Document xmlns:ns0="http://mydata.com/H2H/Automation">
<CstmrCdtTrfInitn>
<GrpHdr>
</GrpHdr>
</CstmrCdtTrfInitn>
</ns0:Document>
with the following
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CstmrCdtTrfInitn>
<GrpHdr>
</GrpHdr>
</CstmrCdtTrfInitn>
</Document>
Any idea about XSLT which can convert this?
I have tried the following XSL, but it is adding the namespace with second Node and also not able to remove first namespace.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes"/>
<xsl:template match="/*">
<xsl:element name="{local-name()}" namespace="http://www.w3.org/2001/XMLSchema-instance">
<xsl:copy-of select="./*" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Your requirement can be a bit tricky: replacing the default namespace of the Document element is straightforward. But adding the unused xslns:xsi namespace in XSLT-1.0 requires the EXSLT extension and a special technique explained by Michael Kay in reply to this question. It involves creating an unused element in a global variable whose namespace is then copied in the template replacing the default namespace. In XSLT-2.0 and above this would be easier (see below).
The EXSLT extension is not available in all XSLT-1.0 processors. But it is necessary to create a node-set from the variable.
So all namespaces are to be defined in the xsl:stylesheet element, and then the root element (here ns0:Document) is matched by a template and replaced with its local-name() part with the new default namespace added, followed by copying the "dummy" namespace of the element defined in the variable.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://mydata.com/H2H/Automation" xmlns:urn="urn:iso:std:iso" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ext="http://exslt.org/common">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<!-- identity template (except elements)-->
<xsl:template match="node()[not(self::*)]|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:variable name="nsXSI">
<xsl:element name="xsi:dummy" namespace="http://www.w3.org/2001/XMLSchema-instance" />
</xsl:variable>
<xsl:template match="ns0:*|*">
<xsl:element name="{local-name()}" namespace="urn:iso:std:iso">
<xsl:copy-of select="ext:node-set($nsXSI)/*/namespace::xsi" />
<xsl:apply-templates select="node() | #*" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output should be as expected, even in XSLT-1.0:
<Document xmlns="urn:iso:std:iso" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CstmrCdtTrfInitn>
<GrpHdr>
</GrpHdr>
</CstmrCdtTrfInitn>
</Document>
The simplified solution requires an XSLT-2.0 capable processor. Then you can use the xsl:namespace instruction as follows and don't need the "dummy" variable:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://mydata.com/H2H/Automation">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<!-- identity template (except elements)-->
<xsl:template match="node()[not(self::element())]|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="ns0:*|*">
<xsl:element name="{local-name(.)}" namespace="urn:iso:std:iso">
<xsl:namespace name="xsi">http://www.w3.org/2001/XMLSchema-instance</xsl:namespace>
<xsl:apply-templates select="node() | #*" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The output is the same.
The above XSLT-2.0 solution could be further simplified by using XSLT-3.0+'s xsl:mode to replace the identity template with
<xsl:mode on-no-match="shallow-copy"/>
Is there a reason why you cannot do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://mydata.com/H2H/Automation"
exclude-result-prefixes="ns0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:element name="{local-name()}" namespace="urn:iso:std:iso">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="/ns0:Document">
<Document xmlns="urn:iso:std:iso" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:apply-templates/>
</Document>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0, copy all child nodes except some

I am trying to copy all child nodes to a specific node, except a few. Haven't been able to get this to work? Any pointers of what I am doing wrong?
Using this XML:
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
<ns0:Header>
<wsse:Sec xmlns:wsse="http://docs.x.org/wsse/">
<saml:Ass xmlns:saml="http://docs.x.org/saml/">
<ds:Sign xmlns:ds="http://docs.x.org/ds/">
<ds:SignVal>SignatureValue</ds:SignVal>
</ds:Sign>
<saml:subj>SubjectValue</saml:subj>
</saml:Ass>
</wsse:Sec>
<To>http://localhost:8080/Test/</To>
<Action>SendTest</Action>
</ns0:Header>
<ns0:Body>...</ns0:Body>
</ns0:Envelope>
The wanted result is to just get the Sec tag and all children:
<wsse:Sec xmlns:wsse="http://docs.x.org/wsse/">
<saml:Ass xmlns:saml="http://docs.x.org/saml/">
<ds:Sign xmlns:ds="http://docs.x.org/ds/">
<ds:SignVal>SignatureValue</ds:SignVal>
</ds:Sign>
<saml:subj>SubjectValue</saml:subj>
</saml:Ass>
</wsse:Sec>
I have tried numerous XSL including this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="Header">
<xsl:copy-of select="*"/>
</xsl:template>
<!-- Exclude these -->
<xsl:template match="To" />
<xsl:template match="Action" />
</xsl:stylesheet>
The result is I get values but no tags...
You have not accounted for namespaces in your XSLT. In your XML, Header is in namespace http://schemas.xmlsoap.org/soap/envelope/, but your XSLT is trying to match a Header in no namespace.
You need to declare the namespaces in your XSLT, and use them in the template matches
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsse="http://docs.x.org/wsse/">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="ns0:Header">
<xsl:copy-of select="wsse:Sec"/>
</xsl:template>
<xsl:template match="ns0:Body" />
</xsl:stylesheet>
Note this XSLT doesn't need templates matching "To" and "Action" because of the explicit copy of wsse:Sec using this approach. However, you do need to template to ensure any test within ns0:Body isn't picked up.
Another approach is to use the identity template, and then you would have the templates to exclude To and Action (and Body)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsse="http://docs.x.org/wsse/">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ns0:Envelope|ns0:Header">
<xsl:apply-templates />
</xsl:template>
<!-- Exclude these -->
<xsl:template match="ns0:Body|To|Action" />
</xsl:stylesheet>
Note there is a template matching ns0:Envelope and ns0:Header as although you don't want these elements themselves, you do need to process the child nodes.
You would need to use XSLT 2 or 3 with
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsse="http://docs.x.org/wsse/"
exclude-result-prefixes="#all"
version="3.0">
<xsl:template match="/">
<xsl:copy-of select="//wsse:Sec" copy-namespaces="no"/>
</xsl:template>
</xsl:stylesheet>
to get the posted result with a simple copy instruction: https://xsltfiddle.liberty-development.net/bnnZVw
In XSLT 1 the copy will always copy the in-scope namespace xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" so to remove it from the result you would need to run your code through some kind of transformation stripping in-scope namespaces (other than the one of the element itself):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wsse="http://docs.x.org/wsse/"
exclude-result-prefixes="wsse"
version="1.0">
<xsl:template match="#*">
<xsl:attribute name="{name()}" namespace="{namespace-uri()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="//wsse:Sec"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bnnZVw/1

remove text from a node in input XML

i am trying to copy entire input xml in a string for further processing and also i have a requirement to remove text from a particular node (plancode) before copying in the variable. May I know how can i achieve this using xslt
INPUT XML:
<CallMember>
<PlanD>
<abcpr>you</abcpr>
<Desd>Protection</Desd>
<plancode>76789</plancode>
<plaDesc>goody</plaDesc>
</PlanD>
<fType>ONLINE</fType>
</CallMember>
XSLT i am trying :
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt" xmlns:func="http://exslt.org/functions" xmlns:dp="http://www.datapower.com/extensions" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:tglfn="http://test.com/tgl" xmlns:date="http://exslt.org/dates-and-times" exclude-result-prefixes="#all" extension-element-prefixes="dp regexp">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="no"/>
<xsl:template match="/">
<xsl:variable name="InputRequest">
<xsl:copy-of select="."/>
</xsl:variable>
<xsl:variable name="modifiedRequest">
<xsl:copy-of select="."/>
<plancode></plancode>
</xsl:variable>
</xsl:template>
OUTput i am expecting in the modifiedRequest variable:
<CallMember>
<PlanD>
<abcpr>you</abcpr>
<Desd>Protection</Desd>
<plancode></plancode> <!-- this value needs to get emptied -->
<plaDesc>goody</plaDesc>
</PlanD>
<fType>ONLINE</fType>
</CallMember>
Use an identity template in combination with an (nearly) empty template for filtering and apply-templates to it:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xslt"
xmlns:func="http://exslt.org/functions"
xmlns:dp="http://www.datapower.com/extensions"
xmlns:regexp="http://exslt.org/regular-expressions"
xmlns:tglfn="http://test.com/tgl"
xmlns:date="http://exslt.org/dates-and-times"
exclude-result-prefixes="#all" extension-element-prefixes="dp regexp">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="no"/>
<!-- identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="InputRequest">
<xsl:copy-of select="."/>
</xsl:variable>
<!-- copies subtree except matching empty template -->
<xsl:variable name="modifiedRequest">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:variable>
</xsl:template>
<!-- (nearly) empty template copies only element without content -->
<xsl:template match="plancode">
<xsl:copy />
</xsl:template>
</xsl:stylesheet>

Require Help in XSLT coding

I am new to XSLT .Kindly help me with the below query :
My Source XML:
<?xml version="1.0" encoding="UTF-8"?>
<ns1:Header1 xmlns:ns1="urn:src:abc">
<Header2>
<Header3>
<field1>1.1.2017</field1>
<field2>12</field2>
<field3> </field3>
</Header3>
</Header2>
</ns1:Header1>
Target/Expected XML
<?xml version="1.0" encoding="UTF-8"?>
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<Header2>
<v1:Header3>
<field1>1.1.2017</field1>
<field2>12</field2>
<field3> </field3>
</v1:Header3>
</Header2>
</ns2:Header1>
And also i need to remove the space/blank between the tag filed3 ( Field3 value sometimes will be blank)
I am using the below code for Transformation
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="2.0">
<xsl:template match="*">
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<Header2>
<xsl:copy-of select="//Header3"/>
</Header2>
</ns2:Header1>
</xsl:template>
</xsl:stylesheet>
I am not able to achieve my Target XML. Kindly help
Many thanks in advance
Regards,
Pavi
Your current template matches * which will match any element, but really you only want it to match the root element.
Additionally, as you want to change Header3 from being in no namespace, to being in the "def.v1" namespace, you should be making use of xsl:apply-templates, not xsl:copy-of
<xsl:template match="/*">
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<xsl:apply-templates />
</ns2:Header1>
</xsl:template>
You would then need a template matching Header3 to change the namespace
<xsl:template match="Header3" xmlns:v1="def.v1">
<v1:Header3>
<xsl:apply-templates />
</v1:Header3>
</xsl:template>
You would then just need the identity template to copy all other nodes without changes.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/*">
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<xsl:apply-templates />
</ns2:Header1>
</xsl:template>
<xsl:template match="Header3" xmlns:v1="def.v1">
<v1:Header3>
<xsl:apply-templates />
</v1:Header3>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See it in action at http://xsltransform.net/jz1PuQb

Add additional namespace with XSLT

I need to add an additional namespace to an already namespaced XML file but only if a particular element does not exist.
My XML doc looks like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<everyone xmlns="AAA" xmlns:ns2="BBB" xmlns:ns3="CCC" company="TestingCorp">
<common>Stuff Here</common>
<ns2:person id="123">
<ns3:firstname>Billy</ns3:firstname>
<ns2:lastname>Bobby</ns2:lastname>
</ns2:person>
</everyone>
... and if there is no ns3:firstname element in the person element, I'd like to add a new namespace and (e.g. xmlns:frog="FFF") and also an additional element within person as shown below:
Desired Output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<everyone xmlns="AAA" xmlns:ns2="BBB" xmlns:ns3="CCC" xmlns:frog="FFF" company="TestingCorp">
<common>Stuff Here</common>
<ns2:person id="123">
<frog:title>
<Master/>
</frog:title>
<ns2:lastname>Bobby</ns2:lastname>
</ns2:person>
</everyone>
My XSL doc currently is:
<?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" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Copy Everything -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:element name="ns:{local-name()}">
<xsl:attribute name="frog">fff</xsl:attribute>
<xsl:apply-templates select="node()|#*" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
.... unfortunately this does not work.
I've tried lots of different things but can't seem to achieve this using XSLT v1.0. Any help would be greatly appreciated.
First you need to declare the various namespaces in your stylesheet, as well as the "AAA" default namespace
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="AAA"
xmlns:frog="FFF"
xmlns:ns2="BBB"
xmlns:ns3="CCC">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Copy Everything -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- for a Person with no firstname, add a frog:title -->
<xsl:template match="ns2:person[not(ns3:firstname)]">
<xsl:copy>
<!-- must handle attributes before elements/text nodes -->
<xsl:apply-templates select="#*" />
<frog:title>
<Master/>
</frog:title>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This will produce
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<everyone xmlns="AAA" xmlns:ns2="BBB" xmlns:ns3="CCC" company="TestingCorp">
<common>Stuff Here</common>
<ns2:person id="123">
<frog:title xmlns:frog="FFF">
<Master/>
</frog:title>
<ns2:lastname>Bobby</ns2:lastname>
</ns2:person>
</everyone>
If the xmlns:frog absolutely must be on the everyone element rather than on each frog:title then you could add another template
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="document('')/xsl:stylesheet/namespace::frog" />
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
to copy the namespace declaration off the stylesheet element (though this would mean that every output document has an xmlns:frog declaration even if it doesn't involve any frog:* elements).
Edit: apparently Xalan doesn't like the copy-of namespaces from document(''), as an alternative, if you know that the document element will always have the same name then you can hard code that as a literal result element
<xsl:template match="/*">
<everyone xmlns:frog="FFF">
<xsl:copy-of select="namespace::*" />
<xsl:apply-templates select="#*|node()" />
</everyone>
</xsl:template>
(technically it will do what you want even without the explicit xmlns:frog in this template, since literal result elements always get the namespace declarations that are in scope at the point in the stylesheet where they are declared, but the intention is arguably clearer if you include it)
This mailing list post gives some possible insights into the reason for document('') not working as it should.