append data ton an element, based on matching this exact element - xslt-3.0

Given the following XML as input,
<?xml version="1.0" encoding="UTF-8"?>
<TABLE NAME="TABLE.DB">
<DATA RECORDS="2">
<RECORD ID="4">
<RECNO>0</RECNO>
<SEQ>0</SEQ>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10354</NUMBER>
<CN>PL</CN>
<PROPERTY>0</PROPERTY>
<DAYS>0</DAYS>
<CURRENTSTATUS>0</CURRENTSTATUS>
<TOTALS>1</TOTALS>
</RECORD>
<RECORD ID="3">
<RECNO>1</RECNO>
<SEQ>0</SEQ>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10355</NUMBER>
<CN>PL</CN>
<PROPERTY>0</PROPERTY>
<DAYS>0</DAYS>
<CURRENTSTATUS>0</CURRENTSTATUS>
<TOTALS>1</TOTALS>
</RECORD>
<RECORD ID="2">
<RECNO>2</RECNO>
<SEQUENCE>0</SEQUENCE>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10356</NUMBER>
<CN>PL 300 L</CN>
<PROPERTY>0</PROPERTY>
<DAYS>10</DAYS>
<CURRENTSTATUS>0</CURRENTSTATUS>
<SUB_A>Some random data not matched.</SUB_A>
</RECORD>
<RECORD ID="1">
<RECNO>3</RECNO>
<SEQUENCE>0</SEQUENCE>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10357</NUMBER>
<CN>PL 300 L</CN>
<PROPERTY>0</PROPERTY>
<DAYS>10</DAYS>
<CURRENTSTATUS>0</CURRENTSTATUS>
<TOTALS>19837</TOTALS>
</RECORD>
<RECORD ID="0">
<RECNO>3</RECNO>
<SEQUENCE>0</SEQUENCE>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10358</NUMBER>
<CN>PL 300 L</CN>
<PROPERTY>0</PROPERTY>
<DAYS>10</DAYS>
<CURRENTSTATUS>0</CURRENTSTATUS>
</RECORD>
</DATA>
</TABLE>
and the following tab separated file:
[yet another value.]\t10358
value i'd like to add\t10355
(another) value i'd like to add\t10357
i used \t, in order to show where the tab exists in the file. i would like to append the data found in the first column, into the element i try to match on, which in this case in NUMBER.
So if NUMBER equals second column, append to it the value found in the first column, using | as a separator.
how one could have the below result, but with keeping the order of the records, sorting on the element Output:
<?xml version="1.0" encoding="UTF-8"?>
<TABLE NAME="TABLE.DB">
<DATA RECORDS="2">
<RECORD ID="0">
<RECNO>3</RECNO>
<SEQUENCE>0</SEQUENCE>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10358 | [yet another value.]</NUMBER>
<CN>PL 300 L</CN>
<PROPERTY>0</PROPERTY>
<DAYS>10</DAYS>
<CURRENTSTATUS>0</CURRENTSTATUS>
</RECORD>
<RECORD ID="1">
<RECNO>3</RECNO>
<SEQUENCE>0</SEQUENCE>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10357 | (another) value i'd like to add</NUMBER>
<CN>PL 300 L</CN>
<PROPERTY>0</PROPERTY>
<DAYS>10</DAYS>
<CURRENTSTATUS>0</CURRENTSTATUS>
<TOTALS>19837</TOTALS>
</RECORD>
<RECORD ID="2">
<RECNO>2</RECNO>
<SEQUENCE>0</SEQUENCE>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10356</NUMBER>
<CN>PL 300 L</CN>
<PROPERTY>0</PROPERTY>
<DAYS>10</DAYS>
<CURRENTSTATUS>0</CURRENTSTATUS>
<SUB_A>Some random data not matched.</SUB_A>
</RECORD>
<RECORD ID="3">
<RECNO>1</RECNO>
<SEQ>0</SEQ>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10355 | value i'd like to add</NUMBER>
<CN>PL</CN>
<PROPERTY>0</PROPERTY>
<DAYS>0</DAYS>
<CURRENTSTATUS>0</CURRENTSTATUS>
<TOTALS>1</TOTALS>
</RECORD>
<RECORD ID="4">
<RECNO>0</RECNO>
<SEQ>0</SEQ>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10354</NUMBER>
<CN>PL</CN>
<PROPERTY>0</PROPERTY>
<DAYS>0</DAYS>
<CURRENTSTATUS>0</CURRENTSTATUS>
<TOTALS>1</TOTALS>
</RECORD>
</DATA>
</TABLE>
I use Saxon latest, v 9.8

