How to compare 2 stage files and find out unique values from stage file 1 in OIC XSLT Mapper? - xslt-2.0

I have two stage files where I have to compare Job_Title from file 1 with JOB_NAME of file 2, I have to display the unique Job_Title values from file 1. My code is working as expected
File 1:
'
<ReadResponse xmlns="http://xmlns.oracle.com/cloud/adapter/stagefile/ReadFilteredInputFile_REQUEST/types">
<Records xmlns="http://TargetNamespace.com/fileReference/ReadFilteredInputFile">
<Record>
<Last_Name>Abbassi</Last_Name>
<First_Name>Bob</First_Name>
<Work_Contact_Work_Email>babbassi#xyz.com</Work_Contact_Work_Email>
<Hire_Date>6/25/2012</Hire_Date>
<Job_Title_Description>ASSOCIATE DIRECTOR</Job_Title_Description>
<Job_Title_Code>ADIR</Job_Title_Code>
<Home_Department_Description>Finance</Home_Department_Description>
<Home_Department>121</Home_Department>
<Rehire_Date></Rehire_Date>
<Termination_Date></Termination_Date>
<Position_ID>DXM001019</Position_ID>
<Position_Status>Active</Position_Status>
<Reports_To_Position_ID>DXM001077</Reports_To_Position_ID>
<Reports_To_Name>Lee, Todd</Reports_To_Name>
<This_is_a_Management_position>No</This_is_a_Management_position>
<Job_Title>Associate Director, FP&A</Job_Title>
</Record>
<Record>
<Last_Name>Abejuela</Last_Name>
<First_Name>Jowdy</First_Name>
<Work_Contact_Work_Email>jabejuela#xyz.com</Work_Contact_Work_Email>
<Hire_Date>12/9/2019</Hire_Date>
<Job_Title_Description>SENIOR MANAGER</Job_Title_Description>
<Job_Title_Code>SRMG</Job_Title_Code>
<Home_Department_Description>Quality General</Home_Department_Description>
<Home_Department>461</Home_Department>
<Rehire_Date></Rehire_Date>
<Termination_Date></Termination_Date>
<Position_ID>DXM001514</Position_ID>
<Position_Status>Active</Position_Status>
<Reports_To_Position_ID>DXM001629</Reports_To_Position_ID>
<Reports_To_Name>Golden, Kihlon</Reports_To_Name>
<This_is_a_Management_position>No</This_is_a_Management_position>
<Job_Title>Senior Manager, QA</Job_Title>
</Record>
<Record>
<Last_Name>Acosta</Last_Name>
<First_Name>Andrea</First_Name>
<Work_Contact_Work_Email>aacosta#xyz.com</Work_Contact_Work_Email>
<Hire_Date>5/18/2020</Hire_Date>
<Job_Title_Description>RESEARCH ASSOCIATE</Job_Title_Description>
<Job_Title_Code>RA</Job_Title_Code>
<Home_Department_Description>Analytical</Home_Department_Description>
<Home_Department>442</Home_Department>
<Rehire_Date></Rehire_Date>
<Termination_Date></Termination_Date>
<Position_ID>DXM001536</Position_ID>
<Position_Status>Active</Position_Status>
<Reports_To_Position_ID>DXM001026</Reports_To_Position_ID>
<Reports_To_Name>Kuehne, Henriette</Reports_To_Name>
<This_is_a_Management_position>No</This_is_a_Management_position>
<Job_Title>Research Associate</Job_Title>
</Record>
</Records>
</ReadResponse>
'
File 2:
'
<ReadResponse xmlns="http://xmlns.oracle.com/cloud/adapter/stagefile/ReadDecodedOTBIJobsResponse_REQUEST/types">
<JobSet xmlns="http://TargetNamespace.com/fileReference/ReadDecodedOTBIJobsResponse">
<Job>
<JOB_ID>300000004123258</JOB_ID>
<JOB_CODE>0002</JOB_CODE>
<JOB_NAME>Senior Manager, QA</JOB_NAME>
</Job>
<Job>
<JOB_ID>300000004123195</JOB_ID>
<JOB_CODE>0001</JOB_CODE>
<JOB_NAME>Associate Director, FP&A</JOB_NAME>
</Job>
</JobSet>
</ReadResponse>
'
Expected output:
'
<JobSet>
<Job>
<Job_Title>Research Associate</JobTitle>
</Job>
</JobSet>
'
My code:
<ns50:NewJobSet>
<xsl:for-each select="$ReadFilteredInputFile/nsmpr8:ReadResponse/nsmpr3:Records/nsmpr3:Record">
<xsl:variable name="jobTitle" select="nsmpr3:Job_Title"/>
<xsl:for-each select="$ReadDecodedOTBIJobsResponse/nsmpr7:ReadResponse/ns37:JobSet/ns37:Job">
<xsl:variable name="jobName" select="ns37:JOB_NAME"/>
<xsl:choose>
<xsl:when test="ns37:JOB_NAME = $jobTitle"/>
<xsl:otherwise>
<ns50:NewJob>
<ns50:Job_Title>
<xsl:value-of select="$jobName"/>
</ns50:Job_Title>
</ns50:NewJob>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</ns50:NewJobSet>
My code is returning duplicates incase if there any matching records between two file, Could anyone please help to fix this ?

