Moving similar nodes to new node using XSLT - xslt

I have the following input xml:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<PQGetCareGaps>
<METHOD>GET</METHOD>
<contract>HXXXX</contract>
</PQGetCareGaps>
<FinalCareGapResults>
<Gap>
<CareGap>Colorectal Cancer Screening</CareGap>
<GapHistory>
<row>
<MEMBERID>AAAAAA000016-00</MEMBERID>
</row>
</GapHistory>
</Gap>
</FinalCareGapResults>
<FinalCareGapResults>
<Gap>
<CareGap>Adult BMI Assessment</CareGap>
</Gap>
</FinalCareGapResults>
</response>
I want to modify the above xml in such a way that all the <Gap> nodes should come under a new node called <TestResults>. The resultant xml should look like below:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<PQGetCareGaps>
<METHOD>GET</METHOD>
<contract>HXXXX</contract>
</PQGetCareGaps>
<TestResults>
<Gap>
<CareGap>Colorectal Cancer Screening</CareGap>
<GapHistory>
<row>
<MEMBERID>AAAAAA000016-00</MEMBERID>
</row>
</GapHistory>
</Gap>
<Gap>
<CareGap>Adult BMI Assessment</CareGap>
</Gap>
</TestResults>
</response>
Could you please help me out?

Try this
<?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="1.0">
<xsl:template match="/">
<xsl:apply-templates select="response" />
</xsl:template>
<xsl:template match="response">
<response>
<xsl:copy-of select="PQGetCareGaps" />
<TestResults>
<xsl:for-each select="FinalCareGapResults/Gap">
<xsl:copy-of select="." />
</xsl:for-each>
</TestResults>
</response>
</xsl:template>
</xsl:stylesheet>

Related

Regex reverse lookup in xsl matches function

I am trying to identify phone numbers that do not match certain criteria. Among oher things, I want to match international numbers that are not French (ie not +33 nor 0033). I have a regex that uses a reverse lookup (ie "?!") to say "not 33" which works when I test it alone. However when I try to use it in an xsl stylesheet I get an error "Invalid regular expression FORX0002".
Here is the part of my Regex that is causing me problems
^((\+|00)(?!33)[1-9]\d{0,2})\D?(\d\D?){9,14}$
Simplified stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="fo fn xs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<root class="object">
<contacts class="array">
<xsl:for-each select="//row">
<row>
<xsl:if test="not(matches(., '^((\+|00)(?!33)[1-9]\d{0,2})\D?(\d\D?){9,14}$'))">
<Business_Phone>
<xsl:value-of select="."/>
</Business_Phone>
</xsl:if>
</row>
</xsl:for-each>
</contacts>
</root>
</xsl:template>
</xsl:stylesheet>
Sample data:
<?xml version="1.0" encoding="utf-8"?>
<root>
<row>
<Identifier>02</Identifier>
<Name>John Doe</Name>
<Business_Phone>+34 67.89</Business_Phone>
</row>
<row>
<Identifier>03</Identifier>
<Name>Jim Doe</Name>
<Business_Phone>+33 123 456 789</Business_Phone>
</row>
</root>
With this sample I am expecting to get:
<root class="object">
<contacts class="array">
<row>
<Identifier class="string">02</Identifier>
<Business_Phone>+34 67.89</Business_Phone>
</row>
</contacts>
</root>
Is there a syntax I can use to make this work?
Many thanks

change element name with attribute value using XSLT

I want to change element name with attribute value
My input xml looks like below
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<ROW id="1">
<D n="ord_code">10033</D>
<D n="po_commts">Part and Service Requisition Example</D>
<D n="createddt">2021-02-11 00:00:00.0</createddt>
</ROW>
<ROW id="2">
<D n="ord_code">10018</D>
<D n="po_commts">GL Test</D>
<D n="createddt">2021-02-04 00:00:00.0</D>
</ROW>
</Envelope>
my expected result child element name replaced with attribute value
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<ROW id="1">
<ord_code>10033</ord_code>
<po_commts>Part and Service Requisition Example</po_commts>
<createddt>2021-02-11 00:00:00.0</createddt>
</ROW>
<ROW id="2">
<ord_code>10018</ord_code>
<po_commts>GL Test</po_commts>
<createddt>2021-02-04 00:00:00.0</createddt>
</ROW>
</Envelope>
Here's how you could do it:
<?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" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="D">
<xsl:element name="{#n}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
See it working here : https://xsltfiddle.liberty-development.net/gVrvcy2