The xsl:merge is similar to your previous question and to sort the merge result you can simply wrap the xsl:merge into an xsl:perform-sort:
<?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"
expand-text="yes" version="3.0">
<xsl:param name="text-uri" as="xs:string">test2017100602.txt</xsl:param>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="lines" as="element(line)*">
<xsl:apply-templates select="unparsed-text-lines($text-uri)"/>
</xsl:variable>
<xsl:template match=".[. instance of xs:string]">
<xsl:variable name="tokens" as="xs:string*" select="tokenize(., ' ')[normalize-space()]"/>
<line number="{$tokens[2]}">{$tokens[1]}</line>
</xsl:template>
<xsl:template match="TABLE/DATA">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:perform-sort>
<xsl:sort select="xs:integer(#ID)"/>
<xsl:merge>
<xsl:merge-source name="record" select="RECORD">
<xsl:merge-key select="NUMBER"/>
</xsl:merge-source>
<xsl:merge-source name="line" select="$lines" sort-before-merge="yes">
<xsl:merge-key select="#number"/>
</xsl:merge-source>
<xsl:merge-action>
<xsl:if test="current-merge-group('record')">
<xsl:copy>
<xsl:apply-templates select="#*, NUMBER/preceding-sibling::*"/>
<NUMBER>
<xsl:value-of select="NUMBER, current-merge-group()[2]"
separator=" | "/>
</NUMBER>
<xsl:apply-templates select="NUMBER/following-sibling::*"/>
</xsl:copy>
</xsl:if>
</xsl:merge-action>
</xsl:merge>
</xsl:perform-sort>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

How to unique values from different nodes in xslt

I have below xml
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>
</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>
</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>
</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>
</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>
</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>
</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>
</Record>
</Record>
</Records>
Expected Output:
<uniqueValues>Inadequate Information Security Practices</uniqueValues>
<uniqueValues>Loss of Systems Including Data Center</uniqueValues>
<uniqueValues>Loss of 50% Staff</uniqueValues>
<uniqueValues>Technology Configuration</uniqueValues>
Here is my code
<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:variable name ="fields" select="//Metadata/FieldDefinitions" />
<!--match the root node-->
<xsl:template match="Records">
<ArcherRecords>
<xsl:for-each select="Record[#levelGuid='29c1b6a4-b7db-49dc-a703-e78aa1b1246a']">
<xsl:variable name="valuesTobeCompared" select="Record/Field[#guid='bae76db7-4e46-4113-a453-68243a76d4f6']/Reference/#id"/>
<xsl:for-each-group select="Record/Record" group-by="./Field[#guid='24640c19-d1de-415b-b349-25b0af521373']">
<xsl:choose>
<xsl:when test="$valuesTobeCompared = ./Field[#guid='24640c19-d1de-415b-b349-25b0af521373']">
</xsl:when>
<xsl:otherwise>
<uniqueValues><xsl:value-of select="./Field[#guid='24640c19-d1de-415b-b349-25b0af521373']"/></uniqueValues>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:for-each>
</ArcherRecords>
</xsl:template>
</xsl:stylesheet>
but it is only giving first set of unique values but I want all unique values from entire record set along with it should also have one value of duplicated value, I was not sure how to get all node values into one variable I am able to store only one node values into variable,
Could anybody help me how to write a xslt code to get the unique values
group-by itself will retrieve unique values from provided selectpath, but how can I give multiple node-set values to for-each-group