I would use a key
<xsl:key name="jobs" match="Job" use="JOB_NAME" xpath-default-namespace="http://TargetNamespace.com/fileReference/ReadDecodedOTBIJobsResponse"/>
and then check e.g.
<xsl:template match="Record/Job_Title[not(key('jobs', ., $jobs))]" xpath-default-namespace="http://TargetNamespace.com/fileReference/ReadFilteredInputFile">
<Job_Title>
<xsl:value-of select="."/>
</Job_Title>
</xsl:template>
where $jobs is the variable holding your second document.
And an empty template for those elements that have references in the second file e.g. <xsl:template match="Record/Job_Title[key('jobs', ., $jobs)]" xpath-default-namespace="http://TargetNamespace.com/fileReference/ReadFilteredInputFile"/>

Related

How to fix collection() function problem in XSLT

collection() function does not encount all 50 letters, only the one on which the transformation is done.
I have to transform many xml-letters in a csv file with the parameters "Key", "bezeichnung", and "reference" from any letter located in the same folder. When I execute the transformation, I get only the data from the actually transformed file and not from all 50 letters. What is the problem here with the collection() function because everything else works
<xsl:variable name="briefe"
select="collection('./?select=l_*.xml')"/>
<xsl:template match="/">
<xsl:text>"Key","Bezeichnung","Referenz"</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each-group select="//tei:correspAction[#type='received']/tei:placeName" group-by="#key">
<xsl:sort select="." />
<xsl:text>"</xsl:text><xsl:value-of select="current-grouping-key()" /><xsl:text>",</xsl:text>
<xsl:text>"</xsl:text><xsl:value-of select="distinct-values(//tei:correspAction[#type='received']/tei:placeName)" /><xsl:text>",</xsl:text>
<xsl:text>"</xsl:text><xsl:value-of select="//tei:correspAction[#type='received']/tei:placeName/#ref" /><xsl:text>"</xsl:text>
<xsl:text>
</xsl:text>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

Return a value type of element from a function in XSLT

This is my XML
<report>
<format-inputs>
<narrative-entity-ids>
<entity id="28495795" type-cdf-meaning="DIAGNOSES"/>
<entity id="28495741" type-cdf-meaning="DIAGNOSES"/>
<entity id="28495471" type-cdf-meaning="DIAGNOSES"/>
</narrative-entity-ids>
</format-inputs>
</report>
I am creating a function in commonFunction.xslt
<xsl:function name="cdocfx:createEntityIdList" >
<xsl:param name="formatInputsNodes"/>
<xsl:if test="fn:exists(n:report/n:format-inputs)"
<xsl:variable name="entityIdList" as="element()*">
<xsl:for-each select="$formatInputsNodes/n:narrative-entity-ids/n:entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
</xsl:variable>
</xsl:if>
<xsl:copy-of select="$entityIdList"/>
</xsl:function>
I am calling this function in other xslt file where commonFunction.xslt was included
<xsl:variable name="entityIdList" select="cdocfx:createEntityIdList(n:report/n:format-inputs)"/>
</xsl:variable>
My question is variable entityIdList should be value type of element but it is having the document-node type how can i achieve this ??
Please provide minimal but complete samples of XML input, XSLT you have, output you want and the one you get together with any exact error messages you have encountered.
I am currently not sure I understand what you want to achieve, if you construct a variable of type element()* you seem to want to construct a sequence of element nodes. Any xsl:value-of however will only output the string values of the selected items in a text node so it is not clear why you first construct elements if you only want to output string values. If you construct nodes and want to output them use xsl:copy-of or xsl:sequence, not xsl:value-of.
To show two examples of writing a function that returns a sequence of elements (i.e. whose result is of type element()*) I have set up https://xsltfiddle.liberty-development.net/3NzcBtE which has two functions
<xsl:function name="mf:ex1">
<xsl:param name="input" as="element()*"/>
<xsl:for-each select="$input">
<item>{ #id }</item>
</xsl:for-each>
</xsl:function>
<xsl:function name="mf:ex2">
<xsl:param name="input" as="element()*"/>
<xsl:variable name="elements" as="element()*">
<xsl:for-each select="$input">
<item>{ #id }</item>
</xsl:for-each>
</xsl:variable>
<xsl:sequence select="$elements"/>
</xsl:function>
the first simply directly constructs some result elements in the function body, that way the result is a sequence of element nodes. The second function uses your approach of constructing a sequence of elements nodes in a variable, the proper way to return that variable value then from the function is to use xsl:sequence.
It is not clear at which position of the posted code you think are dealing with a document-node() node.
Note also that
<xsl:choose>
<xsl:when test="fn:exists($formatInputsNodes/n:narrative-entity-ids)">
<xsl:for-each select="$formatInputsNodes/n:narrative-entity-ids/n:entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
</xsl:when>
</xsl:choose>
can be simplified to
<xsl:for-each select="$formatInputsNodes/n:narrative-entity-ids/n:entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
As you have now presented an XML input that is at least well-formed and some XSLT snippets (that are unfortunately not well-formed and seem to use a namespace although the XML input shown doesn't use one) here is an attempt to morph that into a working sample
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:cdocfx="http://example.com/cdox-functions"
exclude-result-prefixes="#all"
version="3.0">
<xsl:function name="cdocfx:createEntityIdList" >
<xsl:param name="formatInputsNodes"/>
<xsl:variable name="entityIdList" as="element()*">
<xsl:for-each select="$formatInputsNodes/narrative-entity-ids/entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
</xsl:variable>
<xsl:copy-of select="$entityIdList"/>
</xsl:function>
<xsl:variable name="entityIdList" select="cdocfx:createEntityIdList(report/format-inputs)"/>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="$entityIdList instance of element()*, $entityIdList" separator=", "/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/pPqsHTW/1
Output there for the check $entityIdList instance of element()* is true so I am not sure why you say you have a document node.

Substring in XSLT2.0

I need help with one scenario: I need to substring before the last occurrence of space, hyphen or full stop characters in a text. I tried with substring-before
For example, If text is 'SIR WILLIAM SIEMENS SQUARE', then I need to have 'SIR WILLIAM SIEMENS' as first text string and 'SQUARE'. Using Substring before I am getting 'SIR' and 'WILLIAM SIEMENS SQUARE'.
First you need to replace (. and -) with space and then it will work. Try like this.
<xsl:template match="/">
<root>
<xsl:variable name="maintext" select="replace(replace(/root/a, '\.', '. '), '-', '- ')"/>
<a><xsl:value-of select="tokenize($maintext, ' ')[position() != last()]"/></a>
<b><xsl:value-of select="tokenize($maintext, ' ')[last()]"/></b>
</root>
</xsl:template>
You should do like this:
<xsl:template match="/">
<a><xsl:value-of select="tokenize(., ' ')[position() != last()]"/></a>
<b><xsl:value-of select="tokenize(., ' ')[last()]"/></b>
</xsl:template>
you can also use analyze-string
<xsl:template match="/">
<xsl:analyze-string select="normalize-space(.)" regex="(.+[\W])([\w]+)">
<xsl:matching-substring>
<a><xsl:value-of select="normalize-space(regex-group(1))"/></a>
<b><xsl:value-of select="regex-group(2)"/></b>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>

apply templates select substring-after

I've the below XML line.
<toc-title>1. <content-style>Short title</content-style></toc-title>
here i wanted to apply templates on substring-after 1.
I tried the below XSLT.
<xsl:template match="toc-title/text()" mode="x">
<xsl:analyze-string select="substring-after(.,' ')" regex="([a-z]+)">
<xsl:matching-substring>
<xsl:apply-templates select="regex-group(1)" mode="x"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:template match="text()" mode="x">
</xsl:template>
but when i run this it throws the below error.
XSLT 2.0 Debugging Error: Error: file:///C:/Users/u0138039/Desktop/FLPHK_CHAP.xsl:239: Not a node item - item has type xs:string with value 'title' - Details: - XTTE0520: The result of evaluating the 'select' attribute of the <xsl:apply-templates> instruction may only contain nodes
I'm, unable to how to do this. please help me on fixing it.
Thanks

XSLT 2 - pick item from tokenize()'d list by index

My environment is SAXON (last nights build) using XSLT 2.0. My real problem is that the XML document specification is sub-optimal, and in a way, my problem relates to fixing/working around that design issue.
I have a node type (<weaponmodesdata>) where all the direct children are |-separated string lists of 1-or-many elements (each child of the same <weaponmodesdata> will have the same length). I need to go over the various modes represented and "unspin" them out to separate item lists (in plain text), rather than having them all smooshed together.
Unfortunately right now I'm getting a really stubborn
XPTY0020: Required item type of the context item for the child axis is node(); supplied
value has item type xs:string
error on the lines where I pass the node that needs to be split up into my little template.
Currently I have
<xsl:template match="trait" mode="attack">
<xsl:for-each select="tokenize(weaponmodesdata/mode, '\|')">
<xsl:variable name="count" select="position()"/>
<xsl:value-of select="name"/><xsl:text> - </xsl:text>
<xsl:call-template name="split_weaponmode">
<xsl:with-param name="source" select="weaponmodesdata/damage"/>
<xsl:with-param name="item" select="$count"/>
</xsl:call-template>
<xsl:text> </xsl:text>
<xsl:call-template name="split_weaponmode">
<xsl:with-param name="source" select="weaponmodesdata/damtype"/>
<xsl:with-param name="item" select="$count"/>
</xsl:call-template>
<!-- more will go here eventually -->
<xsl:text>.
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="split_weaponmode">
<xsl:param name="source"/>
<xsl:param name="item"/>
<xsl:variable name="parts" select="tokenize($source, '\|')"/>
<xsl:for-each select="$parts">
<xsl:if test="position() = $item">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
An example XML subtree relating to my issue:
<character>
<trait id="1">
<name>Spear</name>
<weaponmodesdata>
<mode>1H Thrust|2H Thrust|Thrown</mode>
<damage>thr+2|thr+3|thr+3</damage>
<damtype>imp|imp|imp</damtype>
</weaponmodesdata>
</trait>
<trait id="2">
<name>Broadsword</name>
<weaponmodesdata>
<mode>1H Thrust|1H Swing</mode>
<damage>thr+1|sw+2</damage>
<damtype>imp|cut</damtype>
</weaponmodesdata>
</trait>
</character>
Example desired output:
Spear - 1H Thrust; thr+2 imp.
Spear - 2H Thrust; thr+3 imp.
Spear - Thrown; thr+3 imp.
Broadsword - 1H Thrust; thr+1 imp.
Broadsword - 1H Swing; sw+2 cut.
One issue (that one causing the error message) with your code is that your for-each operates on a sequence of string value (i.e. inside the for-each body the context item is a string value), yet you have relative XPath expressions like weaponmodesdata/damage that require a context node to makes sense. So you would need to use a variable outside of the for-each to store your context node.
But I think you can simplify your code to
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="trait">
<xsl:variable name="this" select="."/>
<xsl:variable name="count" select="count(tokenize(weaponmodesdata/*[1], '\|'))"/>
<xsl:for-each-group select="weaponmodesdata/*/tokenize(., '\|')" group-by="position() mod $count">
<xsl:value-of select="$this/name"/>
<xsl:text> - </xsl:text>
<xsl:value-of select="current-group()"/>
<xsl:text>.
</xsl:text>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
If you want to stick with your approach of calling templates then make sure you store the context node of the template using e.g. <xsl:variable name="this" select="."/> so that you can access it inside of the for-each iterating over a string item.

Resources