Replace parts of string on line using XSLT 2 - java

I have a WSDL, in which I would like to override the soap address location using XSLT 2.0.
For example:
<soap:address location="https://xyz.company.com/portal/services/Service?param1=myapp&webService=TestWebService"/>
For the location attribute, I would like
xyz.company.com to become abc.company.com
myapp to become xyzapp.
I have written the following xslt:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<xsl:output method="xml"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:variable name="vLocation" select="wsdl:definitions/wsdl:service/wsdl:port/soap:address/#location"/>
<xsl:template match="#location">
<xsl:attribute name="location">
<xsl:value-of select="replace($vLocation, xyz.company.com', 'abc.company.com')"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
This works for replacing point 1 -> xyz.company.com to become abc.company.com.
But how can I also replace point 2. ->myapp to become xyzapp
Please advise.
Thanks,

You can simply call replace again as in
<xsl:template match="soap:address/#location">
<xsl:attribute name="location" select="replace(replace(., 'xyz\.company\.com', 'abc.company.com'), 'myapp', 'xyzapp')"/>
</xsl:template>

Related

subsequent element as attribute in xml using xslt

need help in making subsequent element as attribute to preceding element if subsequent element contains the preceding element name using XSLT.
For below example, <emp_id> contains the preceding the element name so need to convert this element as attribute to element. can you anyone help for this?. I tried using subsequent functions in xslt but not working. Thanks in advance.
Sample XML:
<root>
<emp>test</emp>
<emp_id>1234</emp_id>
<college>something</college>
</root>
Expected out:
<root>
<emp id="1234">test</emp>
<college>something</college>
</root>
In the given example, you could do:
XSLT 1.0
<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="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="following-sibling::*[starts-with(name(), name(current()))]" mode="attr"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" mode="attr">
<xsl:attribute name="{substring-after(name(), '_')}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template match="*[preceding-sibling::*[name()=substring-before(name(current()), '_')]]"/>
</xsl:stylesheet>
to achieve the expected result.
There is probably a more elegant way, but we need to have some more rules, not just a single example.
Added:
Assuming that every element whose name contains a _ has a "parent" element whose attribute it should become, you can start by applying templates to only the "parent" elements (i.e. elements whose name does not contain a '_').
Then use a key to collect the "child" elements that need to be converted to attributes.
XSLT 1.0
<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:key name="atr" match="*" use="substring-before(name(), '_')" />
<xsl:template match="/root">
<xsl:copy>
<xsl:apply-templates select="*[not(contains(name(), '_'))]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each select="key('atr', name())">
<xsl:attribute name="{substring-after(name(), '_')}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

How to concat some value in the existing value of a xml using xslt

Apologises if duplicate. I want to perform a concat operation on the values of the xml elements.
I have xml say Input.xml
<collection>
<one>
<part>1</part>
</one>
<two>
<part>2</part>
</two>
<three>
<part>3</part>
</three>
</collection>
I want the output like :
<collection>
<one>
<part>001</part>
</one>
<two>
<part>002</part>
</two>
<three>
<part>003</part>
</three>
</collection>
how to write xslt for it
Quick hacky XSLT , tested it online it seems to work.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="#*|text()|comment()|processing-instruction">
</xsl:template>
<xsl:template match="//part">
<part>
<xsl:text>00</xsl:text>
<xsl:value-of select="."></xsl:value-of>
</part>
</xsl:template>
</xsl:stylesheet>
Your possible solution might be:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="part">
<xsl:value-of select="format-number(., '000')"/>
</xsl:template>
<!-- identity copy -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Format the text in part to a 3 digit number format

Replace element tag to empty tag

