XSLT to remove duplicates completely - xslt

I know there are solutions plenty to remove duplicates, but this one is slightly different. I need to remove the element from the output if it is a duplicate.
Input:
<SanctionList>
<row>
<PersonId>1000628</PersonId>
<PersonId>1000634</PersonId>
<PersonId>1113918</PersonId>
<PersonId>1133507</PersonId>
<PersonId>1113918</PersonId>
</row>
</SanctionList>
Output expected:
<SanctionList>
<row>
<PersonId>1000628</PersonId>
<PersonId>1000634</PersonId>
<PersonId>1133507</PersonId>
</row>
</SanctionList>
Here is what I tried but the parser returns 1 for each of the groups. Shouldnt it return 2 for PersonId 1113918 since it appears twice in the list?
<?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:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="SanctionList">
<xsl:for-each-group select="row" group-by="PersonId">
<xsl:text> Count for </xsl:text>
<xsl:value-of select="current-grouping-key()" />
<xsl:text> is </xsl:text>
<xsl:value-of select="count(current-group())" />
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
Thanks kindly!

I know there are solutions plenty to remove duplicates, but this one
is slightly different. I need to remove the element from the output if
it is a duplicate
Use this short and simple transformation (both in XSLT 2.0 and XSLT 1.0):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kPersonByVal" match="PersonId" use="."/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PersonId[key('kPersonByVal', .)[2]]"/>
</xsl:stylesheet>
when the transformation is applied on the provided XML document:
<SanctionList>
<row>
<PersonId>1000628</PersonId>
<PersonId>1000634</PersonId>
<PersonId>1113918</PersonId>
<PersonId>1133507</PersonId>
<PersonId>1113918</PersonId>
</row>
</SanctionList>
the wanted, correct result is produced:
<SanctionList>
<row>
<PersonId>1000628</PersonId>
<PersonId>1000634</PersonId>
<PersonId>1133507</PersonId>
</row>
</SanctionList>
Explanation:
A wellknown design pattern for copying an existing XML document and deleting/replacing/inserting some nodes into the copy, is by overriding the identity rule.
In this particular case the task is to delete <PersonId> elements. This is done by providing a matching template with no (empty) body.
The criterion for deletion is that the element must have a duplicate -- that is, at least two <PersonId> elements must exist, having the same string value. This is most conveniently done using an <xsl:key> declaration and the key() function to get all elements with the same string value.
Finally, in the match pattern of the empty (deleting) template we check if the node-set of equally-valued elements has a second element.
Note: You can learn more about the <xsl:key> declaration and the key() function in module 9 of my Pluralsight training course "XSLT 2.0 and 1.0 foundations"

Related

Replace values using Key defined with inline variable

I'm trying to understand the usage of keys and can't get my example to work.
Starting with this XML:
<items>
<item>Blue</item>
<item>Green</item>
<item>Orange</item>
</items>
I want to get this output XML:
<items>
<item>PURPLE</item>
<item>BLACK</item>
<item>PINK</item>
</items>
I defined the mapping directly in a variable in the XSLT transformation:
<?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" indent="yes"/>
<xsl:variable name="mappings">
<mapping orig="Blue" repla="PURPLE"/>
<mapping orig="Green" repla="BLACK"/>
<mapping orig="Orange" repla="PINK"/>
</xsl:variable>
<xsl:key name="mappingsKey" match="$mappings/mapping" use="#orig"/>
<xsl:template match="items">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<xsl:copy>
<xsl:value-of select="key('mappingsKey',.)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I know I'm missing the instruction to tell which is the replacement value, but don't know how to define it.
Example available here: https://xsltfiddle.liberty-development.net/ejivdHp/1
Thanks.
The $mappings variable is in a different document than the processed XML. You need to point the key() function to there. And you also need to select the repla attribute:
<xsl:value-of select="key('mappingsKey', ., $mappings)/#repla"/>
Referencing the variable in the match pattern of the xsl:key element is meaningless.
https://xsltfiddle.liberty-development.net/ejivdHp/2

XSLT code to remove xml tag

