How to replace element with XSLT by keeping the namespace prefix - xslt

I am looking for a way to replace the value of a field from a xml file by using xslt. Everything works great besides the namespace prefix. In the source file below I want to change the Password element value.
<?xml version="1.0" encoding="utf-8"?>
<ns0:MYXML xmlns:ns0="http://www.me.com/myxml" xmlns="http://www.me.com/myxml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:Header>
<ns0:Infromation>
<ns0:From>
<ns0:Credential>
<ns0:User>jeff</ns0:User>
<Password xmlns="">OLD VALUE</Password>
</ns0:Credential>
</ns0:From>
</ns0:Infromation>
<ns0:Misc>
<ns0:ID>1002</ns0:ID>
<ns0:Timestamp>2012-01-16T09:23:33</ns0:Timestamp>
<ns0:Type>unknown</ns0:Type>
</ns0:Misc>
<ns0:State>
<ns0:ConversationId>d66d9304-9025-a580-e111-5640bf36560d</ns0:ConversationId>
</ns0:State>
</ns0:Header>
</ns0:MYXML>
Here is my result:
<?xml version="1.0" encoding="utf-8"?>
<ns0:MYXML xmlns:ns0="http://www.me.com/myxml" xmlns="http://www.me.com/myxml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:Header>
<ns0:Infromation>
<ns0:From>
<ns0:Credential>
<ns0:User>jeff</ns0:User>
<Password xmlns="">New Value</Password>
</ns0:Credential>
</ns0:From>
</ns0:Infromation>
<ns0:Misc>
<ns0:ID>1002</ns0:ID>
<ns0:Timestamp>2012-01-16T09:23:33</ns0:Timestamp>
<ns0:Type>unknown</ns0:Type>
</ns0:Misc>
<ns0:State>
<ns0:ConversationId>d66d9304-9025-a580-e111-5640bf36560d</ns0:ConversationId>
</ns0:State>
</ns0:Header>
</ns0:MYXML>
And that's the xslt code that I use for the transformation:
<?xml version="1.0" encoding="utf-8"?>
<ns0:MYXML xmlns:ns0="http://www.me.com/myxml" xmlns="http://www.me.com/myxml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:Header>
<ns0:Infromation>
<ns0:From>
<ns0:Credential>
<ns0:User>jeff</ns0:User>
<Password xmlns="">New Value</Password>
</ns0:Credential>
</ns0:From>
</ns0:Infromation>
<ns0:Misc>
<ns0:ID>1002</ns0:ID>
<ns0:Timestamp>2012-01-16T09:23:33</ns0:Timestamp>
<ns0:Type>unknown</ns0:Type>
</ns0:Misc>
<ns0:State>
<ns0:ConversationId>d66d9304-9025-a580-e111-5640bf36560d</ns0:ConversationId>
</ns0:State>
</ns0:Header>
</ns0:MYXML>
i tried to use "{name()}" for the element name but this throws an exception that the namespace does not exist.
Any help is greatly appreciated!

It's not clear to me whether you want the Password element to belong to the empty namespace as it appears in the original Xml or you want it to be in the http://www.me.com/myxml namespace. Here is an Xslt stylesheet that replaces the Password element from the empty namespace with a Password element from the http://www.me.com/myxml namespace (I used ns0 prefix so that it looks like other elements in the Xml document):
<?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="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Password">
<ns0:Password xmlns:ns0="http://www.me.com/myxml">
<xsl:copy-of select="#*"/>
<xsl:text>New Value</xsl:text>
</ns0:Password>
</xsl:template>
<!-- This creates the element in the empty namespace
<xsl:template match="Password">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:text>New Value</xsl:text>
</xsl:copy>
</xsl:template>
-->
</xsl:stylesheet>
I also included a template that copies the existing Password element and replaces its value just in case you need it. If you don't need it you can remove the block entirely.
Here is the transformation result:
<?xml version="1.0" encoding="utf-8"?>
<ns0:MYXML xmlns:ns0="http://www.me.com/myxml" xmlns="http://www.me.com/myxml"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:Header>
<ns0:Infromation>
<ns0:From>
<ns0:Credential>
<ns0:User>jeff</ns0:User>
<ns0:Password>New Value</ns0:Password>
</ns0:Credential>
</ns0:From>
</ns0:Infromation>
<ns0:Misc>
<ns0:ID>1002</ns0:ID>
<ns0:Timestamp>2012-01-16T09:23:33</ns0:Timestamp>
<ns0:Type>unknown</ns0:Type>
</ns0:Misc>
<ns0:State>
<ns0:ConversationId>d66d9304-9025-a580-e111-5640bf36560d</ns0:ConversationId>
</ns0:State>
</ns0:Header>
</ns0:MYXML>
Pawel