My requirements is to generate an empty element with the given scenario. I have the same in this Replacing the element tag with value to end tag. However, the output generated is not what I am expecting.
CONDITION:
Map according to priority:
1. If Test1 is equal to Payment1, generate this empty element <st:Test1/> 2. else if Test2 is equal to Payment2, generate this empty element <st:Test2/> 3. else if Test3 is equal to Payment3, generate this empty element <st:Test3/>.
MY XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:st="http://www.ebinterface.at/schema/4p1/" xmlns:po="http://schema.ebinterface.at/schema/4p1/" exclude-result-prefixes="po">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="st:{local-name()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="po:DEFG" priority="1">
<st:DEFG>
<!--handle any existing child content-->
<xsl:apply-templates select="#* | node()"/>
<xsl:if test="not(po:Test1)">
<xsl:call-template name="Test1"/>
</xsl:if>
<xsl:if test="not(po:Test2)">
<xsl:call-template name="Test2"/>
</xsl:if>
<xsl:if test="not(po:Test3)">
<xsl:call-template name="Test3"/>
</xsl:if>
</st:DEFG>
</xsl:template>
<xsl:template match="po:Test1[.='Payment1']" name="Test1" priority="1">
<st:Test1/>
</xsl:template>
<xsl:template match="po:Test2[.='Payment2']" name="Test2" priority="1">
<st:Test2/>
</xsl:template>
<xsl:template match="po:Test3[.='Payment3']" name="Test3" priority="1">
<st:Test3/>
</xsl:template>
<xsl:template match="*[namespace-uri()='http://www.ebinterface.at/schema/4p1/']">
<xsl:element name="po:{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*[namespace-uri()='http://www.ebinterface.at/schema/4p1/']/#*">
<xsl:attribute name="po:{local-name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
INPUT FILE:
<?xml version="1.0" encoding="UTF-8"?>
<Statistics xmlns="http://schema.ebinterface.at/schema/4p1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ebinterface.at/schema/4p1/" Type="abc" Title="Statistics">
<ABC dsig="fh">Sample ABC</ABC>
<DEFG>
<Note>Wir ersuchen um termingerechte Bezahlung.</Note>
<Amount currencyCode="EUR">12.36</Amount>
<Test1>Payment1</Test1>
</DEFG>
</Statistics>
GENERATED OUTPUT:
<?xml version="1.0" encoding="UTF-8"?>
<st:Statistics xmlns:st="http://www.ebinterface.at/schema/4p1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ebinterface.at/schema/4p1/" Type="abc" Title="Statistics">
<st:ABC dsig="fh">Sample ABC</st:ABC>
<st:DEFG>
<st:Note>Wir ersuchen um termingerechte Bezahlung.</st:Note>
<st:Amount currencyCode="EUR">12.36</st:Amount>
<st:Test1/>
<st:Test2/> **This empty element should not appear since there's no Test2=Payment2**
<st:Test3/> **This empty element should not appear since there's no Test3=Payment3**
</st:DEFG>
</st:Statistics>
EXPECTED OUTPUT:
<?xml version="1.0" encoding="UTF-8"?>
<st:Statistics xmlns="http://schema.ebinterface.at/schema/4p1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ebinterface.at/schema/4p1/" st:Type="abc" st:Title="Statistics">
<st:ABC st:dsig="fh">Sample ABC</st:ABC>
<st:DEFG>
<st:Note>Wir ersuchen um termingerechte Bezahlung.</st:Note>
<st:Amount st:currencyCode="EUR">12.36</st:Amount>
<st:Test1/>
</st:DEFG>
</stStatistics>
Thank you in advance.
Your response is greatly appreciated.
When you call a template by name, that template will be executed regardless of whether it matches anything or not.
Not directly related to your question, you also have a template conflict between <xsl:template match="#* | node()"> and <xsl:template match="*">. Overall, you have way too much code; I believe you could do with just:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:po="http://schema.ebinterface.at/schema/4p1/"
xmlns:st="http://www.ebinterface.at/schema/4p1/"
exclude-result-prefixes="po">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move elements to new namespace -->
<xsl:template match="*">
<xsl:element name="st:{local-name()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<!-- move attributes to new namespace -->
<xsl:template match="#*">
<xsl:attribute name="st:{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<!-- except attributes that are NOT in the default namespace -->
<xsl:template match="#*[namespace-uri()]">
<xsl:copy/>
</xsl:template>
<xsl:template match="po:Test1[.='Payment1']">
<st:Test1/>
</xsl:template>
<xsl:template match="po:Test2[.='Payment2']">
<st:Test2/>
</xsl:template>
<xsl:template match="po:Test3[.='Payment3']">
<st:Test3/>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<st:Statistics xmlns:st="http://www.ebinterface.at/schema/4p1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ebinterface.at/schema/4p1/" st:Type="abc" st:Title="Statistics">
<st:ABC st:dsig="fh">Sample ABC</st:ABC>
<st:DEFG>
<st:Note>Wir ersuchen um termingerechte Bezahlung.</st:Note>
<st:Amount st:currencyCode="EUR">12.36</st:Amount>
<st:Test1/>
</st:DEFG>
</st:Statistics>
Note:
The three choices are not mutually exclusive as your question would suggest.
Your expected output is not well-formed XML: you cannot have a prefix without binding it to a namespace.