I have an incoming XML like below: I need to remove the <shoeboxImage> tag from the incoming below XML.
Incoming XML Input:
<attachReceipt>
<baseMessage>
<returnCode>200</returnCode>
</baseMessage>
<payload>
<returnCode>0</returnCode>
<shoeboxItem>
<shoeboxImageCount>2</shoeboxImageCount>
<shoeboxImages>
<shoeboxImage>
<name>receiptImage.jpg</name>
</shoeboxImage>
<shoeboxImage>
<name>receiptImage.jpg</name>
</shoeboxImage>
</shoeboxImages>
</shoeboxItem>
</payload>
</attachReceipt>
Expected Output:
<attachReceipt>
<baseMessage>
<returnCode>200</returnCode>
</baseMessage>
<payload>
<returnCode>0</returnCode>
<shoeboxItem>
<shoeboxImageCount>2</shoeboxImageCount>
<shoeboxImages>
<name>receiptImage.jpg</name>
<name>receiptImage.jpg</name>
</shoeboxImages>
</shoeboxItem>
</payload>
</attachReceipt>
Need some xslt code snippet to do this.
I don't have the necessary software installed to actually test this, but this should work:
<xsl:template match="shoeboxImage">
<xsl:apply-templates select="*|text()"/>
</xsl:template>
The idea is that when a shoeboxImage element is encountered, it generates nothing for the element itself, and just continues with its children.
You need to have an identity template and a template that will remove the element shoeboxImage but will retain its descendants.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<!-- identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- template override for the element shoeboxImage -->
<xsl:template match="shoeboxImage">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

Adding new xml element and changing the value of an element within the same parent element