Related

Looking for generic xslt which extracts the elements and values from the xml string

Looking for XSLT
Sample Incoming xml with xml string inside -
<?xml version="1.0" encoding="UTF-8"?> <app4:App > <app4:appId>45645dfg</app4:appId> <app4:appType>Insert/Update</app4:appType> <app4:appName>Optin</app4:appName> <app4:source>Store</app4:source> <app4:target>apm</app4:target> <app4:corelationId>564654456</app4:corelationId> <app4:payload> <ns0:xmlString><?xml version="1.0" encoding="UTF-8"?> <app6:consumers xmlns:app6="http://www.example.org/consumer_Notification" xmlns:ns="http://com.equinix.product/consumerxa"> <app6:consumerId>yjtuj</app6:consumerId> <app6:consumerNumber>gdfhfh</app6:consumerNumber> <app6:orderNumber>657467476</app6:orderNumber> <app6:accountNumber>75654757</app6:accountNumber> <app6:accountUcid>6574575747-15C8E09FBEBD</app6:accountUcid> <app6:productName>updateportal</app6:productName> <app6:metro>MB</app6:metro> <app6:country>Japan</app6:country> <app6:consumerType>Patch Panel</app6:consumerType> <app6:consumerPartNumber>654757.COMP</app6:consumerPartNumber> <app6:Status>Active</app6:Status> </app6:consumers></ns0:xmlString> </app4:payload> </app4:App>
Looking for Output xml - with well parsed xmlstring
<?xml version="1.0" encoding="UTF-8"?> <app4:App > <app4:appId>45645dfg</app4:appId> <app4:appType>Insert/Update</app4:appType> <app4:appName>Optin</app4:appName> <app4:source>Store</app4:source> <app4:target>apm</app4:target> <app4:corelationId>564654456</app4:corelationId> <app4:payload> <ns0:xmlString><app6:consumers xmlns:app6="http://www.example.org/consumer_Notification" xmlns:ns="http://com.equinix.product/consumerxa"> <app6:consumerId>yjtuj</app6:consumerId> <app6:consumerNumber>gdfhfh</app6:consumerNumber> <app6:orderNumber>657467476</app6:orderNumber> <app6:accountNumber>75654757</app6:accountNumber> <app6:accountUcid>6574575747-15C8E09FBEBD</app6:accountUcid> <app6:productName>updateportal</app6:productName> <app6:metro>MB</app6:metro> <app6:country>Japan</app6:country> <app6:consumerType>Patch Panel</app6:consumerType> <app6:consumerPartNumber>654757.COMP</app6:consumerPartNumber> <app6:Status>Active</app6:Status> </app6:consumers></ns0:xmlString> </app4:payload> </app4:App>
Assuming the input has namespaces declared for app4 and ns0 then with XSLT 3 you can use e.g.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="*:xmlString">
<xsl:copy>
<xsl:apply-templates select="parse-xml(.)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Biztalk Map node validation

