How to get distinct values from different xpaths in xslt - xslt-2.0

I would like to achieve distinct values from the given input file using xslt
here is my input:
<Records count="1">
<Record contentId="2410630" levelId="442" levelGuid="29c1b6a4-b7db-49dc-a703-e78aa1b1246a" moduleId="875" parentId="0">
<Record contentId="2410631" levelId="458" levelGuid="67dbf848-5352-4953-a25b-1b1bbcde89be" moduleId="891" parentId="0">
<Record contentId="2208294" levelId="330" levelGuid="d25cfb04-eb2a-423c-bdab-2db21a58fd4d" moduleId="675" parentId="0">
<Field id="31799" guid="2ebbfd8e-3e89-4be5-9c1f-1b5e85950753" type="1">Unauthorized modification of Information/System - External</Field>
<Field id="31796" guid="24640c19-d1de-415b-b349-25b0af521373" type="6">2208294</Field>
<Field guid="7de0f37f-765a-4480-bbea-da1638ab3296">Cyber - 1</Field>
<Field guid="001a8562-9091-4e42-b96a-d17c33c19f6f" >Is all software that will be used for this initiative currently approved for use within</Field>
</Record>
</Record>
<Record contentId="2410632" levelId="458" levelGuid="67dbf848-5352-4953-a25b-1b1bbcde89be" moduleId="891" parentId="0">
<Record contentId="2208289" levelId="330" levelGuid="d25cfb04-eb2a-423c-bdab-2db21a58fd4d" moduleId="675" parentId="0">
<Field id="31799" guid="2ebbfd8e-3e89-4be5-9c1f-1b5e85950753" type="1">Inadequate Information Security Practices</Field>
<Field id="31796" guid="24640c19-d1de-415b-b349-25b0af521373" type="6">2208289</Field>
<Field id="41131" guid="7de0f37f-765a-4480-bbea-da1638ab3296" type="1">Cyber - 10</Field>
<Field id="41132" guid="001a8562-9091-4e42-b96a-d17c33c19f6f" type="1">Will there be a requirement to connect a 3rd party to xxxxx network and what would be the purpose of this connection?</Field>
</Record>
</Record>
<Record contentId="2410633" levelId="458" levelGuid="67dbf848-5352-4953-a25b-1b1bbcde89be" moduleId="891" parentId="0">
<Record contentId="2208270" levelId="330" levelGuid="d25cfb04-eb2a-423c-bdab-2db21a58fd4d" moduleId="675" parentId="0">
<Field id="31799" guid="2ebbfd8e-3e89-4be5-9c1f-1b5e85950753" type="1">Loss of Systems Including Data Center</Field>
<Field id="31796" guid="24640c19-d1de-415b-b349-25b0af521373" type="6">2208270</Field>
<Field id="41131" guid="7de0f37f-765a-4480-bbea-da1638ab3296" type="1">Cyber - 11</Field>
<Field id="41132" guid="001a8562-9091-4e42-b96a-d17c33c19f6f" type="1">Is this product or service hosted or recovered </Field>
</Record>
</Record>
<Record contentId="2410636" levelId="458" levelGuid="67dbf848-5352-4953-a25b-1b1bbcde89be" moduleId="891" parentId="0">
<Record contentId="2208289" levelId="330" levelGuid="d25cfb04-eb2a-423c-bdab-2db21a58fd4d" moduleId="675" parentId="0">
<Field id="31799" guid="2ebbfd8e-3e89-4be5-9c1f-1b5e85950753" type="1">Inadequate Information Security Practices</Field>
<Field id="31796" guid="24640c19-d1de-415b-b349-25b0af521373" type="6">2208289</Field>
<Field id="41131" guid="7de0f37f-765a-4480-bbea-da1638ab3296" type="1">Cyber - 3</Field>
<Field id="41132" guid="001a8562-9091-4e42-b96a-d17c33c19f6f" type="1">Define the Data Classification</Field>
</Record>
</Record>
<Record contentId="2410661" levelId="463" levelGuid="cc59604e-cc41-4253-879a-5fbde3ffd760" moduleId="896" parentId="0">
<Field id="41541" guid="bae76db7-4e46-4113-a453-68243a76d4f6" type="9">
<Reference id="2208289">Inadequate Information Security Practices</Reference>
</Field>
<Field id="41485" guid="6fe04171-26b5-4bad-9430-d6d3d592c404" type="1">QL1 - LS Test - Architecture</Field>
</Record>
<Record contentId="2410666" levelId="463" levelGuid="cc59604e-cc41-4253-879a-5fbde3ffd760" moduleId="896" parentId="0">
<Field id="41541" guid="bae76db7-4e46-4113-a453-68243a76d4f6" type="9">
<Reference id="2208273"> Loss of 50% Staff </Reference>
</Field>
<Field id="41485" guid="6fe04171-26b5-4bad-9430-d6d3d592c404" type="1">QL3 - LS Test - Architecture</Field>
</Record>
<Record contentId="2410649" levelId="462" levelGuid="83a26d99-e79d-41af-8a20-fa069f791cef" moduleId="895" parentId="0">
<Field id="41453" guid="9a764db7-a75e-4a49-9b26-de03e2bc4bb5" type="9">
<Reference id="2208328">Technology Configuration</Reference>
</Field>
<Field id="41397" guid="c9574505-854b-44c2-aa4c-4a419c80b1e6" type="1">DG - Analytics</Field>
</Record>
</Record>
</Records>
My expected output is :
<Record>
<uniqueValues>Unauthorized modification of Information/System - External</uniqueValues>
<cyber>Cyber - 1</cyber>
<question>Is all software that will be used for this initiative currently approved for use within</question>
</Record>
<Record>
<uniqueValues>Inadequate Information Security Practices</uniqueValues>
<cyber>Cyber - 10</cyber>
<question>Will there be a requirement to connect a 3rd party to xxxxx network and what would be the purpose of this connection?</question>
</Record>
<Record>
<uniqueValues>Loss of Systems Including Data Center</uniqueValues>
<cyber>Cyber - 11</cyber>
<question>Is this product or service hosted or recovered </question>
</Record>
<Record>
<uniqueValues>Loss of 50% Staff</uniqueValues>
<question>QL3 - LS Test - Architecture</question>
</Record>
<Record>
<uniqueValues>Technology Configuration</uniqueValues>
<question>DG - Analytics</question>
</Record>
I have tried using distinct values but it is giving me only atomic values i can't able to capture remining field values
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="3.0" 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" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:output method="xml"/>
<xsl:template match="Records">
<xsl:for-each select="distinct-values((Record/Record/Field[#guid ='2ebbfd8e-3e89-4be5-9c1f-1b5e85950753'],Record/Record/Field[#guid ='bae76db7-4e46-4113-a453-68243a76d4f6']/Reference,Record/Record/Field[#guid ='9a764db7-a75e-4a49-9b26-de03e2bc4bb5']/Reference))">
<Record>
<uniqueValues><xsl:value-of select="."/></uniqueValues>
</Record>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
this gives only distinct values
Could you please help me how can i write an xslt to get the expected output

