I'm working in a database that doesn't really support subelements. To get around this, we've been using squiggly brackets }} in a string of values to indicate separations between subelements. When we export to xml, it looks something like this:
<geographicSubject>
<data>Mexico }} tgn }} 123456</data>
<data>Mexico City }} tgn }} 7891011</data>
<data>Main Street }} tgn }} 654321</data>
</geographicSubject>
My question: how do I create our XSLT so that it splits the strings in <data> into separate uniquely named subelements like this:
<data>
<location>Mexico</location>
<source>tgn</source>
<id>123456</id>
</data>
The first }} indicates the start of "source", the second }} indicates the start of "id". Thanks to anyone willing to help!
Compose a tokenizer:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:e="http://localhost">
<e:e>location</e:e>
<e:e>source</e:e>
<e:e>id</e:e>
<xsl:variable name="vElement" select="document('')/*/e:*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="data/text()" name="tokenizer">
<xsl:param name="pString" select="string()"/>
<xsl:param name="pPosition" select="1"/>
<xsl:if test="$pString">
<xsl:element name="{$vElement[$pPosition]}">
<xsl:value-of
select="normalize-space(
substring-before(concat($pString,'}}'),'}}')
)"/>
</xsl:element>
<xsl:call-template name="tokenizer">
<xsl:with-param name="pString"
select="substring-after($pString,'}}')"/>
<xsl:with-param name="pPosition" select="$pPosition + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output:
<geographicSubject>
<data>
<location>Mexico</location>
<source>tgn</source>
<id>123456</id>
</data>
<data>
<location>Mexico City</location>
<source>tgn</source>
<id>7891011</id>
</data>
<data>
<location>Main Street</location>
<source>tgn</source>
<id>654321</id>
</data>
</geographicSubject>
Related
Please suggest for how to select the range's between IDs. Example if range is 5-8, then 6,7 are required ids. If figs <link href="fig3">-<link href="fig7">, then fig4 fig5 fig6 are required IDs.
XML:
<root>
<p id="p1">This <link href="#fig-0001 #fig-0002"/>, <link href="#fig-0003"/>-<link href="#fig-0006"/></p>
<figure xml_id="fig-0001"><label>Fig. 1</label><caption><p>One</p></caption></figure>
<figure xml_id="fig-0002"><label>Fig. 2</label><caption><p>Two</p></caption></figure>
<figure xml_id="fig-0003"><label>Fig. 3</label><caption><p>Three</p></caption></figure>
<figure xml_id="fig-0004"><label>Fig. 4</label><caption><p>Four</p></caption></figure>
<figure xml_id="fig-0005"><label>Fig. 5</label><caption><p>Five</p></caption></figure>
<figure xml_id="fig-0006"><label>Fig. 6</label><caption><p>Six</p></caption></figure>
</root>
XSLT2:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*">
<xsl:copy><xsl:apply-templates select="node()|#*"/></xsl:copy>
</xsl:template>
<xsl:key name="kFloat" match="*" use="#xml_id"/>
<xsl:template match="link[ancestor::p/following-sibling::*[1][matches(name(), '^(figure)$')]][matches(key('kFloat', if(contains(#href, ' ')) then substring-after(substring-before(#href, ' '), '#') else substring-after(#href, '#'))/name(), '^(figure)$')]">
<xsl:copy><xsl:apply-templates select="node()|#*"/></xsl:copy><!-- link element retaining-->
<!--Range between IDs selection -->
<xsl:if test="matches(preceding-sibling::node()[1][self::text()], '^(–|–|–|-)$')">
<xsl:variable name="varRangeFirst" select="substring-after(preceding-sibling::node()[2][name()='link']/#href, '#')"/>
<xsl:variable name="varRangeLast" select="substring-after(#href, '#')"/>
<xsl:variable name="varRangeBetweenIDs1">
<!--xsl:value-of select="for $i in key('kFloat', $varRangeLast)/preceding-sibling::figure return $i/#xml_id"/--><!-- this will select all preceding figures, but it should between 3 and 6 -->
<xsl:value-of select="for $i in key('kFloat', $varRangeLast)/preceding-sibling::figure[for $k in preceding-sibling::figure return contains($k/#xml_id, $varRangeFirst)] return $i/#xml_id"/><!-- here getting error--><!-- please suggest to select range's between IDs from this -->
<!--xsl:if test="matches(key('kFloat', $varRangeLast)/name(), '^(figure)$')">
<xsl:for-each select="key('kFloat', $varRangeLast)/preceding-sibling::figure">
<a><xsl:value-of select="#xml_id"/></a>
</xsl:for-each>
</xsl:if-->
</xsl:variable>
<xsl:for-each select="$varRangeBetweenIDs1/a">
<xsl:variable name="var2"><xsl:value-of select="preceding-sibling::a"/></xsl:variable>
<xsl:if test="contains($var2, $varRangeFirst)">
<xsl:element name="float"><xsl:attribute name="id" select="."/></xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:if>
<xsl:for-each select="tokenize(#href, ' ')"><!--for each link's individual hrefs will have respective float element -->
<float id="{substring-after(., '#')}"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Required Result:
<root>
<p id="p1">This <link href="#fig-0001 #fig-0002"/><float id="fig-0001"/><float id="fig-0002"/>, <link href="#fig-0003"/><float id="fig-0003"/>-<link href="#fig-0006"/><float id="fig-0004"/><float id="fig-0005"/><float id="fig-0006"/></p>
<figure xml_id="fig-0001"><label>Fig. 1</label><caption><p>One</p></caption></figure>
<figure xml_id="fig-0002"><label>Fig. 2</label><caption><p>Two</p></caption></figure>
<figure xml_id="fig-0003"><label>Fig. 3</label><caption><p>Three</p></caption></figure>
<figure xml_id="fig-0004"><label>Fig. 4</label><caption><p>Four</p></caption></figure>
<figure xml_id="fig-0005"><label>Fig. 5</label><caption><p>Five</p></caption></figure>
<figure xml_id="fig-0006"><label>Fig. 6</label><caption><p>Six</p></caption></figure>
</root>
I couldn't quite work out your logic from your current XSLT, so I would consider a different approach, using templates to match the various types of link element you required. Specifically, have separate ones for link elements that precede or follow a text node with a hyphen in.
Try this XSLT. This makes use of the intersect function to get the range of elements you require in the case of 003-006.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kFloat" match="figure" use="#xml_id"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy><xsl:apply-templates select="node()|#*"/></xsl:copy>
</xsl:template>
<xsl:template match="p/link[following-sibling::node()[1][self::text()][. = '-']][following-sibling::node()[2][self::link]]" priority="3">
<xsl:call-template name="identity" />
<xsl:apply-templates select="key('kFloat', substring-after(#href, '#'))" mode="float" />
</xsl:template>
<xsl:template match="p/link[preceding-sibling::node()[1][self::text()][. = '-']][preceding-sibling::node()[2][self::link]]" priority="2">
<xsl:call-template name="identity" />
<xsl:variable name="firstLink" select="preceding-sibling::node()[2]" />
<xsl:apply-templates select="key('kFloat', substring-after($firstLink/#href, '#'))/following-sibling::figure intersect key('kFloat', substring-after(#href, '#'))/preceding-sibling::figure" mode="float" />
<xsl:apply-templates select="key('kFloat', substring-after(#href, '#'))" mode="float" />
</xsl:template>
<xsl:template match="p/link[#href]">
<xsl:next-match />
<xsl:variable name="doc" select="/" />
<xsl:for-each select="for $ref in tokenize(#href, ' ') return substring-after($ref, '#')">
<xsl:apply-templates select="key('kFloat', ., $doc)" mode="float" />
</xsl:for-each>
</xsl:template>
<xsl:template match="figure" mode="float">
<float id="{#xml_id}"/>
</xsl:template>
</xsl:stylesheet>
See it in action at http://xsltfiddle.liberty-development.net/ncdD7nu
With the help from Tim Sir's suggestion, I modified my answer as below.
<xsl:value-of select="for $i in key('kFloat', $varRangeLast)/preceding-sibling::figure
[some $k in preceding-sibling::figure satisfies
contains($k/#xml_id, $varRangeFirst)] return $i/#xml_id"/>
XSLT2:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*">
<xsl:copy><xsl:apply-templates select="node()|#*"/></xsl:copy>
</xsl:template>
<xsl:key name="kFloat" match="*" use="#xml_id"/>
<xsl:template match="link[ancestor::p/following-sibling::*[1][matches(name(), '^(figure)$')]][matches(key('kFloat', if(contains(#href, ' ')) then substring-after(substring-before(#href, ' '), '#') else substring-after(#href, '#'))/name(), '^(figure)$')]">
<xsl:copy><xsl:apply-templates select="node()|#*"/></xsl:copy><!-- link element retaining-->
<!--Range between IDs selection -->
<xsl:if test="matches(preceding-sibling::node()[1][self::text()], '^(–|–|–|-)$')">
<xsl:variable name="varRangeFirst" select="substring-after(preceding-sibling::node()[2][name()='link']/#href, '#')"/>
<xsl:variable name="varRangeLast" select="substring-after(#href, '#')"/>
<xsl:variable name="varRangeBetweenIDs1">
<xsl:value-of select="for $i in key('kFloat', $varRangeLast)/preceding-sibling::figure[some $k in preceding-sibling::figure satisfies contains($k/#xml_id, $varRangeFirst)] return $i/#xml_id"/>
</xsl:variable>
<xsl:for-each select="tokenize($varRangeBetweenIDs1, ' ')">
<xsl:element name="float"><xsl:attribute name="id" select="."/></xsl:element>
</xsl:for-each>
</xsl:if>
<xsl:for-each select="tokenize(#href, ' ')"><!--for each link's individual hrefs will have respective float element -->
<float id="{substring-after(., '#')}"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I have an xml. I am transforming it using xsl stylesheet and showing in html page using java. My requirement is , I need to compare two node values and if there is a difference, I need to highlight the changed character value. How can this be done ?
XML :
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Message>
<DiffDetailMessage>
<TestCaseID>000001</TestCaseID>
<res1>VI7002 1D</res1>
<res2>VI7002 DD </res2>
</DiffDetailMessage>
<DiffDetailMessage>
<TestCaseID>000002</TestCaseID>
<res1>BS7002 1D</res1>
<res2>BS7002 SS </res2>
</DiffDetailMessage>
</Message>
XSL :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>Report</h2>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="Message/DiffDetailMessage">
<table border="2">
<xsl:apply-templates select="TestCaseID"/>
<xsl:apply-templates select="res1"/>
<xsl:apply-templates select="res2"/>
</table>
</xsl:template>
<xsl:template match="TestCaseID">
<tr><td><b>Test CaseID </b></td>
<td><xsl:value-of select="."/></td></tr>
</xsl:template>
<xsl:template match="res1">
<tr><td><b>Res1</b></td>
<td><xsl:value-of select="."/> </td> </tr>
</xsl:template>
<xsl:template match="res2">
<tr><td><b>Res2</b></td>
<td><xsl:value-of select="."/></td> </tr>
</xsl:template>
</xsl:stylesheet>
How can I compare node values res1 with res2 ? In my case, value of "VI70002 ID" should be compared against "VI7002 DD" and since there is a change, I should highlight "D" character in html page using xsl. Can someone help on this regard.
If you are using only XSLT-1.0, you can use a recursive named template to iterate over the string char-by-char: the following template takes two strings as input and puts a bold emphasis on each different char. If the second string is longer than the first one, these trailing chars are highlighted, too.
<xsl:template name="cmp">
<xsl:param name="str1" />
<xsl:param name="str2" />
<xsl:choose>
<xsl:when test="substring($str1,1,1) = substring($str2,1,1)">
<xsl:value-of select="substring($str2,1,1)"/>
</xsl:when>
<xsl:when test="substring($str1,1,1) != substring($str2,1,1)">
<b><xsl:value-of select="substring($str2,1,1)"/></b>
</xsl:when>
<xsl:when test="$str1 = '' and substring($str2,1,1)">
<b><xsl:value-of select="substring($str2,1,1)"/></b>
</xsl:when>
</xsl:choose>
<xsl:if test="string-length($str1) > 0 or string-length($str2) > 0">
<xsl:call-template name="cmp">
<xsl:with-param name="str1" select="substring($str1,2)" />
<xsl:with-param name="str2" select="substring($str2,2)" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Call this template from one of the other templates to get a (partially) highlighted "string", e.g.
<xsl:template match="res2">
<tr><td><b>Res2</b></td>
<td>
<xsl:call-template name="cmp">
<xsl:with-param name="str1" select="../res1" />
<xsl:with-param name="str2" select="." />
</xsl:call-template>
</td> </tr>
</xsl:template>
I have a specific problem. I have to transform an XML structure to other, where the base XSD is same, different only the namespace definition. The first part is simple, because here I have to use field-mapping. The second part is the simple copy. And here is the problem. The copied "main" node contains the original xmlns attribute. I need to remove this "attribute".
The base xml:
<?xml version="1.0" encoding="UTF-8"?>
<S2SCTIcf:SCTIcfBlkCredTrf xmlns:S2SCTIcf="urn:S2SCTIcf:xsd:$SCTIcfBlkCredTrf"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:S2SCTIcf:xsd:$SCTIcfBlkCredTrf SCTIcfBlkCredTrf.xsd">
<S2SCTIcf:SndgInst>XXXXXXX0</S2SCTIcf:SndgInst>
<S2SCTIcf:RcvgInst>YYYYYYY0</S2SCTIcf:RcvgInst>
<S2SCTIcf:FileRef>2013111200800546</S2SCTIcf:FileRef>
<S2SCTIcf:SrvcId>SCT</S2SCTIcf:SrvcId>
<S2SCTIcf:TstCode>T</S2SCTIcf:TstCode>
<S2SCTIcf:FType>ICF</S2SCTIcf:FType>
<S2SCTIcf:FDtTm>2013-11-12T16:26:31</S2SCTIcf:FDtTm>
<S2SCTIcf:NumCTBlk>1</S2SCTIcf:NumCTBlk>
<S2SCTIcf:NumPCRBlk>0</S2SCTIcf:NumPCRBlk>
<S2SCTIcf:NumRFRBlk>0</S2SCTIcf:NumRFRBlk>
<S2SCTIcf:NumROIBlk>0</S2SCTIcf:NumROIBlk>
<S2SCTIcf:FIToFICstmrCdtTrf xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02">
<GrpHdr>
<MsgId>xxddccxxaaa</MsgId>
<CreDtTm>2013-11-12T16:26:31</CreDtTm>
<NbOfTxs>1</NbOfTxs>
<TtlIntrBkSttlmAmt Ccy="EUR">469.12</TtlIntrBkSttlmAmt>
<IntrBkSttlmDt>2013-11-13</IntrBkSttlmDt>
<SttlmInf>
<SttlmMtd>CLRG</SttlmMtd>
<ClrSys>
<Prtry>ST2</Prtry>
</ClrSys>
</SttlmInf>
<InstgAgt>
<FinInstnId>
<BIC>XXXXXXX0</BIC>
</FinInstnId>
</InstgAgt>
</GrpHdr>
<CdtTrfTxInf>
<PmtId>
<EndToEndId>1114405599,1114382976</EndToEndId>
<TxId>F3232323232</TxId>
</PmtId>
<PmtTpInf>
<SvcLvl>
<Cd>SEPA</Cd>
</SvcLvl>
</PmtTpInf>
<IntrBkSttlmAmt Ccy="EUR">469.12</IntrBkSttlmAmt>
<ChrgBr>SLEV</ChrgBr>
<Dbtr>
<Nm>ddffrrddsaasas</Nm>
<PstlAdr>
<Ctry>HU</Ctry>
<AdrLine>dssdsdsdsdsdaas</AdrLine>
</PstlAdr>
</Dbtr>
<DbtrAcct>
<Id>
<IBAN>HU26XXXXXXXXXXXXXX</IBAN>
</Id>
</DbtrAcct>
<DbtrAgt>
<FinInstnId>
<BIC>CCCCHUH0</BIC>
</FinInstnId>
</DbtrAgt>
<CdtrAgt>
<FinInstnId>
<BIC>CVCVCVCVCVC</BIC>
</FinInstnId>
</CdtrAgt>
<Cdtr>
<Nm>XXXXX</Nm>
<PstlAdr>
<Ctry>DE</Ctry>
</PstlAdr>
</Cdtr>
<CdtrAcct>
<Id>
<IBAN>DE12vvvvvvvhghhg</IBAN>
</Id>
</CdtrAcct>
<RmtInf>
<Ustrd>0000000000,0000000000 </Ustrd>
</RmtInf>
</CdtTrfTxInf>
</S2SCTIcf:FIToFICstmrCdtTrf>
</S2SCTIcf:SCTIcfBlkCredTrf>
The xslt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns0="urn:S2SCTIcf:xsd:$SCTIcfBlkCredTrf"
xmlns:sw8="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02"
xmlns:S2SCTScf="urn:S2SCTScf:xsd:$SCTScfBlkCredTrf" exclude-result-prefixes="xs ns0 ">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="var1_SCTIcfBlkCredTrf" select="ns0:SCTIcfBlkCredTrf"/>
<S2SCTScf:SCTScfBlkCredTrf
xsi:schemaLocation="urn:S2SCTScf:xsd:$SCTScfBlkCredTrf SCTScfBlkCredTrf.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:S2SCTScf="urn:S2SCTScf:xsd:$SCTScfBlkCredTrf">
<xsl:for-each select="$var1_SCTIcfBlkCredTrf">
<S2SCTScf:SndgInst>
<xsl:value-of select="string(ns0:RcvgInst)"/>
</S2SCTScf:SndgInst>
</xsl:for-each>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf">
<S2SCTScf:RcvgInst>
<xsl:value-of select="string(ns0:SndgInst)"/>
</S2SCTScf:RcvgInst>
</xsl:for-each>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf">
<S2SCTScf:SrvcId>
<xsl:value-of select="string(ns0:SrvcId)"/>
</S2SCTScf:SrvcId>
</xsl:for-each>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf">
<S2SCTScf:TstCode>
<xsl:value-of select="string(ns0:TstCode)"/>
</S2SCTScf:TstCode>
</xsl:for-each>
<S2SCTScf:FType>SCF</S2SCTScf:FType>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf">
<S2SCTScf:FileRef>
<xsl:value-of select="string(ns0:FileRef)"/>
</S2SCTScf:FileRef>
</xsl:for-each>
<S2SCTScf:RoutingInd>DIR</S2SCTScf:RoutingInd>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf">
<S2SCTScf:FileBusDt>
<xsl:value-of select="string(ns0:FDtTm)"/>
</S2SCTScf:FileBusDt>
</xsl:for-each>
<S2SCTScf:FIToFICstmrCdtTrf xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02">
<xsl:for-each select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf">
<GrpHdr>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf">
<MsgId>
<xsl:value-of select="string(sw8:GrpHdr/sw8:MsgId)"/>
</MsgId>
</xsl:for-each>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf">
<CreDtTm>
<xsl:value-of select="string(sw8:GrpHdr/sw8:CreDtTm)"/>
</CreDtTm>
</xsl:for-each>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf">
<NbOfTxs>
<xsl:value-of select="string(sw8:GrpHdr/sw8:NbOfTxs)"/>
</NbOfTxs>
</xsl:for-each>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf">
<xsl:variable name="var2_TtlIntrBkSttlmAmt"
select="sw8:GrpHdr/sw8:TtlIntrBkSttlmAmt"/>
<TtlIntrBkSttlmAmt>
<xsl:attribute name="Ccy" namespace="">
<xsl:value-of select="string($var2_TtlIntrBkSttlmAmt/#Ccy)"/>
</xsl:attribute>
<xsl:value-of
select="string(number(string($var2_TtlIntrBkSttlmAmt)))"/>
</TtlIntrBkSttlmAmt>
</xsl:for-each>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf">
<IntrBkSttlmDt>
<xsl:value-of select="string(sw8:GrpHdr/sw8:IntrBkSttlmDt)"/>
</IntrBkSttlmDt>
</xsl:for-each>
<SttlmInf>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf">
<SttlmMtd>
<xsl:value-of
select="string(sw8:GrpHdr/sw8:SttlmInf/sw8:SttlmMtd)"/>
</SttlmMtd>
</xsl:for-each>
<ClrSys>
<xsl:for-each select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf">
<Prtry>
<xsl:value-of
select="string(sw8:GrpHdr/sw8:SttlmInf/sw8:ClrSys/sw8:Prtry)"
/>
</Prtry>
</xsl:for-each>
</ClrSys>
</SttlmInf>
<InstdAgt>
<FinInstnId>
<xsl:for-each
select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf/sw8:GrpHdr/sw8:InstgAgt">
<BIC>
<xsl:value-of select="string(sw8:FinInstnId/sw8:BIC)"/>
</BIC>
</xsl:for-each>
</FinInstnId>
</InstdAgt>
</GrpHdr>
<xsl:copy-of select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf/sw8:CdtTrfTxInf" />
</xsl:for-each>
</S2SCTScf:FIToFICstmrCdtTrf>
</S2SCTScf:SCTScfBlkCredTrf>
</xsl:template>
</xsl:stylesheet>
The wrong part of output:
...
</InstdAgt>
</GrpHdr>
<CdtTrfTxInf xmlns:S2SCTIcf="urn:S2SCTIcf:xsd:$SCTIcfBlkCredTrf">
<PmtId>
<EndToEndId>1114405599,1114382976</EndToEndId>
<TxId>F3232323232</TxId>
...
I don't want to get the xmlns:S2SCTIcf="urn:S2SCTIcf:xsd:$SCTIcfBlkCredTrf" attribute this line.
Have someone any idea?
Thank you!
Feri
Your issue is that
<xsl:copy-of select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf/sw8:CdtTrfTxInf" />
copies the node from the original tree including its "namespace nodes", i.e. the namespace declarations that were in scope at that point in the original document. When this node is serialized any of these namespace nodes that are not already in force at this point in the output document will be declared by the serializer.
If you were able to use XSLT 2.0 then you could try setting copy-namespaces="no" on the copy-of but that isn't an option in XSLT 1.0. So instead of using copy-of you need to use templates to copy that node (and all its descendants recursively) without including the namespace nodes. The simplest way I can think of to do this is to declare two additional templates
<xsl:template match="*" mode="copy">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="#*|node()" mode="copy" />
</xsl:element>
</xsl:template>
<xsl:template match="#*|text()|comment()" mode="copy">
<xsl:copy/>
</xsl:template>
and then replace that copy-of with
<xsl:apply-templates mode="copy"
select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf/sw8:CdtTrfTxInf" />
The trick here is that xsl:element is creating a new element node that happens to have the same name and namespace as the original one, rather than copying the original node, so it doesn't copy the namespace nodes.
You can use a variant of the answer here to get what you want.
Basically, you would create a template to rebuild that element without any namespaces. So you would add the following two templates to your current XSLT:
<xsl:template match="*" mode="copy-no-namespaces">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="node()" mode="copy-no-namespaces"/>
</xsl:element>
</xsl:template>
<xsl:template match="comment()| processing-instruction()" mode="copy-no-namespaces">
<xsl:copy/>
</xsl:template>
And then update your copy-of to
<xsl:apply-templates select="$var1_SCTIcfBlkCredTrf/ns0:FIToFICstmrCdtTrf/sw8:CdtTrfTxInf" mode="copy-no-namespaces"/>
This is my first question here.
I want to transform this xml using XSL 1.0 :
<RESULTS>
<RES>
<GROUP>1</GROUP>
<SUBGROUP>A</SUBGROUP>
<NAME>Alice</NAME>
</RES>
<RES>
<GROUP>1</GROUP>
<SUBGROUP>A</SUBGROUP>
<NAME>Bart</NAME>
</RES>
<RES>
<GROUP>1</GROUP>
<SUBGROUP>B</SUBGROUP>
<NAME>Keira</NAME>
</RES>
<RES>
<GROUP>2</GROUP>
<SUBGROUP>A</SUBGROUP>
<NAME>Mike</NAME>
</RES>
<RES>
<GROUP>2</GROUP>
<SUBGROUP>B</SUBGROUP>
<NAME>Peter</NAME>
</RES>
<RES>
<GROUP>2</GROUP>
<SUBGROUP>B</SUBGROUP>
<NAME>Olaf</NAME>
</RES>
</RESULTS>
Into this:
<h1> 1 </h1>
<h2>A</h2>
<p>Alice</p>
<p>Bart</p>
<h2>B</h2>
<p>Keira</p>
<h1> 2 </h1>
<h2>A</h2>
<p>Mike</p>
<h2>B</h2>
<p>Peter</p>
<p>Olaf</p>
I already tried using Muenchian Method, however this only allowed me to sort by GROUP, and I could not sort the sorted results by SUBGROUP. Note that I have to view the header only once per group/subgroup.
# C. M. Sperberg-McQueen
I did not want to post a wall of text, but if it might help I do it:
This is one of the solutions I have tried:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="byGROUP" match="RESULTS/RES" use="GROUP" />
<xsl:template match="RESULTS">
<xsl:for-each select="RES[count(. | key('byGROUP', GROUP)[1]) = 1]">
<xsl:sort select="GROUP" order="descending" />
<h1>
<xsl:value-of select="GROUP" />
</h1>
<xsl:for-each select="key('byGROUP', GROUP)">
<xsl:sort select="SUBGROUP" order="descending" />
<h2>
<xsl:value-of select="SUBGROUP" />
</h2>
<p>
<xsl:value-of select="NAME" />
</p>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I tried using preceiding-sibling to test whether to view the SUBGROUP but I found it imposible to iterate through the nodes, so perhaps it is not a good approach.
The typical way to do multiple groupings is to use the concatenation of the current level's value with all of the parent values as the key value:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="byGROUP" match="RESULTS/RES" use="GROUP" />
<xsl:key name="bySUBGROUP" match="RESULTS/RES"
use="concat(GROUP, '+', SUBGROUP)" />
<xsl:template match="RESULTS">
<xsl:apply-templates
select="RES[count(. | key('byGROUP', GROUP)[1]) = 1]
/GROUP">
<xsl:sort select="." order="ascending" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="GROUP">
<h1>
<xsl:value-of select="." />
</h1>
<xsl:variable name="thisGroup" select="key('byGROUP', .)" />
<xsl:apply-templates
select="$thisGroup[count(. |
key('bySUBGROUP', concat(GROUP, '+', SUBGROUP))[1])
= 1]
/SUBGROUP">
<xsl:sort select="." order="ascending" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="SUBGROUP">
<h2>
<xsl:value-of select="." />
</h2>
<xsl:apply-templates select="key('bySUBGROUP', concat(../GROUP, '+', .))"/>
</xsl:template>
<xsl:template match="RES">
<p>
<xsl:value-of select="NAME" />
</p>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, this produces:
<h1>1</h1>
<h2>A</h2>
<p>Alice</p>
<p>Bart</p>
<h2>B</h2>
<p>Keira</p>
<h1>2</h1>
<h2>A</h2>
<p>Mike</p>
<h2>B</h2>
<p>Peter</p>
<p>Olaf</p>
I am struggling with xslt from the past 2 days, owing to my starter status.My requirement is that given any input XML file ,I want the output to be a list of all the XPaths of all the tags in order in which they appear in the original XML document(parent, then parent,parents Attributes list/child, parent/child/childOFchild and so forth). THe XSLT should not be specific to any single XMl schema. It should work for any XML file, which is a valid one.
Ex:
If the Input XML Is :
<v1:Root>
<v1:UserID>test</v1:UserID>
<v1:Destination>test</v1:Destination>
<v1:entity name="entiTyName">
<v11:attribute name="entiTyName"/>
<v11:attribute name="entiTyName"/>
<v11:attribute name="entiTyName"/>
<v11:filter type="entiTyName">
<v11:condition attribute="entiTyName" operator="eq" value="{FB8D669E-D090-E011-8F43-0050568E222C}"/>
<v11:condition attribute="entiTyName" operator="eq" value="1"/>
</v11:filter>
<v11:filter type="or">
<v11:filter type="or">
<v11:filter type="and">
<v11:filter type="and">
<v11:condition attribute="cir_customerissuecode" operator="not-like" value="03%"/>
</v11:filter>
</v11:filter>
</v11:filter>
</v11:filter>
</v1:entity>
</v1:Root>
I want my output to be :
/v1:Root/v1:UserID
/v1:Root/v1:Destination
/v1:Root/v1:entity/#name
/v1:Root/v1:entity/v11:attribute
/v1:Root/v1:entity/v11:attribute/#name
/v1:Root/v1:entity/v11:attribute[2]
/v1:Root/v1:entity/v11:attribute[2]/#name
/v1:Root/v1:entity/v11:attribute[3]
/v1:Root/v1:entity/v11:attribute[3]/#name
/v1:Root/v1:entity/v11:filter/#type
/v1:Root/v1:entity/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter/v11:condition/#attribute
/v1:Root/v1:entity/v11:filter/v11:condition/#operator
/v1:Root/v1:entity/v11:filter/v11:condition/#value
/v1:Root/v1:entity/v11:filter/v11:condition[2]
/v1:Root/v1:entity/v11:filter/v11:condition[2]/#attribute
/v1:Root/v1:entity/v11:filter/v11:condition[2]/#operator
/v1:Root/v1:entity/v11:filter/v11:condition[2]/#value
/v1:Root/v1:entity/v11:filter[2]/v11:filter/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition/#attribute
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition/#operator
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition/#value
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition/#attribute
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition/#operator
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition/#value
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]/#attribute
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]/#operator
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]/#value
So, it is basically all the XPath of each element ,then the Xpath of the elements Attributes.
I have an XSLT with me, which is like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" />
<xsl:template match="*[not(child::*)]">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="concat('/', name())" />
<xsl:if test="count(preceding-sibling::*[name() = name(current())]) != 0">
<xsl:value-of
select="concat('[', count(preceding-sibling::*[name() = name(current())]) + 1, ']')" />
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="*" />
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="*" />
</xsl:template>
</xsl:stylesheet>
THe output which gets Produced does not cater to complex tags and also the tag's attributes in the resulting Xpath list :(.
Kindly help me in fixing this xslt to produce the output as mentioned above.
THe present output from the above XSLT is like this :
/v1:Root/v1:UserID
/v1:Root/v1:Destination
/v1:Root/v1:entity/v11:attribute
/v1:Root/v1:entity/v11:attribute[2]
/v1:Root/v1:entity/v11:attribute[3]
/v1:Root/v1:entity/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter[2]/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter[2]/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter[2]/v11:condition[3]
I think there's a discrepancy between your sample input and output, in that the output describes a filter element with two conditions that's not in the source XML. At any rate, I believe this works:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" />
<!-- Handle attributes -->
<xsl:template match="#*">
<xsl:apply-templates select="ancestor-or-self::*" mode="buildPath" />
<xsl:value-of select="concat('/#', name())"/>
<xsl:text>
</xsl:text>
</xsl:template>
<!-- Handle non-leaf elements (just pass processing downwards) -->
<xsl:template match="*[#* and *]">
<xsl:apply-templates select="#* | *" />
</xsl:template>
<!-- Handle leaf elements -->
<xsl:template match="*[not(*)]">
<xsl:apply-templates select="ancestor-or-self::*" mode="buildPath" />
<xsl:text>
</xsl:text>
<xsl:apply-templates select="#*" />
</xsl:template>
<!-- Outputs a path segment for the matched element: '/' + name() + [ordinalPredicate > 1] -->
<xsl:template match="*" mode="buildPath">
<xsl:value-of select="concat('/', name())" />
<xsl:variable name="sameNameSiblings" select="preceding-sibling::*[name() = name(current())]" />
<xsl:if test="$sameNameSiblings">
<xsl:value-of select="concat('[', count($sameNameSiblings) + 1, ']')" />
</xsl:if>
</xsl:template>
<!-- Ignore text -->
<xsl:template match="text()" />
</xsl:stylesheet>