I need to make certain modifications to my XML input, depending on certain conditions. I am using XSLT 1.0.
the value of the message_type element (child element of m_cotrol) should be changed
A new element message_status should be added (as a child of the m_control element).
These changes are reflected in the expected output XML. With my current XSLT code, I am only able to achieve the second requirement.
Input XML:
<?xml version="1.0"?>
<message xmlns="http://www.origoservices.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<m_control>
<control_timestamp>2013-04-12T09:24:38.902</control_timestamp>
<message_id>a50ec030-72ab</message_id>
<retry_number>0</retry_number>
<message_type>Request</message_type>
<message_version>test.XSD</message_version>
<expected_response_type>synchronous</expected_response_type>
<initiator_id>FST</initiator_id>
<initiator_orchestration_id>1637280</initiator_orchestration_id>
<responder_id>mycomp</responder_id>
</m_control>
<m_content>
<b_control>
<service_provider_reference_number>650971</service_provider_reference_number>
<intermediary_case_reference_number>Sample1</intermediary_case_reference_number>
<quote_type>Comparison</quote_type>
<quote_or_print>Print</quote_or_print>
<message_version_number>3.7</message_version_number>
<submission_date>0001-04-12</submission_date>
</b_control>
</m_content>
</message>
Expected Output:
<message xmlns="http://www.origoservices.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<m_control>
<control_timestamp>2013-04-12T09:24:38.902</control_timestamp>
<message_id>a50ec030-72ab</message_id>
<retry_number>0</retry_number>
<message_type>Response</message_type>
<message_version>test.XSD</message_version>
<expected_response_type>synchronous</expected_response_type>
<initiator_id>FST</initiator_id>
<initiator_orchestration_id>1637280</initiator_orchestration_id>
<responder_id>mycomp</responder_id>
<message_status>User not allowed access</message_status>
</m_control>
<m_content>
<b_control>
<service_provider_reference_number>650971</service_provider_reference_number>
<intermediary_case_reference_number>Sample1</intermediary_case_reference_number>
<quote_type>Comparison</quote_type>
<quote_or_print>Print</quote_or_print>
<message_version_number>3.7</message_version_number>
<submission_date>0001-04-12</submission_date>
<quote_response_status>Error</quote_response_status>
<quote_error_note>
<reason>[Error] Check if the User has access to the requested service</reason>
</quote_error_note>
</b_control>
</m_content>
</message>
XSLT code: Based on the value of DataPower variable (var://service/error-message), I need the expected output.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dp="http://www.datapower.com/extensions" version="1.0" extension-element-prefixes="dp" exclude-result-prefixes="dp">
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[contains(name(),'m_control')]">
<xsl:choose>
<xsl:when test="dp:variable('var://service/error-message') = 'not present'">
<m_control xmlns="http://www.origoservices.com">
<xsl:apply-templates select="#* | *"/>
<message_status>User not recognized</message_status>
</m_control>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="//*[contains(name(),'b_control')]">
<xsl:choose>
<xsl:when test="dp:variable('var://service/error-subcode')='0x01d30002'">
<b_control xmlns="http://www.origoservices.com">
<xsl:apply-templates select="#* | *"/>
<quote_response_status>Error</quote_response_status>
<quote_error_note>
<reason>[Error] Check if the User has access to the requested service</reason>
</quote_error_note>
</b_control>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The following stylesheet meets both of your requirements. It does a common identity transform (which your XSLT does, too) with exceptions.
Note that I have not taken into consideration any changes that are performed by your stylesheet but not listed as a requirement (i.e. changing quote_error_note and quote_response_status).
This line:
<xsl:template match="text()[parent::ori:message_type]">
meets your first requirement, the one you were unable to code. It matches the text content of message_type and outputs "Response" instead.
But this solution differs from yours in another way: it does not match elements along the lines of:
<xsl:template match="//*[contains(name(),'m_control')]">
Rather, their correct namespace is identified:
<xsl:template match="ori:m_control">
Now, what's the difference? Your way of describing the template match allows elements of any namespace to be matched. This might not be a problem in your case (no conflicting namespaces) but it could be one in general.
Full stylesheet
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ori="http://www.origoservices.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="ori xsi">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ori:m_control">
<xsl:copy>
<xsl:apply-templates/>
<message_status>
<xsl:text>User not allowed access</xsl:text>
</message_status>
</xsl:copy>
</xsl:template>
<xsl:template match="text()[parent::ori:message_type]">
<xsl:text>Response</xsl:text>
</xsl:template>
</xsl:stylesheet>

Transform an non numbered list to a numbered list with XSLT

I am trying to transform
<Address>
<Line>Some street1</Line>
<Line>Some street2</Line>
<Line>Some street3</Line>
...
</Address>
into
<Address1>Some street1</Address1>
<Address2>Some street2</Address2>
<Address3>Some street3</Address3>
<Address4></Address4>
<Address5></Address5>
The first xml is malleable and can be redefined if neccessary, however the second xml is part of a legacy system which cannot me changed.
Most of what I find, correctly, points me to using attributes but unfortunatly, its the element itself that I wish to edit.
Would anyone be able to assist or if not, point me in the right direction?
As easy as this, and probably the shortest solution:
<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="Line">
<xsl:element name="Address{position()}"><xsl:apply-templates/></xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Address>
<Line>Some street1</Line>
<Line>Some street2</Line>
<Line>Some street3</Line>
</Address>
the wanted, correct result is produced:
<Address1>Some street1</Address1>
<Address2>Some street2</Address2>
<Address3>Some street3</Address3>
Explanation:
Proper use of xsl:element and AVTs (Attribute Value Templates).
Have a look at the <xsl:element> element. In its name attribute, you can also supply an expression that is computed while running the XSLT:
<xsl:template match="Line">
<xsl:element name="{concat('Address', position())}"><xsl:value-of select="text()"/></xsl:element>
</xsl:template>
Update: position() is one-based.
It can be done by mangling a new element with the current position() :
<xsl:template match="/Address">
<Addresses>
<xsl:for-each select="Line">
<xsl:variable name="elename" select="concat('Address', string(position()))"></xsl:variable>
<xsl:element name="{$elename}">
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each >
</Addresses>
</xsl:template>

Doing a double-pass in XSL?

Is it possible to store the output of an XSL transformation in some sort of variable and then perform an additional transformation on the variable's contents? (All in one XSL file)
(XSLT-2.0 Preferred)
XSLT 2.0 Solution :
<xsl:variable name="firstPassResult">
<xsl:apply-templates select="/" mode="firstPass"/>
</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates select="$firstPassResult" mode="secondPass"/>
</xsl:template>
The trick here, is to use mode="firstPassResult" for the first pass while all the templates for the sedond pass should have mode="secondPass".
Edit:
Example :
<root>
<a>Init</a>
</root>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="firstPassResult">
<xsl:apply-templates select="/" mode="firstPass"/>
</xsl:variable>
<xsl:template match="/" mode="firstPass">
<test>
<firstPass>
<xsl:value-of select="root/a"/>
</firstPass>
</test>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="$firstPassResult" mode="secondPass"/>
</xsl:template>
<xsl:template match="/" mode="secondPass">
<xsl:message terminate="no">
<xsl:copy-of select="."/>
</xsl:message>
</xsl:template>
</xsl:stylesheet>
Output :
[xslt] <test><firstPass>Init</firstPass></test>
So the first pass creates some elements with the content of root/a and the second one prints the created elements to std out. Hopefully this is enough to get you going.
Yes, with XSLT 2.0 it is easy. With XSLT 1.0 you can of course also use modes and store a temporary result in a variable the same way as in XSLT 2.0 but the variable is then a result tree fragment, to be able to process it further with apply-templates you need to use an extension function like exsl:node-set on the variable.