Using
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-skip"/>
<xsl:output method="xml" indent="yes" />
<xsl:template match="/Records">
<xsl:copy>
<xsl:for-each-group select="Record/Record" group-by="Record/Field[#guid ='2ebbfd8e-3e89-4be5-9c1f-1b5e85950753'], Field[#guid = ('bae76db7-4e46-4113-a453-68243a76d4f6', '9a764db7-a75e-4a49-9b26-de03e2bc4bb5')]/Reference">
<xsl:copy>
<uniqueValues>{current-grouping-key()}</uniqueValues>
<xsl:apply-templates/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="Field[#guid = '7de0f37f-765a-4480-bbea-da1638ab3296']">
<cyber>
<xsl:value-of select="."/>
</cyber>
</xsl:template>
<xsl:template match="Field[#guid = ('001a8562-9091-4e42-b96a-d17c33c19f6f', '6fe04171-26b5-4bad-9430-d6d3d592c404', 'c9574505-854b-44c2-aa4c-4a419c80b1e6')]">
<question>
<xsl:value-of select="."/>
</question>
</xsl:template>
</xsl:stylesheet>
outputs
<Records>
<Record>
<uniqueValues>Unauthorized modification of Information/System - External</uniqueValues>
<cyber>Cyber - 1</cyber>
<question>Is all software that will be used for this initiative currently approved for use within</question>
</Record>
<Record>
<uniqueValues>Inadequate Information Security Practices</uniqueValues>
<cyber>Cyber - 10</cyber>
<question>Will there be a requirement to connect a 3rd party to xxxxx network and what would be the purpose of this connection?</question>
</Record>
<Record>
<uniqueValues>Loss of Systems Including Data Center</uniqueValues>
<cyber>Cyber - 11</cyber>
<question>Is this product or service hosted or recovered </question>
</Record>
<Record>
<uniqueValues> Loss of 50% Staff </uniqueValues>
<question>QL3 - LS Test - Architecture</question>
</Record>
<Record>
<uniqueValues>Technology Configuration</uniqueValues>
<question>DG - Analytics</question>
</Record>
</Records>
Of course the group-by expression with a sequence i.e. group-by="Record/Field[#guid ='2ebbfd8e-3e89-4be5-9c1f-1b5e85950753'], Field[#guid = ('bae76db7-4e46-4113-a453-68243a76d4f6', '9a764db7-a75e-4a49-9b26-de03e2bc4bb5')]/Reference" will only work if for each item in the grouping population only one of the subexpressions selects a key value and the other ones select the empty sequence.

