I have a xml file and namespaces are given in the xml file. What I need to do is to use the namespaces given in the xml file only and qualify the xml file. Below is the sample xml file.
<Ticketing xmlns="ticketing.4.0" mfAction="BOOKING">
<Reference>
<Code>190</Code>
</Reference>
<BookingID>194283532</BookingID>
<BookingCode>MCHOI190</BookingCode>
<BookingDate>2011-04-21T15:40:04.000</BookingDate>
<Persons>
<Person>
<Code>ADULT</Code>
<Count>2</Count>
</Person>
<Person>
<Code>CHILD</Code>
<Count>2</Count>
</Person>
</Persons>
<CreditCards>
<CreditCard BookingType="BOOKING">
<BookCreditCard xmlns="creditcard.3.0">
<CardCode>VS</CardCode>
<CardNumber>4444333322221111</CardNumber>
<CardExpire>2011-12-31</CardExpire>
</BookCreditCard>
</CreditCard>
</CreditCards>
</Ticketing>
I have to use the namespaces already present in the xml file and give them a prefix and qualify the xml with those namespaces. The output should be like below:-
<ticket:Ticketing xmlns:ticket="ticketing.4.0" mfAction="BOOKING">
<ticket:Reference>
<ticket:Code>190</ticket:Code>
</ticket:Reference>
<ticket:BookingID>194283532</ticket:BookingID>
<ticket:BookingCode>MCHOI190</ticket:BookingCode>
<ticket:BookingDate>2011-04-21T15:40:04.000</ticket:BookingDate>
<ticket:Persons>
<ticket:Person>
<ticket:Code>ADULT</ticket:Code>
<ticket:Count>2</ticket:Count>
</ticket:Person>
<ticket:Person>
<ticket:Code>CHILD</ticket:Code>
<ticket:Count>2</ticket:Count>
</ticket:Person>
</ticket:Persons>
<ticket:CreditCards>
<ticket:CreditCard BookingType="BOOKING">
<credit:BookCreditCard xmlns:credit="creditcard.3.0">
<credit:CardCode>VS</credit:CardCode>
<credit:CardNumber>4444333322221111</credit:CardNumber>
<credit:CardExpire>2011-12-31</credit:CardExpire>
</credit:BookCreditCard>
</ticket:CreditCard>
</ticket:CreditCards>
</ticket:Ticketing>
Can someone suggest how to implement this.
Thanks
Rudra
I'm not sure is the best way to do it. But it does the job:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="*">
<xsl:element name="ticket:{name()}" namespace="ticketing.4.0">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="BookCreditCard|BookCreditCard//*"">
<xsl:element name="credit:{name()}" namespace="creditcard.3.0">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
In XSLT 1.0 there is no guaranteed solution, because implementations are free to use any prefix in the output file that they want. However most processors do the reasonable thing, and #empo's solution will therefore usually work. In XSLT 2.0 it is guaranteed to work.
I would be inclined to use the namespace to control which template is chosen, something like this:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ticket="ticketing.4.0" xmlns:credit="creaditcard.3.0">
<xsl:output indent="yes"/>
<xsl:template match="ticket:*">
<xsl:element name="ticket:{local-name()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="credit:*">
<xsl:element name="credit:{local-name()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
A more general solution (no hardcoded prefixes and can work with any number of namespaces):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="*">
<xsl:element name=
"{substring(namespace-uri(),1,6)}:{local-name()}"
namespace="{namespace-uri()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<Ticketing xmlns="ticketing.4.0" mfAction="BOOKING">
<Reference>
<Code>190</Code>
</Reference>
<BookingID>194283532</BookingID>
<BookingCode>MCHOI190</BookingCode>
<BookingDate>2011-04-21T15:40:04.000</BookingDate>
<Persons>
<Person>
<Code>ADULT</Code>
<Count>2</Count>
</Person>
<Person>
<Code>CHILD</Code>
<Count>2</Count>
</Person>
</Persons>
<CreditCards>
<CreditCard BookingType="BOOKING">
<BookCreditCard xmlns="creditcard.3.0">
<CardCode>VS</CardCode>
<CardNumber>4444333322221111</CardNumber>
<CardExpire>2011-12-31</CardExpire>
</BookCreditCard>
</CreditCard>
</CreditCards>
</Ticketing>
the wanted, correct result is produced:
<ticket:Ticketing xmlns:ticket="ticketing.4.0" mfAction="BOOKING">
<ticket:Reference>
<ticket:Code>190</ticket:Code>
</ticket:Reference>
<ticket:BookingID>194283532</ticket:BookingID>
<ticket:BookingCode>MCHOI190</ticket:BookingCode>
<ticket:BookingDate>2011-04-21T15:40:04.000</ticket:BookingDate>
<ticket:Persons>
<ticket:Person>
<ticket:Code>ADULT</ticket:Code>
<ticket:Count>2</ticket:Count>
</ticket:Person>
<ticket:Person>
<ticket:Code>CHILD</ticket:Code>
<ticket:Count>2</ticket:Count>
</ticket:Person>
</ticket:Persons>
<ticket:CreditCards>
<ticket:CreditCard BookingType="BOOKING">
<credit:BookCreditCard xmlns:credit="creditcard.3.0">
<credit:CardCode>VS</credit:CardCode>
<credit:CardNumber>4444333322221111</credit:CardNumber>
<credit:CardExpire>2011-12-31</credit:CardExpire>
</credit:BookCreditCard>
</ticket:CreditCard>
</ticket:CreditCards>
</ticket:Ticketing>
Do note: The first 6 characters of the namespace-uri of each element are used for the prefix of the corresponding generated name. Therefore this solution works correctly as long as the starting 6 characters of any namespace-uri of an element obey the sintactical rules for NCName.
Related
Is there any way on how to avoid populating the namespace of common: prefix in the output? And, this namespace should replace as a default namespace. I have this sample file:
INPUT:
<IntraConsignment xmlns="http://www.minfin.fgov.be/IntraConsignment" xmlns:common="http://www.minfin.fgov.be/InputCommon" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.minfin.fgov.be/IntraConsignment NewICO-in_v0_7.xsd" IntraListingsNbr="1">
<Representative>
<common:RepresentativeID identificationType="NVAT" issuedBy="BE">9876941603</common:RepresentativeID>
</Representative>
<IntraListing SequenceNumber="1" ClientsNbr="1" AmountSum="1000.00">
<Declarant>
<common:VATNumber>9876941603</common:VATNumber>
</Declarant>
<Period>
<Month>07</Month>
</Period>
<IntraClient SequenceNumber="1">
<CompanyVATNumber issuedBy="DE">123456</CompanyVATNumber>
</IntraClient>
</IntraListing>
</IntraConsignment>
And, I need to remove the common: prefix in the XML and the elements that doesn't have a prefix should have a ns2: prefix.
I have this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://www.minfin.fgov.be/IntraConsignment" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:common="http://www.minfin.fgov.be/InputCommon" xmlns="http://www.minfin.fgov.be/InputCommon" exclude-result-prefixes="xs xsi xsl common">
<xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="common:*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:element name="ns2:{local-name()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
My xslt is quite working but the namespace xmlns="http://www.minfin.fgov.be/InputCommon" did not appear in the output.
GENERATED OUTPUT:
<ns2:IntraConsignment xmlns:ns2="http://www.minfin.fgov.be/IntraConsignment" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.minfin.fgov.be/IntraConsignment NewICO-in_v0_7.xsd" IntraListingsNbr="1">
<ns2:Representative>
<RepresentativeID xmlns="http://www.minfin.fgov.be/InputCommon" identificationType="NVAT" issuedBy="BE">9876941603</RepresentativeID>
</ns2:Representative>
<ns2:IntraListing SequenceNumber="1" ClientsNbr="1" AmountSum="1000.00">
<ns2:Declarant>
<VATNumber xmlns="http://www.minfin.fgov.be/InputCommon">9876941603</VATNumber>
</ns2:Declarant>
<ns2:Period>
<ns2:Month>07</ns2:Month>
</ns2:Period>
<ns2:IntraClient SequenceNumber="1">
<ns2:CompanyVATNumber issuedBy="DE">123456</ns2:CompanyVATNumber>
<ns2:Amount>1000.00</ns2:Amount>
</ns2:IntraClient>
</ns2:IntraListing>
</ns2:IntraConsignment>
EXPECTED:
<ns2:IntraConsignment xmlns:ns2="http://www.minfin.fgov.be/InputCommon" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.minfin.fgov.be/IntraConsignment NewICO-in_v0_7.xsd" xmlns="http://www.minfin.fgov.be/InputCommon" IntraListingsNbr="1">
<ns2:Representative>
<RepresentativeID identificationType="NVAT" issuedBy="BE">9876941603</RepresentativeID>
</ns2:Representative>
<ns2:IntraListing SequenceNumber="1" ClientsNbr="1" AmountSum="1000.00">
<ns2:Declarant>
<VATNumber>9876941603</VATNumber>
</ns2:Declarant>
<ns2:Period>
<ns2:Month>07</ns2:Month>
</ns2:Period>
<ns2:IntraClient SequenceNumber="1">
<ns2:CompanyVATNumber issuedBy="DE">123456</ns2:CompanyVATNumber>
<ns2:Amount>1000.00</ns2:Amount>
</ns2:IntraClient>
</ns2:IntraListing>
</ns2:IntraConsignment>
With XSLT 1.0 the choice of namespace prefixes is left largely to the XSLT processor, whereas XSLT 2.0 is much more prescriptive. However, most XSLT 1.0 processors do the "right thing" even though they're not strictly required to.
You basically seem to have two rules:
(a) elements in namespace http://www.minfin.fgov.be/IntraConsignment should be copied using the prefix ns2:
<xsl:template match="x:*" xmlns:x="http://www.minfin.fgov.be/IntraConsignment">
<xsl:element name="ns2:{local-name()}" namespace="http://www.minfin.fgov.be/IntraConsignment">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
(b) elements in namespace http://www.minfin.fgov.be/InputCommon should be output in the default namespace:
<xsl:template match="x:*" xmlns:x="http://www.minfin.fgov.be/InputCommon">
<xsl:element name="{local-name()}" namespace="http://www.minfin.fgov.be/InputCommon">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
I have a scenario where the input(source) xml is having an element which contains a valid well formed xml as string. I am trying to write an xslt that would give me the text value of that desired element which contains the payload xml. In essence, output should only be text of the element that contains it. Here is what I am trying, am I missing something obvious here. I am using xslt 1.0
Thanks.
Input xml:
<BatchOrders xmlns="http://Microsoft.ABCD.OracleDB/STMT">
<BatchOrdersRECORD>
<BatchOrdersRECORD>
<ActualPayload>
<PersonName>
<PersonGivenName>CaptainJack</PersonGivenName>
<PersonMiddleName>Walter</PersonMiddleName>
<PersonSurName>Sparrow</PersonSurName>
<PersonNameSuffixText>Sr.</PersonNameSuffixText>
</PersonName>
</ActualPayload>
</BatchOrdersRECORD>
</BatchOrdersRECORD>
</BatchOrders>
Xslt:
<?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="xml" indent="yes"/>
<xsl:template match="text()|#*" name="sourcecopy" mode="xml-to-string">
<xsl:value-of select="*"/>
</xsl:template>
<xsl:template name="xml-to-string-called-template">
<xsl:param name ="param1">
<xsl:element name ="DestPayload">
<xsl:text disable-output-escaping ="yes"><![CDATA[</xsl:text>
<xsl:call-template name ="sourcecopy"/>
<xsl:text disable-output-escaping ="yes">]]></xsl:text>
</xsl:element>
</xsl:param>
</xsl:template>
</xsl:stylesheet>
Desired Output:
<PersonName>
<PersonGivenName>CaptainJack</PersonGivenName>
<PersonMiddleName>Walter</PersonMiddleName>
<PersonSurName>Sparrow</PersonSurName>
<PersonNameSuffixText>Sr.</PersonNameSuffixText>
</PersonName>
Do you really need the mode="xml-to-string"?
Change
<xsl:template match="text()|#*" name="sourcecopy" mode="xml-to-string">
<xsl:value-of select="*"/>
</xsl:template>
to
<xsl:template match="text()|#*" name="sourcecopy">
<xsl:value-of select="." disable-output-escaping ="yes"/>
</xsl:template>
Would this template suffice?
The requirement is to find the duplicate element(BaseName) in XML and marked the parent element(Account) with isDuplicate attribute. The XSL is working correctly when the input XML RootElement has no namespaces. When the root element has namespace then I get empty object. I am not sure why the namespace is causing XSL to generate empty output. Any help to get the right output would be greatly appreciated.`
Input XML WITH NAMESPACE
<?xml version="1.0"?>
<objects xmlns="urn:s.sexmaple.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Account>
<Id>001A00F</Id>
<RecordTypeId>012A00</RecordTypeId>
<BaseName>EFGH</BaseName>
</Account>
<Account>
<Id>001A0</Id>
<RecordTypeId>012A0</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
<Account>
<Id>001A</Id>
<RecordTypeId>012A</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
</objects>
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml"
version="1.0"
encoding="UTF-8"
indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="node()|#*">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="Accounts">
<objects>
<xsl:for-each select="//Account">
<xsl:sort select="BaseName" />
<xsl:apply-templates select="." />
</xsl:for-each>
</objects>
</xsl:variable>
<xsl:variable name="unqentity">
<objects>
<xsl:for-each select="$Accounts/objects/Account">
<xsl:choose>
<xsl:when test="not(following-sibling::Account/BaseName=./BaseName) and not(preceding-sibling::Account/BaseName=./BaseName) ">
<xsl:copy-of select="." />
</xsl:when>
<xsl:otherwise>
<Account>
<xsl:attribute name="isDuplicate">yes</xsl:attribute>
<xsl:for-each select="child::*">
<xsl:element name="{name()}">
<xsl:copy-of select="#*|node()" />
</xsl:element>
</xsl:for-each>
</Account>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</objects>
</xsl:variable>
<xsl:copy-of select="$unqentity" />
</xsl:template>
</xsl:stylesheet>
Output XML WHEN INPUT XML HAS NAMESPACE
<?xml version="1.0" encoding="UTF-8"?>
<objects/>
Output XML when Input has no Namespaces
<?xml version="1.0" encoding="UTF-8"?>
<objects>
<Account>
<Id>001A00F</Id>
<RecordTypeId>012A00</RecordTypeId>
<BaseName>EFGH</BaseName>
</Account>
<Account isDuplicate="yes">
<Id>001A0</Id>
<RecordTypeId>012A0</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
<Account isDuplicate="yes">
<Id>001A</Id>
<RecordTypeId>012A</RecordTypeId>
<BaseName>ABCD</BaseName>
</Account>
</objects>
When you have a namespace, it means the element within the namespace is not the same as an element without a namespace (or indeed an element in a different name space).
This means when you do this in your XSLT...
<xsl:for-each select="//Account">
You are looking for an Account element with no namespace, and so it will not match the Account element in your source XML, which is in the amusingly titled "urn:s.sexmaple.com" (which I suspect is a misspelling)
As you are using XSLT2.0 though, there is a simple way to get around this, by specifying a default namespace for any xpath expressions, using the xpath-default-namespace. Normally, this may be enough, but you have slightly complicated matters by creating new elements within a variable, which you then later try to select.
<xsl:for-each select="$Accounts/objects/Account">
This means when you create the objects and Account elements in the $Accounts variable, they will need to be part of the namespace too.
To cut to the chase, this is what your xsl:stylesheet element needs to look like
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns="urn:s.sexmaple.com"
xpath-default-namespace="urn:s.sexmaple.com">
So, the xpath-default-namespace="urn:s.sexmaple.com" is used to match elements in your source XML, whilst the xmlns="urn:s.sexmaple.com" is used to ensure the elements you create in the variable have this namespace and can be matched later on.
Having said all that, you have rather over-complicated your whole XSLT. Are you simply trying to add an IsDuplicate attribute to Account elements with the same BaseName? Well, create a key to look up duplicates, like so
<xsl:key name="account" match="Account" use="BaseName" />
Then you can look up duplicates like so:
<xsl:if test="key('account', BaseName)[2]">
<xsl:attribute name="isDuplicate">Yes</xsl:attribute>
</xsl:if>
Try this XSLT, which should give the same results
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="urn:s.sexmaple.com">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="account" match="Account" use="BaseName" />
<xsl:template match="Account">
<xsl:copy>
<xsl:if test="key('account', BaseName)[2]">
<xsl:attribute name="isDuplicate">Yes</xsl:attribute>
</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>
Note how this only needs to use xpath-default-namespace as it is not creating whole new elements, just copying existing ones (which get their namespace copied across too).
The reason your input XML without a namespace works were the one with a namespace doesn't, isn't because of the input XML, but because of the XSLT stylesheet.
When your XML file has a default namespace, that namespace will need to be declared in the stylesheet itself.
For example, with the following XML:
<test xmlns="test.xml.schema">
<element>Content</element>
</test>
When I apply the following XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<out>
<namespace>None</namespace>
<xsl:apply-templates />
</out>
</xsl:template>
<xsl:template match="test">
<test out="True">hello</test>
</xsl:template>
<xsl:template match="*"/>
</xsl:stylesheet>
The output is just:
<out><namespace>None</namespace></out>
The <xsl:template match="test"> can't match on the test element in the input xml, as it is actually test.xml.schema:test while the match in the stylesheet, with no namespace is actually :test. Thus no match is possible.
However, when we just add a namespace for the input document adn modify the template, like so:
<xsl:stylesheet xmlns:t="test.xml.schema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<out>
<namespace>test.xml.schema</namespace>
<xsl:apply-templates />
</out>
</xsl:template>
<xsl:template match="t:test">
<test out="True">hello</test>
</xsl:template>
<xsl:template match="*"/>
</xsl:stylesheet>
The output becomes:
<out xmlns:t="test.xml.schema">
<namespace>test.xml.schema</namespace>
<test out="True">hello</test>
</out>
Its important to note that the namespace abbreviation in the input document and XSL don't need to be the same (eg. blank vs. "t"), but the namespaces themselfs do: (e.g both blank and "t" must be bound to test.xml.schema).
Also note, that using a default namespace in XSLT can be fraught with issues. So its best to use declared namespaces in XSLT.
I am using Microsoft's XSLT processor (1.0 only)
XML opening lines:
<?xml version="1.0" encoding="utf-8"?>
<Header xmlns="http:\\OldNameSpace.com">
<Detail>
Have the following XSLT template to pick up the <Header> element of my document and change its namespace.
<xsl:template match="*">
<xsl:element name="{name()}" xmlns="http:\\NewNameSpace.com">
<xsl:copy-of select="#*"/>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
Which turns <Header xmlns="http:\\OldNameSpace.com"> Into <Header xmlns="http:\\NewNameSpace.com">
However I now need to add a second namespace to this so that I get the following output:
<Header xmlns="NewNameSpace.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
I have tried using:
<xsl:template match="*">
<xsl:element name="{name()}" xmlns="NewNameSpace.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:copy-of select="#*"/>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
However I still only get the same output as the original XSLT template.
Can anyone enlighten to me as to why this is?
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:old="http:\\OldNameSpace.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="old xsi">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pNewNamespace" select="'http:\\NewNameSpace.com'"/>
<xsl:variable name="vXsi" select="document('')/*/namespace::*[name()='xsi']"/>
<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="{$pNewNamespace}">
<xsl:copy-of select="$vXsi"/>
<xsl:copy-of select="#*"/>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document:
<Header xmlns="http:\\OldNameSpace.com">
<Detail/>
</Header>
produces (what I guess is) the wanted, correct result:
<Header xmlns="http:\\NewNameSpace.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Detail/>
</Header>
xsl:element (unlike literal result elements) does not copy all in scope namespaces to the result, just the namespace required for the element name 9either implicitly from its name or as specified with the namespace argument).
xslt2 adds an xsl:namespace instruction for this case but in xslt1 the easiest thing to do is
where
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
somewhere on an ancestor (eg on xsl:stylesheet.)
that will add a spurious xsi:tmp="" to the output but also then a namespace declaration,
If you actually need an attribute in this namespace eg xsi:type use that instead of tmp in the above and you are done. If you don't mind the extra, possibly invalid attribute in the xsi namespace you are done. Otherwise do the above in a variable, then use msxsl:node-set to query in to the variable and remove the spurious extra attribute.
If you know statically what namespace you want to generate, then the easiest way to do it in XSLT 1.0 is using xsl:copy-of. Create a source document <dummy xmlns:xsi="http://whatever"/>,
and then do <xsl:copy-of select="document('dummy.xml')/*/namespace::xsi"/> inside your call of xsl:element.
I need to change namespaces in the root element as follows:
input document:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xsi:schemaLocation="urn:isbn:1-931666-22-9 http://www.loc.gov/ead/ead.xsd"
xmlns:ns2="http://www.w3.org/1999/xlink" xmlns="urn:isbn:1-931666-22-9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
desired output:
<foo audience="external" xsi:schemaLocation="urn:isbn:1-931666-22-9
http://www.loc.gov/ead/ead.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="urn:isbn:1-931666-22-9">
I was trying to do it as I copy over the whole document and before I give any other transformation instructions, but the following doesn't work:
<xsl:template match="* | processing-instruction() | comment()">
<xsl:copy copy-namespaces="no">
<xsl:for-each select=".">
<xsl:attribute name="audience" select="'external'"/>
<xsl:namespace name="xlink" select="'http://www.w3.org/1999/xlink'"/>
</xsl:for-each>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
Thanks for any advice!
XSLT 2.0 isn't necessary to solve this problem.
Here is an XSLT 1.0 solution, which works equally well as XSLT 2.0 (just change the version attribute to 2.0):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xlink="http://www.w3.org/1999/xlink"
exclude-result-prefixes="xlink"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select=
"namespace::*
[not(name()='ns2')
and
not(name()='')
]"/>
<xsl:copy-of select=
"document('')/*/namespace::*[name()='xlink']"/>
<xsl:copy-of select="#*"/>
<xsl:attribute name="audience">external</xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When the above transformation is applied on this XML document:
<foo
xsi:schemaLocation="urn:isbn:1-931666-22-9 http://www.loc.gov/ead/ead.xsd"
xmlns:ns2="http://www.w3.org/1999/xlink"
xmlns="urn:isbn:1-931666-22-9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
the wanted result is produced:
<foo xmlns="urn:isbn:1-931666-22-9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xlink="http://www.w3.org/1999/xlink"
xsi:schemaLocation="urn:isbn:1-931666-22-9 http://www.loc.gov/ead/ead.xsd"
audience="external"/>
You should really be using the "identity template" for this, and you should always have it on hand. Create an XSLT with that template, call it "identity.xslt", then into the current XSLT. Assume the prefix "bad" for the namespace you want to replace, and "good" for the one you want to replace it with, then all you need is a template like this (I'm at work, so forgive the formatting; I'll get back to this when I'm at home): ... If that doesn't work in XSLT 1.0, use a match expression like "*[namespace-uri() = 'urn:bad-namespace'", and follow Dimitre's instructions for creating a new element programmatically. Within , you really need to just apply-template recursively...but really, read up on the identity template.