XSLT for-each-group from variable does not work

for-each-group from XSLT 2.0 works as expected from a file but not from a variable.
Have this file:
~$ cat test.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<delimiter/>
<c>A</c><c>B</c>
<delimiter/>
<c>C</c>
</root>
Using stylesheet for grouping this file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0"
encoding="UTF-8" indent="yes" omit-xml-declaration="no" />
<xsl:template match="*">
<!-- variable not used for file test -->
<xsl:variable name="fields">
<root>
<delimiter/>
<c>A</c><c>B</c>
<delimiter/>
<c>C</c>
</root>
</xsl:variable>
<xsl:for-each-group select="*" group-starting-with="delimiter">
<field>
<xsl:for-each select="current-group()">
<xsl:value-of select="self::c"/>
</xsl:for-each>
</field>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
I get the result I want:
<?xml version="1.0" encoding="UTF-8"?>
<field>AB</field>
<field>C</field>
Trying to group the variable name="fields" with:
<xsl:for-each-group select="$fields/*" group-starting-with="delimiter">
I get the result:
<?xml version="1.0" encoding="UTF-8"?>
<field/>
Why does for-each-group works on a file but not from a variable?
The variable fields is a document-node(), you can define the type of the variable to be element()
<xsl:variable name="fields" as="element()">
<root>
<delimiter/>
<c>A</c><c>B</c>
<delimiter/>
<c>C</c>
</root>
</xsl:variable>

Importing OAI source into Filemaker

I have a problem with importing an OAI source into Filemaker. The mapping is ok but the result is empty.
This is the source:
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://dublincore.org/documents/dcmi-namespace/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
<responseDate>2015-01-15T12:05:11Z</responseDate>
<request verb="ListRecords" metadataPrefix="oai_dc">
http://api.memorix-maior.nl/collectiebeheer/oai-pmh/key/SORRY_THIS_KEY_I_CANNOT_SHOW/tenant/nfm
</request>
<ListRecords>
<record>
<header>
<identifier>
e:1d59bf74-a57c-11e1-af90-bf6f69fae6b6:000a80bf-e7d6-7670-b2bd-c269b2e58878
</identifier>
etc.
And this is the xslt I made:
<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<METADATA>
<FIELD NAME="identifier" TYPE="TEXT"/>
</METADATA>
<RESULTSET>
<xsl:for-each select="OAI-PMH/ListRecords/record">
<ROW>
<COL>
<DATA><xsl:value-of select="header/identifier"/></DATA>
</COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>
To make it clear I only pointed the first field in the OAI source.
I hope you can help me to fix this.
Best regards,
Boudewijn Ridder
The reason why your attempt doesn't work is that the source XML nodes are in a namespace. You must declare this namespace in your stylesheet, assign it a prefix and use that prefix when addressing the nodes:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:oai="http://www.openarchives.org/OAI/2.0/">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<METADATA>
<FIELD NAME="identifier" TYPE="TEXT"/>
</METADATA>
<RESULTSET>
<xsl:for-each select="oai:OAI-PMH/oai:ListRecords/oai:record">
<ROW>
<COL>
<DATA><xsl:value-of select="oai:header/oai:identifier"/></DATA>
</COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>
Note:
If your input example is representative, you might want to use :
<xsl:value-of select="normalize-space(oai:header/oai:identifier)"/>
to trim the extraneous whitespace from the result.

XSLT for picking the first detail corresponding to one field