Related

XSLT 3.0 Streaming (Saxon) facing error "There is more than one consuming operand" when I use two different string functions within same template

Here is my sample input xml
<?xml version="1.0" encoding="UTF-8"?>
<Update xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Request>
<List>
<RequestP><ManNumber>3B4</ManNumber></RequestP>
<RequestP><ManNumber>8T7_BE</ManNumber></RequestP>
<RequestP><ManNumber>3B5</ManNumber></RequestP>
<RequestP><ManNumber>5E9_BE</ManNumber></RequestP>
<RequestP><ManNumber>9X6</ManNumber></RequestP>
</List>
</Request>
</Update>
and xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" exclude-result-prefixes="#all">
<xsl:output method="xml" omit-xml-declaration="no" indent="yes" />
<xsl:mode streamable="yes" />
<xsl:template match="List/RequestP/ManNumber">
<ManNumber>
<xsl:value-of select="replace(.,'_BE','')" />
</ManNumber>
<xsl:if test="contains(.,'_BE')">
<ManDescrip>BE</ManDescrip>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I am getting below error for above xslt, I am using Saxon 11.2 version
Template rule is not streamable
* There is more than one consuming operand: {<ManNumber {xsl:value-of}/>} on line 6, and {if(fn:contains(...)) then ... else ...} on line 9
The xslt works fine if I use either "replace" or "contains" but not both within same template.
Streamed processing, if you have needs (huge input documents in the size of gigabytes) to use it, requires you to limit your XSLT to streamable code, that means you can for instance make a copy of that element and processed only that small element node as a complete in memory element in a different mode
<xsl:template match="List/RequestP/ManNumber">
<xsl:apply-templates select="copy-of(.)" mode="grounded"/>
</xsl:template>
<xsl:template name="grounded" match="ManNumber">
<ManNumber>
<xsl:value-of select="replace(.,'_BE','')" />
</ManNumber>
<xsl:if test="contains(.,'_BE')">
<ManDescrip>BE</ManDescrip>
</xsl:if>
</xsl:template>

Creating xsl:result-document with xpath 3.1 fn:transform using saxon 9.9 EE