Customizing/Remove name space in Soap Response - JAX-WS [duplicate]

I need to remove the namespace prefix from an un-SOAP'd message.
This is the message that has had the SOAP envelope removed. As you can see it contains ns1 prefix on the elements:
<ns1:BookingSource xmlns:ns1="urn:EDI/Booking/artifacts">
<ns1:BookingHeader>
<ns1:BookingNo>000123</ns1:BookingNo>
<ns1:BookingDate>01/01/2012</ns1:BookingDate>
<ns1:DSBookingDetail>
<ns1:BookingNo>000123</ns1:BookingNo>
<ns1:SeqNo>1</ns1:SeqNo>
<ns1:LineType>Item</ns1:LineType>
<ns1:ProductCode>Box</ns1:ProductCode>
</ns1:DSBookingDetail>
<ns1:DSBookingDetail>
<ns1:BookingNo>000123</ns1:BookingNo>
<ns1:SeqNo>2</ns1:SeqNo>
<ns1:LineType>Item</ns1:LineType>
<ns1:ProductCode>BrakeShoe</ns1:ProductCode>
</ns1:DSBookingDetail>
</ns1:DSBookingHeader>
<ns1:BookingHeader>
<ns1:BookingNo>000124</ns1:BookingNo>
<ns1:BookingDate>01/01/2012</ns1:BookingDate>
<ns1:DSBookingDetail>
<ns1:BookingNo>000124</ns1:BookingNo>
<ns1:SeqNo>1</ns1:SeqNo>
<ns1:LineType>Item</ns1:LineType>
<ns1:ProductCode>Box</ns1:ProductCode>
</ns1:DSBookingDetail>
<ns1:DSBookingDetail>
<ns1:BookingNo>000124</ns1:BookingNo>
<ns1:SeqNo>2</ns1:SeqNo>
<ns1:LineType>Item</ns1:LineType>
<ns1:ProductCode>BrakeShoe</ns1:ProductCode>
</ns1:DSBookingDetail>
</ns1:DSBookingHeader>
</ns1:BookingSource>
To this:
<BookingSource>
<BookingHeader>
<BookingNo>000123</BookingNo>
<BookingDate>01/01/2012</BookingDate>
<DSBookingDetail>
<BookingNo>000123</BookingNo>
<SeqNo>1</SeqNo>
<LineType>Item</LineType>
<ProductCode>Box</ProductCode>
</DSBookingDetail>
<DSBookingDetail>
<BookingNo>000123</BookingNo>
<SeqNo>2</SeqNo>
<LineType>Item</LineType>
<ProductCode>BrakeShoe</ProductCode>
</DSBookingDetail>
</DSBookingHeader>
<BookingHeader>
<BookingNo>000124</BookingNo>
<BookingDate>01/01/2012</BookingDate>
<DSBookingDetail>
<BookingNo>000124</BookingNo>
<SeqNo>1</SeqNo>
<LineType>Item</LineType>
<ProductCode>Box</ProductCode>
</DSBookingDetail>
<DSBookingDetail>
<BookingNo>000124</BookingNo>
<SeqNo>2</ns1:SeqNo>
<LineType>Item</LineType>
<ProductCode>BrakeShoe</ProductCode>
</DSBookingDetail>
</DSBookingHeader>
</BookingSource>
I've searched through the KB and found some hints on how to do it, but the final solution evades me.
Thanks,
Tony.
It is called namespace, below is a code to remove namespace from all elements and attributes ..
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*">
<xsl:element name="{local-name(.)}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name(.)}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>

CDATA XML masking with XSLT should return same XML with few masked fields