I'm mapping home, work and mobile number nodes from the source schema to the home, work and mobile node in the destination schema.
I need to ensure that the data matches destination pattern (No space, punctuation, leading zeros, matching [+0][0-9]*. Can this be achieved via XSLT?
Source
<HTelephone>01656 123 123</HTelephone>
<WTelephone>01656-123-123</WTelephone>
<MTelephone>+447656 123 123</MTelephone>
Destination
<HTelephone>01656123123</HTelephone>
<WTelephone>01656123123</WTelephone>
<MTelephone>+447656123123</MTelephone>
Current Inline XSLT Call Template
<xsl:template name="MNo" xmlns:msxsl="urn:schemas-microsoft-com:xslt" >
<xsl:param name="inTelNo"/>
<xsl:element name="MTelephone" >
<xsl:value-of select="concat('+', translate($inTelNo, translate($inTelNo,'0123456789',''), ''))"/>
</xsl:element>
We need to validation the first character to allow a 0 or + also, any ideas?
Assuming correct input ( a root element to make it well-formed XML) use the concat() and translate functions to change the strings.
Input
<?xml version="1.0" encoding="utf-8"?>
<root>
<HTelephone>01656 123 123</HTelephone>
<WTelephone>01656 123 123</WTelephone>
<MTelephone>01656 123 123</MTelephone>
</root>
Stylesheet
<?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" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/root">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/*">
<xsl:copy>
<xsl:value-of select="concat('+',translate(.,' ',''))"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="utf-8"?>
<root>
<HTelephone>+01656123123</HTelephone>
<WTelephone>+01656123123</WTelephone>
<MTelephone>+01656123123</MTelephone>
</root>

How to select the value from an attribute that has a colon in xslt?

I am working with xslt to handle the results that are returned from a web service. I first need to determine which web service the results are for. I know that the tag platformCore:record has the attribute "xsi:type="listRel:Contact or "xsi:type="listEmp:Employee". I am trying to select the value that the attribute is storing, but the colon seems to be causing some issues when I attempt to select the value.
Here is what I tried, but fails to work.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<xsl:variable name="Type"><xsl:value-of select="//*[local-name()='searchResponse']//*[local-name()='searchResult']//*[local-name()='recordList']//*[local-name()='record']#xsi:type"/></xsl:variable>
<root>
<test><xsl:value-of select="$Type"/></test>
</root>
</xsl:template>
</xsl:stylesheet>
Here is a simple sample
<?xml version="1.0" encoding="UTF-8"?>
<searchResponse:searchResponse xmlns="urn:messages_2012_2.platform.webservices.itsthesuite.com"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:searchResponse="urn:messages_2012_2.platform.webservices.itsthesuite.com"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<platformCore:searchResult xmlns:platformCore="urn:core_2012_2.platform.webservices.itsthesuite.com">
<platformCore:status isSuccess="true"/>
<platformCore:totalRecords>1</platformCore:totalRecords>
<platformCore:recordList>
<platformCore:record internalId="154098" xsi:type="listRel:Contact" xmlns:listRel="urn:relationships_2012_2.lists.webservices.itsthesuite.com">
<listRel:entityId>John Smith</listRel:entityId>
<listRel:firstName>John</listRel:firstName>
<listRel:lastName>Smith</listRel:lastName>
<listRel:phone>(777) 777-7777</listRel:phone>
<listRel:email>john.smith#yormoms.com</listRel:email>
</platformCore:record>
</platformCore:recordList>
</platformCore:searchResult>
</searchResponse:searchResponse>
I need the solution to work for this sample as well.
Employee Sample
<?xml version="1.0" encoding="UTF-8"?>
<searchResponse xmlns="urn:messages_2012_2.platform.webservices.netsuite.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:searchResponse="urn:messages_2012_2.platform.webservices.netsuite.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<platformCore:searchResult xmlns:platformCore="urn:core_2012_2.platform.webservices.netsuite.com" >
<platformCore:status isSuccess="true"/>
<platformCore:totalRecords>1</platformCore:totalRecords>
<platformCore:recordList>
<platformCore:record internalId="158778" xsi:type="listEmp:Employee" xmlns:listEmp="urn:employees_2012_2.lists.webservices.netsuite.com">
<listEmp:entityId>331sfds Dipo Chaponda</listEmp:entityId>
<listEmp:salutation>Mr.</listEmp:salutation>
<listEmp:firstName>Dipo</listEmp:firstName>
<listEmp:lastName>Chaponda</listEmp:lastName>
<listEmp:email>dchapond#youmm.com</listEmp:email>
</platformCore:record>
</platformCore:recordList>
</platformCore:searchResult>
</searchResponse>
You can select an attribute using local name similarly to what you are already doing, but by prefacing the * with an #:
#*[local-name() = 'type']
However, littering your XPaths with local-name() = and double slashes is not a good practice. You should use namespaces properly, and use precise paths when they are known, although it seems that is not an option for the elements in your case because they are using different namespaces in the two examples. This should work:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="sr pc xsi"
>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<xsl:variable name="Type">
<xsl:value-of select="*[local-name() = 'searchResponse']/
*[local-name() = 'searchResult']/
*[local-name() = 'recordList']/
*[local-name() = 'record']/
#xsi:type"/>
</xsl:variable>
<root>
<test>
<xsl:value-of select="$Type"/>
</test>
</root>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, this produces the expected result:
<root>
<test>listRel:Contact</test>
</root>

Removing an XML tag that is named like xfdf:field (with a namespace)

I want to remove an XML element from an XML file. The tag that I want to remove is named as xfdf:field.
How do I specify this in my xslt ? I tried this and I am getting an error saything "org.apache.xpath.domapi.XPathStylesheetDOM3Exception: Prefix must resolve to a namespace: xfdf ".
Here is my xslt.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="xfdf:field"></xsl:template>
</xsl:stylesheet>
Here is my xml.
<xfa:datasets
xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/"
xmlns:dd="http://ns.adobe.com/data-description/" xmlns:tns="http://hostname"
xmlns:xfdf="http://ns.adobe.com/xfdf/">
<xfa:data>
<tns:form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<tns:formHeader>
<tns:formId>formid</tns:formId>
<tns:revId>Rev123</tns:revId>
<tns:agencyId>agency</tns:agencyId>
<tns:progId>program</tns:progId>
<tns:serviceId>service</tns:serviceId>
</tns:formHeader>
<tns:formFields>
<tns:date>08-13-1967</tns:date>
<tns:agreementBetween></tns:agreementBetween>
<tns:ncr>xxxx</tns:ncr>
<tns:formConfirmationInfo>
<tns:confNbrLbl>nbrlabel</tns:confNbrLbl>
<tns:confNbrData>1231</tns:confNbrData>
<tns:custNameLbl>3332</tns:custNameLbl>
<tns:custNameData>dasdas</tns:custNameData>
<tns:dateLbl>date</tns:dateLbl>
<tns:dateData>01012001</tns:dateData>
</tns:formConfirmationInfo>
</tns:formFields>
<xfdf:field xmlns:xfdfi="http://ns.adobe.com/xfdf-transition/"
xfdfi:original="FSAPPLICATIONDATA_">
<!--irrelevant data omitted-->
</xfdf:field>
</tns:form>
</xfa:data>
</xfa:datasets>
You need to specify the namespace in your XSLT file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xfdf="http://ns.adobe.com/xfdf/">

xpath-function max doesn't work

For unknown reason max function doesn't work.
XML input file:
test.xml
<?xml version="1.0" encoding="UTF-8"?>
<numbers>
<number>3</number>
<number>5</number>
<number>10</number>
<number>1</number>
</numbers>
XSL input file
test.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/02/xpath-functions"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" >
<xsl:output method="xml" indent="yes" />
<xsl:template match="/numbers">
<numbers>
<xsl:value-of select="/numbers/number" />
fn:max(2, 3)
</numbers>
</xsl:template>
</xsl:stylesheet>
Output.xml
<?xml version="1.0" encoding="UTF-8"?>
<numbers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:fn="http://www.w3.org/2005/02/xpath-functions">3
fn:max(2, 3)
</numbers>
Input file is not important here, but I would like to have '3' instead of fn:max(2, 3). How to do it?
for this XSL file:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/02/xpath-functions"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" >
<xsl:output method="xml" indent="yes" />
<xsl:template match="/numbers">
<numbers>
<xsl:value-of select="/numbers/number" />
fn:max(2, 3)
<xsl:value-of select="max(/numbers/number)"/>
</numbers>
</xsl:template>
</xsl:stylesheet>
the following error occurs:
SystemId Unknown; Line #13; Column #49; Could not find function: max
SystemId Unknown; Line #13; Column #49; function token not found.
(Location of error unknown)java.lang.NullPointerException
(Location of error unknown)XSLT Error (javax.xml.transform.TransformerException)
: No xml-stylesheet PI found in: test.xml
Exception in thread "main" java.lang.RuntimeException: No xml-stylesheet PI foun
d in: test.xml
at org.apache.xalan.xslt.Process.doExit(Process.java:1155)
at org.apache.xalan.xslt.Process.main(Process.java:1128)
I used Xalan - Version Xalan Java 2.7.1, Command: java org.apache.xalan.xslt.Process -in test.xml -xsl test.xsl -out output.xml
There are several problems: max() needs to be in a value-of, and that you've said xsl:stylesheet version="2.0" for Xalan, which only supports XSLT 1.0. For 2.0, you'd need Saxon 9.x.
Since max() isn't part of XSLT 1.0, you need to invoke the EXSLT extension support, which Xalan does have:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math">
<xsl:template match="/numbers">
<xsl:value-of select="math:max(number)"/>
</xsl:template>
</xsl:stylesheet>
or
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math">
<xsl:template match="/">
<xsl:value-of select="math:max(numbers/number)"/>
</xsl:template>
</xsl:stylesheet>
You've put fn:max(2,3) in a text block. Nothing is going to interpret that. You need to put functions in value-of expressions if you want them to be evaluated.
Lavino,
Thanks for the response. I don't know why but I was pretty sure that Xalan supports 2.0... I've tested it and it works for Saxon 9.
You can use
<xsl:value-of select="max(number)" />
to get the max of all numbers.
Soln:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/numbers">
<numbers>
<max>
<xsl:value-of select="max(number)"/>
</max>
<xsl:apply-templates/>
</numbers>
</xsl:template>
<xsl:template match="number">
<number>
<xsl:value-of select="."/>
</number>
</xsl:template>
</xsl:stylesheet>
You can omit the number template and <xsl:apply-templates/> if its not reqd. This will be the output with the above xslt:
<?xml version="1.0" encoding="UTF-8"?>
<numbers xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<max>10</max>
<number>3</number>
<number>5</number>
<number>10</number>
<number>1</number>
</numbers>