I'd like to create an output document using the xpath 3.1 fn:transform. Following is A.xsl. It creates A.xml when run directly (from oxygen):
<?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"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:output name="xml" method="xml" indent="true" />
<xsl:template name="xsl:initial-template">
<xsl:message select="'A'"/>
<xsl:result-document href="file:/C:/Work/test/A.xml" format="xml">
<resultDoc>
<text>The result of A.</text>
</resultDoc>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>
Result: A.xml is created with the desired output:
<?xml version="1.0" encoding="UTF-8"?>
<resultDoc>
<text>The result of A.</text>
</resultDoc>
Now, using the transform function to call A.xsl:
<?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"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output name="xml" method="xml" encoding="UTF-8" indent="true" />
<!-- Global Constants -->
<xsl:variable name="xsl-file-base" select="'file:/C:/Work/test/'" as="xs:string"/>
<xsl:variable name="xsl-pipeline" select="'A.xsl'" as="xs:string"/>
<!-- Entry Point -->
<xsl:template name="xsl:initial-template">
<xsl:iterate select="$xsl-pipeline">
<xsl:variable name="file" select="$xsl-file-base || ." as="xs:string"/>
<xsl:result-document href="file:/C:/Work/test/A.xml" format="xml">
<xsl:sequence select="transform(map{'stylesheet-location' : $file})?output"/>
</xsl:result-document>
</xsl:iterate>
</xsl:template>
</xsl:stylesheet>
Result: A.xml is created but incomplete. Any help is appreciated.
<?xml version="1.0" encoding="UTF-8"?>
The result of the transform function is a map with an entry named output for the primary result document and further entries for secondary result documents. Your called stylesheet creates a secondary result with the URI file:/C:/Work/test/A.xml so
<xsl:sequence
select="transform(map{'stylesheet-location' : $file})('file:/C:/Work/test/A.xml')"/>
is more likely to produce an output.

Having difficulty in looping partNumber XSLT being applied shown below