I have a requirement of masking few fields in XML of CDATA inside XML with XSLT.
So the resultant XML should be same like the input XML but few fileds are masked with XSLT.
I followed this link which is masking as expected but producing XML is in different format.
I tried many other solutions from SO, they are almost outputing the new XML/HTML in other format which is different from the input XML.
Please check the following example for better understading.
Input XML with CDATA content.
<XML>
<LogLevel>info</LogLevel>
<Content><![CDATA[ <Msg>
<AccountNo>2701000098983</AccountNo>
<ApplName>Testing</ApplName>
</Msg>]]></Content>
<Date>20140909</Date>
</XML>
Output XML should be:
<XML>
<LogLevel>info</LogLevel>
<Content><![CDATA[ <Msg>
<AccountNo>XXXXXXXXXX983</AccountNo>
<ApplName>Testing</ApplName>
</Msg>]]></Content>
<Date>20140909</Date>
</XML>
Edit:
I used the following XSLT
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:choose>
<xsl:when test="contains(.,'<AccountNo>')">
<!-- This is the CDATA that I want to mask and write back out as CDATA -->
<xsl:variable name="tcontent">
<xsl:value-of
select="substring-after(substring-before(.,'</AccountNo>'),'<AccountNo>') " />
</xsl:variable>
<xsl:text disable-output-escaping="yes"><![CDATA[<AccountNo></xsl:text>
<xsl:call-template name="maskVariable">
<xsl:with-param name="tvar" select="$tcontent" />
</xsl:call-template>
<xsl:text disable-output-escaping="yes"></AccountNo>]]></xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="maskVariable">
<xsl:param name="tvar" />
<xsl:variable name="length" select="string-length($tvar)" />
<xsl:choose>
<xsl:when test="$length > 3">
<xsl:value-of
select="concat ('************', substring($tvar,$length - 1, 2))" />
</xsl:when>
<xsl:when test="$length > 1">
***
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
Output of using this XSLT is :
<LogLevel>info</LogLevel>
<Content><![CDATA[<AccountNo>************02</AccountNo>]]></Content>
<Date>20140909</Date>
Here in output, only masked output of is displaying.
How to make other part of the code to get displayed ?
Please give me some idea how to do it ?
Any help is highly appreciated.
Why don't you try:
XSLT 1.0
<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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Content">
<xsl:copy>
<xsl:value-of select="substring-before(.,'<AccountNo>')" />
<xsl:text><AccountNo></xsl:text>
<xsl:variable name="acct-num" select="substring-before(substring-after(.,'<AccountNo>'), '</AccountNo>')" />
<xsl:value-of select="concat('************', substring($acct-num, string-length($acct-num) - 2))" />
<xsl:text></AccountNo></xsl:text>
<xsl:value-of select="substring-after(.,'</AccountNo>')" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Applied to your input, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<LogLevel>info</LogLevel>
<Content> <Msg>
<AccountNo>************983</AccountNo>
<ApplName>Testing</ApplName>
</Msg></Content>
<Date>20140909</Date>
</XML>
Alternatively, you could use:
<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" cdata-section-elements="Content"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Content">
<xsl:copy>
<xsl:variable name="content">
<xsl:value-of select="substring-before(.,'<AccountNo>')" />
<xsl:text><AccountNo></xsl:text>
<xsl:variable name="acct-num" select="substring-before(substring-after(.,'<AccountNo>'), '</AccountNo>')" />
<xsl:value-of select="concat('************', substring($acct-num, string-length($acct-num) - 2))" />
<xsl:text></AccountNo></xsl:text>
<xsl:value-of select="substring-after(.,'</AccountNo>')" />
</xsl:variable>
<xsl:value-of select="$content" disable-output-escaping="yes"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
to produce:
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<LogLevel>info</LogLevel>
<Content><![CDATA[ <Msg>
<AccountNo>************983</AccountNo>
<ApplName>Testing</ApplName>
</Msg>]]></Content>
<Date>20140909</Date>
</XML>
although this might not work with every processor (tested to work with Xalan 2.7.1: http://xsltransform.net/jyH9rMk).
The stuff inside the CDATA section is XML disguised as text. XSLT is good at transforming XML, it's not so good at transforming text, especially text with a complex grammar. So my approach would be: extract the text from the outer XML document, parse it as XML, transform it using XSLT (real XSLT that works on nodes rather than on markup), serialize it back to text, then stuff the text back into the original (outer) XML document.
Raw XSLT 1.0 can't do this within a single stylesheet. You need the functions parse() and serialize() to turn lexical XML into a node tree, and back again. These are available as extensions in some processors (such as Saxon), they become available as standard functions in XPath 3.0, and they can be written as simple extension functions (e.g. in Javascript) code in most other processors.

Categories