I am writing xslt for filtering the data based on one of the fields.
Here is my input xml:
<?xml version="1.0" encoding="UTF-8"?>
<Consumer>
<header>
<msfnm>MSFNM</msfnm>
<mslnm>MSLNM</mslnm>
<msmnm>MSMNM</msmnm>
<msssn>MSSSN</msssn>
<csscstno>CSSCSTNO</csscstno>
<msact>MSACT</msact>
</header>
<data>
<msfnm>Nitin</msfnm>
<mslnm>Jain</mslnm>
<msmnm/>
<msssn>123</msssn>
<csscstno>111</csscstno>
<msact>1234</msact>
</data>
<data>
<msfnm>Nitin1</msfnm>
<mslnm>Jain1</mslnm>
<msmnm>L1</msmnm>
<msssn>1233</msssn>
<csscstno>111</csscstno>
<msact>1233556</msact>
</data>
<data>
<msfnm>Nitin2</msfnm>
<mslnm>Jain2</mslnm>
<msmnm>L1</msmnm>
<msssn>1234</msssn>
<csscstno>123</csscstno>
<msact>12334256</msact>
</data>
<data>
<msfnm>Nitin</msfnm>
<mslnm>Jain</mslnm>
<msmnm/>
<msssn>123</msssn>
<csscstno>111</csscstno>
<msact>1234</msact>
</data>
and I want my output xml should be like
<?xml version="1.0" encoding="UTF-8"?>
<Consumer>
<data>
<msfnm>Nitin</msfnm>
<mslnm>Jain</mslnm>
<msmnm/>
<msssn>123</msssn>
<csscstno>111</csscstno>
<msact>1234</msact>
</data>
<data>
<msfnm>Nitin2</msfnm>
<mslnm>Jain2</mslnm>
<msmnm>L1</msmnm>
<msssn>1234</msssn>
<csscstno>123</csscstno>
<msact>12334256</msact>
</data>
</Consumer>
Condition: basically what I want, to take first occurrence of csscstno only. If in next occurrence, csscstno is same, then the whole set should be rejected.
My xslt:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xml>
<xsl:for-each select="/Consumer/data">
<Consumer>
<msfnm><xsl:value-of select="msfnm"/></msfnm>
<mslnm><xsl:value-of select="mslnm"/></mslnm>
<msmnm><xsl:value-of select="msmnm"/></msmnm>
<msssn><xsl:value-of select="msssn"/></msssn>
<xsl:if test="position()=1">
<csscstno><xsl:value-of select="csscstno"/></csscstno>
</xsl:if>
<msact><xsl:value-of select="msact"/></msact>
</Consumer>
</xsl:for-each>
</xml>
</xsl:template>
</xsl:stylesheet>
This is not working. Let me know, what I am doing wrong here.
You can use keys. Just like the stylesheet below:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes" />
<!-- create a key for your target node -->
<xsl:key name="Customer_No" match="csscstno" use="."/>
<!-- copy only the 1st matched keys in the output -->
<xsl:template match="/">
<Consumer>
<xsl:copy-of select="Consumer/data[count(csscstno | key('Customer_No', csscstno)[1]) = 1]"/>
<!-- or you can use the following line instead
<xsl:copy-of select="Consumer/data[generate-id(csscstno) = generate-id(key('Customer_No', csscstno)[1])]"/>
-->
</Consumer>
</xsl:template>
</xsl:transform>
when this is applied to your input XML, the result is:
<?xml version="1.0" encoding="utf-8"?>
<Consumer>
<data>
<msfnm>Nitin</msfnm>
<mslnm>Jain</mslnm>
<msmnm/>
<msssn>123</msssn>
<csscstno>111</csscstno>
<msact>1234</msact>
</data>
<data>
<msfnm>Nitin2</msfnm>
<mslnm>Jain2</mslnm>
<msmnm>L1</msmnm>
<msssn>1234</msssn>
<csscstno>123</csscstno>
<msact>12334256</msact>
</data>
</Consumer>
Or, you can use keys for grouping data by csscstno to get the first occurence. Also, You can use a copy-of to select "data":
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="dataByCsscstno" match = "data" use = "csscstno"/>
<xsl:template match="/">
<Consumer>
<xsl:copy-of select="/Consumer/data[generate-id() = generate-id(key('dataByCsscstno', csscstno)[1])]"/>
</Consumer>
</xsl:template>
</xsl:stylesheet>
Eventually I have found another way, which is working as expected.
Here is my xslt:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xml>
<xsl:for-each-group select="/Consumer/data" group-by="csscstno">
<xsl:copy-of select="current-group()[1]"/>
</xsl:for-each-group>
</xml>
</xsl:template>
</xsl:stylesheet>