Sample SOAP Input XML Given below:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Body>
<PullCustomerPartsPricingResponse xmlns="http://cdx.dealerbuilt.com/Api/0.99/">
<PullCustomerPartsPricingResult xmlns:a="http://schemas.datacontract.org/2004/07/DealerBuilt.BaseApi" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:CustomerPart>
<a:Placement>
<a:GroupId>10</a:GroupId>
</a:Placement>
<a:Attributes xmlns:b="http://schemas.datacontract.org/2004/07/DealerBuilt.Models.Parts">
<b:Description>PAD SET, RR.</b:Description>
<b:PartNumber>31500SB2100M</b:PartNumber>
<b:PartNumberFormatted>31500-SB2-100M</b:PartNumberFormatted>
</a:Attributes>
</a:CustomerPart>
<a:CustomerPart>
<a:Placement>
<a:GroupId>10</a:GroupId>
</a:Placement>
<a:Attributes xmlns:b="http://schemas.datacontract.org/2004/07/DealerBuilt.Models.Parts">
<b:Description>Kite SET, RR.</b:Description>
<b:PartNumber>60211T7J305ZZ</b:PartNumber>
</a:Attributes>
</a:CustomerPart>
</PullCustomerPartsPricingResult>
</PullCustomerPartsPricingResponse>
</s:Body>
</s:Envelope>
XSLT Code being applied Shown below:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<root xmlns="http://www.dataprint.com/global/3.0/rest/">
<xsl:for-each select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='PullCustomerPartsPricingResponse']/*[local-name()='PullCustomerPartsPricingResult']/*[local-name()='CustomerPart']">
<partDetail>
<partNumber>
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='PullCustomerPartsPricingResponse']/*[local-name()='PullCustomerPartsPricingResult']/*[local-name()='CustomerPart']/*[local-name()='Attributes'] /*[local-name()='PartNumber']" />
</partNumber>
<partDescription>
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='PullCustomerPartsPricingResponse']/*[local-name()='PullCustomerPartsPricingResult']/*[local-name()='CustomerPart']/*[local-name()='Attributes'] /*[local-name()='Description']" />
</partDescription>
</partDetail>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
My Current sample Output Shown below:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.dataprint.com/global/3.0/rest/">
<results>
<partDetail>
<partNumber>31500SB2100M</partNumber>
<partDescription>PAD SET, RR.</partDescription>
</partDetail>
</results>
<results>
<partDetail>
<partNumber>31500SB2100M</partNumber>
<partDescription>PAD SET, RR.</partDescription>
</partDetail>
</results>
</root>
My Desired Output shown below:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.dataprint.com/global/3.0/rest/">
<results>
<partDetail>
<partNumber>31500SB2100M</partNumber>
<partDescription>PAD SET, RR.</partDescription>
</partDetail>
</results>
<results>
<partDetail>
<partNumber>60211T7J305ZZ</partNumber>
<partDescription>Kite SET, RR.</partDescription>
</partDetail>
</results>
</root>
I'm trying to loop element " a:CustomerPart" to print PartNumber and b:Descriptionas Output but first a:CustomerPart data is getting repeated Twice.I Undestand there is code change is required,please help on this.
Eiríkr Útlendi your patience/suggestions is much appreciated
Inside the xsl:for-each, the selected CustomerPart item becomes the context item. You should then select children/descendants of this CustomerPart using a relative path starting at this context item, not an absolute path starting at the root of the document (/).
That is, you should replace
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']
/*[local-name()='PullCustomerPartsPricingResponse']
/*[local-name()='PullCustomerPartsPricingResult']
/*[local-name()='CustomerPart']
/*[local-name()='Attributes']
/*[local-name()='PartNumber']" />
by
<xsl:value-of select="*[local-name()='Attributes'] /*[local-name()='PartNumber']" />
or better, by
<xsl:value-of select="*:Attributes/*:PartNumber" />

xmlproperty and retrieving property value of xml element if specific attribute is present/set

My Xml looks like:
<root>
<foo location="bar"/>
<foo location="in" flag="123"/>
<foo location="pak"/>
<foo location="us" flag="256"/>
<foo location="blah"/>
</root>
For foo xml element flag is optional attribute.
And when I say:
<xmlproperty file="${base.dir}/build/my.xml" keeproot="false"/>
<echo message="foo(location) : ${foo(location)}"/>
prints all locations :
foo(location) : bar,in,pak,us,blah
Is there a way to get locations only if flag is set to some value?
Is there a way to get locations only if flag is set to some value?
Not with xmlproperty, no, as that will always conflate values that have the same tag name. But xmltask can do what you need as it supports the full power of XPath:
<taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask">
<classpath path="xmltask.jar" />
</taskdef>
<xmltask source="${base.dir}/build/my.xml">
<copy path="/root/foo[#flag='123' or #flag='256']/#location"
property="foo.location"
append="true" propertySeparator="," />
</xmltask>
<echo>${foo.location}</echo><!-- prints in,us -->
If you absolutely cannot use third-party tasks then I'd probably approach the problem by using a simple XSLT to extract just the bits of the XML that you do want into another file:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="targetFlag" />
<xsl:template name="ident" match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()" /></xsl:copy>
</xsl:template>
<xsl:template match="foo">
<xsl:if test="#flag = $targetFlag">
<xsl:call-template name="ident" />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Call this with the xslt task
<xslt in="${base.dir}/build/my.xml" out="filtered.xml" style="extract.xsl">
<param name="targetFlag" expression="123" />
</xslt>
This will create filtered.xml containing just
<root>
<foo location="in" flag="123"/>
</root>
(modulo changes in whitespace) and you can load this using xmlproperty in the normal way.

xslt Ant Task not passing parameters to my stylesheet

I have a style sheet like this
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="testParam"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="resources/integer[#name='LOG_LEVEL']/text()">
<xsl:value-of select="$testParam"/>
</xsl:template>
</xsl:stylesheet>
And I have an input xml like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="LOG_LEVEL">3</integer>
<string name="app_name">Test Application</string>
</resources>
But when I try to call an xslt transform in ant using this:
<xslt in="in.xml" out="out.xml" style="style_above.xsl">
<outputproperty name="method" value="xml"/>
<outputproperty name="encoding" value="UTF-8"/>
<outputproperty name="indent" value="yes"/>
<param name="testParam" expression="test"/>
</xslt>
I get the following:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<integer name="LOG_LEVEL"/>
<string name="app_name">Test Application</string>
</resources>
it doesn't seem to be changing my xslt parameter to the value I specify in my ant target
Yep, i figured out the problem was a different thing. Was about to update this was too late. I defined the xslt task in a macro and have a optional element also named param and that was the culprit. Thanks

Resources