i need to remove prefixes using xslt-3and exclude-result-prefixes

input xml
<?xml version="1.0" encoding="UTF-8" ?>
<mr:collection
xmlns:mr="http://www.lc.gov/mr2/slim"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.lc.gov/mr2/slim http://www.lc.gov/standards/mrxml/schema/mr21slim.xsd">
<mr:rc>
<mr:dtf tg="2000" i1="1" i2=" ">
<mr:sbf cd="a">Christoph Kolumbus</mr:sbf>
<mr:sbf cd="d">John Diter</mr:sbf>
<mr:sbf cd="b">Julie Nat</mr:sbf>
<mr:sbf cd="f">Darius Milhaud</mr:sbf>
<mr:sbf cd="g">Erich kleiber</mr:sbf>
<mr:sbf cd="g">Franz Ludwig Horth</mr:sbf>
</mr:dtf>
<mr:dtf tg="3000" i1="1" i2=" ">
<mr:sbf cd="a">Christoph Kolumbus</mr:sbf>
<mr:sbf cd="d">Serg</mr:sbf>
<mr:sbf cd="b">Mak</mr:sbf>
<mr:sbf cd="f">DarMil</mr:sbf>
<mr:sbf cd="g">Erikl</mr:sbf>
<mr:sbf cd="g">LudHorth</mr:sbf>
</mr:dtf>
</mr:rc>
<mr:rc>
<mr:dtf tg="2000" i1="1" i2="0">
<mr:sbf cd="a">Chris Prante</mr:sbf>
<mr:sbf cd="e">"Chris Dietz"</mr:sbf>
</mr:dtf>
</mr:rc>
</mr:collection>
with the following xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="http://www.lc.gov/mr2/slim"
xmlns:e="https://example.com"
xmlns:dc="https://examples.com"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="collection">
<O-PM>
<ListRcs>
<xsl:apply-templates/>
</ListRcs>
</O-PM>
</xsl:template>
<xsl:template match="rc">
<e:rc>
<xsl:apply-templates/>
</e:rc>
</xsl:template>
<xsl:template match="dtf[#tg = 2000]">
<mdtd>
<rc>
<dc:title xml:lang="el">{sbf[#cd = 'a']} {sbf[#cd = 'b']}{sbf[#cd = 'e']!(':', .)} {sbf[#cd = 'f']!('/', .)}{(sbf[#cd = 'g'] => string-join(' ; '))!('', .)}</dc:title>
</rc>
</mdtd>
</xsl:template>
<xsl:template match="dtf[#tg != 2000]"/>
</xsl:stylesheet>
we get
<?xml version="1.0" encoding="UTF-8"?>
<O-PM>
<ListRcs>
<e:rc xmlns:e="https://example.com">
<mdtd>
<rc>
<dc:title xmlns:dc="https://examples.com" xml:lang="el">Christoph Kolumbus Julie Nat / Darius Milhaud Erich kleiber ; Franz Ludwig Horth</dc:title>
</rc>
</mdtd>
</e:rc>
<e:rc xmlns:e="https://example.com">
<mdtd>
<rc>
<dc:title xmlns:dc="https://examples.com" xml:lang="el">Chris Prante : "Chris Dietz" </dc:title>
</rc>
</mdtd>
</e:rc>
</ListRcs>
</O-PM>
how can one get <e:rc> instead of <e:rc xmlns:e="https://example.com">?
exclude-result-prefixes should work for literal result elements we create in our XSLT code... I need to declare namespaces and i am not getting the output i want.
ie <e:rc> instead of <e:rc xmlns:e="https://example.com">
and <dc:title xml:lang="el"> instead of <dc:title xmlns:dc="https://examples.com" xml:lang="el">
If you use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="http://www.lc.gov/mr2/slim"
xmlns:e="https://example.com"
xmlns:dc="https://examples.com"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
i.e. don't exclude the prefixes e and dc you declared for serialization you will get a root element declaring them e.g.
<O-PM xmlns:dc="https://examples.com" xmlns:e="https://example.com">
and content like
<e:rc>
<mdtd>
<rc>
<dc:title xml:lang="el">Christoph Kolumbus Julie Nat / Darius Milhaud Erich kleiber ; Franz Ludwig Horth</dc:title>
</rc>
</mdtd>
</e:rc>

make an element of a record unique, compared to another xml file

Given two xml files, i need to compare if the combination of TITLEID, and ARTIST, is unique (meaning that in the second file there is not the same combination of TITLEID,ARTIST, existing in the first xml file)
<CATALOG>
<RECORD ID="109">
<TITLEID>54</TITLEID>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR> </CD>
</RECORD>
<RECORD ID="187">
<TITLEID>88</TITLEID>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR> </CD>
</RECORD>
</CATALOG>
second xml:
<CATALOG>
<RECORD ID="109">
<TITLEID>54</TITLEID>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR> </CD>
</RECORD>
<RECORD ID="187">
<TITLEID>text ',.</TITLEID>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR> </CD>
</RECORD>
</CATALOG>
So i need to do the following, only for those records that have the following condition true:
TITLEIDARTIST (from xml1) TITLEIDARTIST (from xml2)
i need to make the TITLEIDARTIST combination unique, in the first xml file, adding for instance a digit to the TITLEID (making sure though that this digit addition will not make a new number that will create a new combination that will match the second xml).
SO the desired output would be like (i only need to modify the first xml, and leave untouched the second):
<CATALOG>
<RECORD ID="109">
<TITLEID>540</TITLEID>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR> </CD>
</RECORD>
<RECORD ID="187">
<TITLEID>88</TITLEID>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR> </CD>
</RECORD>
</CATALOG>
Please note that in the second XML file, in the TITLEID, one may find text, not only digits.
With XSLT 3 you can use a composite key to identify the elements for which there are duplicates in the secondary document and then generate a new id based on generate-id():
<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:param name="doc2">
<CATALOG>
<RECORD ID="109">
<TITLEID>54</TITLEID>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</RECORD>
<RECORD ID="187">
<TITLEID>text ',.</TITLEID>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</RECORD>
</CATALOG>
</xsl:param>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:key name="ref" match="RECORD" composite="yes" use="TITLEID, ARTIST"/>
<xsl:template match="RECORD[key('ref', (TITLEID, ARTIST), $doc2)]/TITLEID">
<xsl:copy>{generate-id()}</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bnnZVZ/0
For compactness and self-containedness that example include the secondary document inline but of course you could use the parameter to pass in the document from the outside or change the code to use e.g. <xsl:param name="doc2" select="doc($doc2-uri)"/> with a second parameter to pass in the URI/location of the second document e.g. <xsl:param name="doc2-uri" as="xs:string">second-doc.xml</xsl:param>.

add an element to a node, if it doesnot exist, based on matching criteria

Using XSLT 3.0,
I have as input the following XML:
<?xml ="1.0" encoding="UTF-8"?>
<TABLE NAME="TABLE.DB">
<DATA RECORDS="2">
<RECORD ID="1">
<RECNO>1</RECNO>
<SEQ>0</SEQ>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<ORDER>10355</ORDER>
<CN>PL</CN>
<PROPERTY>06</PROPERTY>
</RECORD>
<RECORD ID="2">
<RECNO>2</RECNO>
<SEQUENCE>0</SEQUENCE>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<ORDER>000026672</ORDER>
<CN>PL 300 L</CN>
</RECORD>
<RECORD ID="3">
<RECNO>3</RECNO>
<SEQUENCE>0</SEQUENCE>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10357</NUMBER>
<CN>PL 300 L</CN>
<PROPERTY>0</PROPERTY>
</RECORD>
</DATA>
</TABLE>
given values used for matching:
(i use \t to define the tab separated nature of my input file)
"10355"\t"PL"
"000026672"\t"PL 300 L"
i need to insert to all records that do not already have a PROPERTY tag, with the value of 06
Desired result:
<?xml ="1.0" encoding="UTF-8"?>
<TABLE NAME="TABLE.DB">
<DATA RECORDS="2">
<RECORD ID="1">
<RECNO>1</RECNO>
<SEQ>0</SEQ>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<ORDER>10355</ORDER>
<CN>PL</CN>
<PROPERTY>06</PROPERTY>
</RECORD>
<RECORD ID="2">
<RECNO>2</RECNO>
<SEQUENCE>0</SEQUENCE>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<ORDER>000026672</ORDER>
<CN>PL 300 L</CN>
<PROPERTY>06</PROPERTY>
</RECORD>
<RECORD ID="3">
<RECNO>3</RECNO>
<SEQUENCE>0</SEQUENCE>
<DATE>17/12/1999 2:44:08 μμ</DATE>
<ID>12/11/2015 3:15:25 μμ</ID>
<NUMBER>10357</NUMBER>
<CN>PL 300 L</CN>
</RECORD>
</DATA>
</TABLE>
What i have tried, adds the element property, even if it is already there, so i end up with two elements PROPERTY, in the same node, if it already exists. Could you give me an example implementation, i use SAXON latest release (9.8)
xsl: which adds an element, even when one exists:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="xsl exsl xs">
<xsl:output method="xml" version="1.0" indent="yes" encoding="utf-8" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="//*[local-name() = 'RECORD ID']">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<xsl:choose>
<xsl:when test="not(PRODUCT)">
<PRODUCT><xsl:value-of select="98"/></PRODUCT>
</xsl:when>
<xsl:otherwise>
<xsl:copy><xsl:value-of select="98"/></xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Have been using the solution suggested with my real data, (which differ a lot from the example), and i face the following issue:
How could i also have a report that would let me know, which of the additions while they were to be done, according to the input file, were not inserted?
A few observations:
(a) in your sample output, the PROPERTY element has been deleted from record 3. I can't see anything in your description of requirements that explains why.
(b) in your requirements statement, the sentence "i need to insert to all records that do not already have a PROPERTY tag, with the value of 06" is ambiguous. I would read this as saying that if there is a PROPERTY 'tag' (correctly, element) with a value other than 06 then you should insert another PROPERTY element, but that seems to contradict what you say elsewhere.
(c) your code has a template rule with match="//*[local-name() = 'RECORD ID']". You can delete the "//" at the start of a match pattern, it's redundant. More importantly, no element will ever have a local name equal to "RECORD ID" - element names cannot include spaces. So the template rule will never match anything.
(d) assuming that this template rule was intended to match RECORD elements, you certainly don't want the xsl:copy inside the xsl:otherwise, as this will create a nested copy of the whole RECORD.
(e) you've asked for an XSLT 3.0 solution but there's nothing in your problem that requires XSLT 3.0, and in fact your own stylesheet says version="2.0".
(f) I can't see what role the tab-separated parameter file plays in any of this.
In short, there's an awful lot of clarification needed before anyone can start to write any code.
Your solution requires a few changes:
The template shoud match RECORD, not RECORD ID (ID is an
attribute which takes no part in any decision).
Your general concept is OK:
copy the starting tag (<RECORD>),
apply templated to the inside of the current RECORD,
check whether PROPERTY (not PRODUCT) element is absent (in the
current RECORD),
if it is (absent), then output PROPERTY element with the required value,
copy the closing tag (</RECORD>).
I removed the otherwise part (not needed) and changed choose to
a single if..
As you specified XSLT 3.0, even the identity template can be
replaced with (a bit shorter) on-no-match="shallow-copy".
I also added version to your source XML. Otherwise it is not well-formed.
So the whole script can look like below:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="RECORD">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<xsl:if test="not(PROPERTY)">
<PROPERTY>06</PROPERTY>
</xsl:if>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Tested on http://xsltfiddle.liberty-development.net/

Replace new line char with <br /> XSL

I have a Sharepoint list which I want to convert to a JSON via an XSL dataview.
I have an XSL recursive replace function which I use to replace all special characters (escape backslash, double quotes to " etc) which gives me nice clean JSON which parses correctly in the users browser.
The final thing that I need to escape / replace is the new line char. The new line causes errors in parsing the JSON in some browsers.
Here is some xsl which tests if the contents of title has a new line char, if it does we output a paragraph:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="catalog/cd">
<xsl:if test='contains(title,"
")'>
<p>Found <xsl:value-of select="title" /></p>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Here is some sample xml:
<catalog>
<cd>
<title>Empire
Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<country>USA</country>
<company>RCA</company>
<price>9.90</price>
<year>1982</year>
</cd>
</catalog>
"Empire Burlesque" should be the only item to pass the test, but all three titles pass the if statement and are outputted.
EDIT
Modifying the solution below, I assume this should work if I wanted to do the search and replace on a individual node basis? I won't be able to test it in Sharepoint until tomorrow.
<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="/">
<xsl:for-each select="catalog/cd">
<xsl:variable name="title_clean">
<xsl:call-template name="repNL">
<xsl:with-param name="pText" select="title"/>
</xsl:call-template>
</xsl:variable>
<p><xsl:value-of select='$title_clean' /></p>
</xsl:for-each>
</xsl:template>
<xsl:template name="repNL">
<xsl:param name="pText" select="."/>
<xsl:copy-of select="substring-before(concat($pText,'
'),'
')"/>
<xsl:if test="contains($pText, '
')">
<br />
<xsl:call-template name="repNL">
<xsl:with-param name="pText" select=
"substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
"Empire Burlesque" should be the only
item to pass the test, but all three
titles pass the if statement and are
outputted.
Cannot reproduce the alleged problem -- tested with 9 different XSLT processors, including all from Microsoft.
Anyway:
This transformation replaces any NL character in a text node with a br element:
<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="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()" name="repNL">
<xsl:param name="pText" select="."/>
<xsl:copy-of select=
"substring-before(concat($pText,'
'),'
')"/>
<xsl:if test="contains($pText, '
')">
<br />
<xsl:call-template name="repNL">
<xsl:with-param name="pText" select=
"substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document (the provided one with an added cd to make it more interesting):
<catalog>
<cd>
<title>Line1
Line2
Line3
</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Empire
Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<country>USA</country>
<company>RCA</company>
<price>9.90</price>
<year>1982</year>
</cd>
</catalog>
the wanted, correct result is produced:
<catalog>
<cd>
<title>Line1<br/> Line2<br/> Line3<br/>
</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Empire<br/> Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<country>USA</country>
<company>RCA</company>
<price>9.90</price>
<year>1982</year>
</cd>
</catalog>
Explanation:
The identity rule/template copies every node "as-is".
The overriding template that matches any text node (also named as "repNL") prforms the following processing:
Using a sentinel (a NL character appended to the string), the substring before the first NL character (or the complete string, if th no NL character is contained) is copied to the output.
If a NL character is really contained, then a br element is generated and the template calls itself recursively for the remaining string after this NL character.
If you are using XslCompiledTransform(C#), I believe you will encounter the issue if you use below API to transform the XML:
XslCompiledTransform.Transform(XmlReader input, XmlWriter results)
However, below API works well:
XslCompiledTransform.Transform(string, string);
This is wired, but I don't figure out why...

Resources