xslt 1.0 add (conditional) tag only if previous tag is different - xslt

I have the following source document;
<instance class-name="Deelnemer">
<attr attr-name="achternaam">
<value>McFly</value>
</attr>
<attr attr-name="geslacht">
<value>MAN</value>
</attr>
<attr attr-name="geboortedatum">
<value>1968-03-14</value>
</attr>
<attr attr-name="verzorgers">
<value>{"uuid":"f5a8fecd-7ea6-499e-a8a6-c6eb20e582b8","volgnummer":1}</value>
</attr>
<attr attr-name="verzorgers">
<value>{"uuid":"54748d2b-b2bf-4362-8520-4c4827a6660c","volgnummer":2}</value>
</attr>
</instance>
I transform it using this xslt (1.0);
<xsl:for-each select="./attr[#attr-name]">
<xsl:if test="string-length(.)"/>
<xsl:variable name="name" select="./#attr-name"/>
<xsl:variable name="value" select="normalize-space(./value)"/>
<modify-attr>
<xsl:attribute name="attr-name">
<xsl:value-of select="$name"/>
</xsl:attribute>
<remove-all-values/>
<add-value>
<value>
<xsl:value-of select="$value"/>
</value>
</add-value>
</modify-attr>
</xsl:for-each>
Which currently results in;
<modify-attr attr-name="geboortedatum">
<remove-all-values/>
<add-value>
<value>1968-03-14</value>
</add-value>
</modify-attr>
<modify-attr attr-name="verzorgers">
<remove-all-values/>
<add-value>
<value>{"uuid":"f5a8fecd-7ea6-499e-a8a6-c6eb20e582b8","volgnummer":1}</value>
</add-value>
</modify-attr>
<modify-attr attr-name="verzorgers">
<remove-all-values/>
<add-value>
<value>{"uuid":"54748d2b-b2bf-4362-8520-4c4827a6660c","volgnummer":2}</value>
</add-value>
</modify-attr>
But i want it to transform conditional if the previous tag (attribute) is the same as the current one.
<modify-attr attr-name="geboortedatum">
<remove-all-values/>
<add-value>
<value>1968-03-14</value>
</add-value>
</modify-attr>
<modify-attr attr-name="verzorgers">
<remove-all-values/>
<add-value>
<value>{"uuid":"f5a8fecd-7ea6-499e-a8a6-c6eb20e582b8","volgnummer":1}</value>
</add-value>
</modify-attr>
<modify-attr attr-name="verzorgers">
<add-value>
<value>{"uuid":"54748d2b-b2bf-4362-8520-4c4827a6660c","volgnummer":2}</value>
</add-value>
</modify-attr>
As seen by the "verzorgers" attr-name it appears twice but i only want a <remove-all-values> with the first occurrence of this tag/attr-name.
Tried several with currently the use of "preceding-siblings" to know the previous value of "attr-name" within the loop but the syntax keeps nagging me to result in usable code.
So helpful suggestions are more then welcome...

You could do:
<xsl:if test="not(#attr-name = preceding-sibling::attr[1]/#attr-name)">
<remove-all-values/>
</xsl:if>
Alternatively, you could define a key as:
<xsl:key name="attr-by-name" match="attr" use="#attr-name" />
and then do something along the lines of Muenchian grouping:
<xsl:if test="count(. | key('attr-by-name', #attr-name)[1]) = 1">
<remove-all-values/>
</xsl:if>
Note that the two are not the same: you will see a difference if the groups are not contiguous.

Related

xsl get node that not compare in key

In this example i have this xml
<CityStates>
<States>
<State Abbr="AL">Alabama</State>
<State Abbr="AK">Alaska</State>
<State Abbr="AZ">Arizona</State>
<State Abbr="AR">Arkansas</State>
</States>
<Cities>
<City State="NY" >New York</City>
<City State="CA" >Los Angeles</City>
<City State="AZ" >Chicago</City>
<City State="AR" >Houston</City>
<City State="AR" >Philadelphia</City>
</Cities>
</CityStates>
I would like to view only the nodes that do not have the State with the Key()
xsl is this:
<xsl:key name="keyState" match="State" use="#Abbr"/>
<xsl:template match="/">
<xsl:for-each select="//City">
<xsl:value-of select="City"/>
</xsl:for-each>
</xsl:template>
Use a predicate City[not(key('keyState', #State))] i.e.
<xsl:for-each select="//City[not(key('keyState', #State))]">
<xsl:value-of select="."/>
</xsl:for-each>
Try something like:
<xsl:template match="/CityStates">
<xsl:for-each select="Cities/City[not(key('keyState', #State))]" >
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
You haven't posted the expected result. You will probably want to add some kind of delimiter after the xsl:value-of instruction.

XML to pipe-delimited - How to copy/duplicate data to different rows

I'm new to XSLT and trying to transform from XML to pipe-delimited format. The problem that I'm having is that in the output, each claim has to be duplicated for each service line.
Expected Output:
EP030315706890704|TESTSUBMITTER|FAMILY HEALTHCARE|1122334455|1|99214|179.00
EP030315706890704|TESTSUBMITTER|FAMILY HEALTHCARE|1122334455|2|2000F|0.00
EP030315706890705|TESTSUBMITTER2|FAMILY HEALTHCARE|1122334455|1|99214|179.00
EP030315706890705|TESTSUBMITTER2|FAMILY HEALTHCARE|1122334455|2|2000F|0.00
Input XML looks as follows:
<payloadContainer>
<afile>
<clm>
<hdr>
<corn>EP030315706890704</corn>
<idSend>112233445</idSend>
<nmSend>TESTSUBMITTER</nmSend>
</hdr>
<provBill>
<name>
<nmOrg>FAMILY HEALTHCARE</nmOrg>
</name>
<id T="XX" P="P">1122334455</id>
</provBill>
<serv S="1">
<numLine>1</numLine>
<prof>
<px L="S">
<cdPx T="HC">99214</cdPx>
</px>
<amtChrg>179.00</amtChrg>
</prof>
</serv>
<serv S="2">
<numLine>2</numLine>
<prof>
<px L="S">
<cdPx T="HC">2000F</cdPx>
</px>
<amtChrg>0.00</amtChrg>
</prof>
</serv>
</clm>
<clm>
<hdr>
<corn>EP030315706890705</corn>
<idSend>112233445</idSend>
<nmSend>TESTSUBMITTER2</nmSend>
</hdr>
<provBill>
<name>
<nmOrg>FAMILY HEALTHCARE</nmOrg>
</name>
<id T="XX" P="P">1122334455</id>
</provBill>
<serv S="1">
<numLine>1</numLine>
<prof>
<px L="S">
<cdPx T="HC">99214</cdPx>
</px>
<amtChrg>179.00</amtChrg>
</prof>
</serv>
<serv S="2">
<numLine>2</numLine>
<prof>
<px L="S">
<cdPx T="HC">2000F</cdPx>
</px>
<amtChrg>0.00</amtChrg>
</prof>
</serv>
</clm>
</afile>
</payloadContainer>
Desired output XML:
<Table>
<row>
.... All the fields represented here.
</row>
</Table>
Possible solution: https://www.dropbox.com/s/wzvtzw7ihtgxx9o/claimtoRedshift.xsl
This scenario creates two row's dynamically. However, I'm still stuck at how to duplicate for each service line.
I don't see the connection of the linked XSLT to the question presented here. AFAICT, the following stylesheet will return the expected pipe-delimited output:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/payloadContainer">
<xsl:for-each select="afile/clm">
<xsl:variable name="common">
<xsl:value-of select="hdr/corn"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="hdr/nmSend"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="provBill/name/nmOrg"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="provBill/id"/>
<xsl:text>|</xsl:text>
</xsl:variable>
<xsl:for-each select="serv">
<xsl:value-of select="$common"/>
<xsl:value-of select="numLine"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="prof/px/cdPx"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="prof/amtChrg"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

xslt printing a name from a node reference

This is my first attempt at xslt.
I am setting up fields to print on a PDF form.
I have this:
<Field>
<Name>Accident1_Name</Name>
<Value>
<xsl:value-of
select="PersPolicy/AccidentViolation[1]/#DriverRef" />
</Value>
<Type>Tx</Type>
</Field>
But this just give me the driver number.
in XML it looks like this
<AccidentViolation id="N196" DriverRef="N86" saved="true">
<AccidentViolationCd id="N202">NO</AccidentViolationCd>
<AccidentViolationDt id="N198">2012-04-05</AccidentViolationDt>
<PlaceIncident id="N201">Tampa Florida</PlaceIncident>
<ConvictionDt id="N199">2012-04-05</ConvictionDt>
<AccidentViolationRecordTypeCd id="N200">AAM</AccidentViolationRecordTypeCd>
</AccidentViolation>
<PersDriver id="N86" saved="true">
<GeneralPartyInfo id="N88">
<NameInfo id="N89">
<PersonName id="N90">
<Surname id="N93">Test</Surname>
<GivenName id="N91">Mary</GivenName>
<OtherGivenName id="N92">P</OtherGivenName>
</PersonName>
<CommlName id="N116">
<CommercialName id="N117">School District</CommercialName>
</CommlName>
</NameInfo>
<Addr id="N119">
<AddrTypeCd id="N118">EmployerAddress</AddrTypeCd>
<Addr1 id="N120">100 Applewild</Addr1>
<City id="N121">Lady Lake</City>
<StateProvCd id="N122">FL</StateProvCd>
<PostalCode id="N123">33333-4444</PostalCode>
</Addr>
</GeneralPartyInfo>
</PersDriver>
how do I get the field to show 'Test Mary'?
Thank you for any assistance you can provide. Sorry if this is a duplicate of another question I didn't quite know what to search for.
Appreciate the help.
You can do this using keys. Put this near the top of your XSLT, inside the xsl:stylesheet element and outside of any xsl:template elements:
<xsl:key name="kPers" match="PersDriver" use="#id" />
Then you can do this:
<xsl:variable name="a1driver"
select="key('kPers', PersPolicy/AccidentViolation[1]/#DriverRef)" />
<Field>
<Name>Accident1_Name</Name>
<Value>
<xsl:variable name="name"
select="$a1driver/GeneralPartyInfo/NameInfo/PersonName" />
<xsl:value-of select="concat($name/Surname, ' ', $name/GivenName)" />
</Value>
<Type>Tx</Type>
</Field>
$a1driver should be a reference to the driver's PersDriver element, and you can use it to get any other information about the driver that you need.
A more dynamic way to do this for multiple drivers (like you're doing in the answer you posted) is to use for-each or templates:
<xsl:for-each select="PersPolicy/AccidentViolation">
<xsl:variable name="driver" select="key('kPers', #DriverRef)" />
<Field>
<Name><xsl:value-of select="concat('Accident', position(), '_Name')" /></Name>
<Value>
<xsl:variable name="name"
select="$driver/GeneralPartyInfo/NameInfo/PersonName" />
<xsl:value-of select="concat($name/Surname, ' ', $name/GivenName)" />
</Value>
<Type>Tx</Type>
</Field>
</xsl:for-each>

Generate XPath using XPath

I have two separate types of XML documents (one is a.xml & the other is b.xml). Document a.xml, is my main source document, on which I have to run queries. Document b.xml contains all possible information to fetch records from a.xml.
Document: «a.xml»
<rs>
<r id="r1">
<f0>typeA</f0>
<f1>contains value1, value2 and value3</f1>
</r>
<r id="r2">
<f0>typeB</f0>
<f1>contains value4 and value7</f1>
</r>
<r id="r3">
<f0>typeA</f0>
<f1>contains value2 and value5</f1>
</r>
<r id="r4">
<f0>typeC</f0>
<f1>contains value1 and value6</f1>
</r>
<r id="r5">
<f0>typeA</f0>
<f1>contains value5</f1>
</r>
<r id="r6">
<f0>typeC</f0>
<f1>contains value1, value2 and value3</f1>
</r>
</rs>
Document: «b.xml»
<?xml version="1.0"?>
<qs>
<q id="q1">
<i0>typeA</i0>
<i1>value1|value2|value3</i1>
<i2>value18|value35</i2>
<i3>value1|value7</i3>
</q>
<q id="q2">
<i0>typeB</i0>
<i1>value2|value7</i1>
<i2>value9|value20</i2>
<i3>value4</i3>
</q>
</qs>
Now I like to generate dynamic XPath selector strings based on the values of b.xml to be stored in Document c.xml. And it would look like:
Document c.xml
<xps>
<xp id="q1">
<t1>/rs/r[contains(f0,'typeA')
and contains(f1,'value1')
and contains(f1,'value2')
and contains(f1,'value3')]</t1>
<t2>/rs/r[contains(f0,'typeA')
and contains(f1,'value18')
and contains(f1,'value35')]</t2>
<t3>/rs/r[contains(f0,'typeA')
and contains(f1,'value1')
and contains(f1,'value7')]</t3>
</xp>
<xp id="q2">
<t1>/rs/r[contains(f0,'typeB')
and contains(f1,'value2')
and contains(f1,'value7')]</t1>
<t2>/rs/r[contains(f0,'typeA')
and contains(f1,'value9')
and contains(f1,'value20')]</t2>
<t3>/rs/r[contains(f0,'typeA')
and contains(f1,'value4')]</t3>
</xp>
</xps>
If somebody here having any idea, how to do that job in XSLT version 1.0. Thanks in advance.
Here is a solution in XSLT 1.0:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="#*|text()" />
<xsl:template match="/">
<xps>
<xsl:apply-templates/>
</xps>
</xsl:template>
<xsl:template match="q">
<xp id="{#id}">
<xsl:apply-templates/>
</xp>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'i')][not(self::i0)]">
<xsl:element name="t{substring-after(name(), 'i')}">
<xsl:text>/rs/r[contains(f0, '</xsl:text>
<xsl:value-of select="preceding-sibling::i0"/>
<xsl:text>')</xsl:text>
<xsl:call-template name="more-conditions">
<xsl:with-param name="list" select="."/>
</xsl:call-template>
<xsl:text>]</xsl:text>
</xsl:element>
</xsl:template>
<xsl:template name="more-conditions">
<xsl:param name="list"/>
<xsl:param name="delimiter" select="'|'"/>
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<xsl:call-template name="more-conditions">
<xsl:with-param name="list" select="substring-before($list, $delimiter)"/>
</xsl:call-template>
<xsl:call-template name="more-conditions">
<xsl:with-param name="list" select="substring-after($list, $delimiter)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:text> and contains(f1, '</xsl:text>
<xsl:value-of select="$list"/>
<xsl:text>')</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Applied to your input document, it produces the following output:
<xps>
<xp id="q1">
<t1>/rs/r[contains(f0, 'typeA') and contains(f1, 'value1') and contains(f1, 'value2') and contains(f1, 'value3')]</t1>
<t2>/rs/r[contains(f0, 'typeA') and contains(f1, 'value18') and contains(f1, 'value35')]</t2>
<t3>/rs/r[contains(f0, 'typeA') and contains(f1, 'value1') and contains(f1, 'value7')]</t3>
</xp>
<xp id="q2">
<t1>/rs/r[contains(f0, 'typeB') and contains(f1, 'value2') and contains(f1, 'value7')]</t1>
<t2>/rs/r[contains(f0, 'typeB') and contains(f1, 'value9') and contains(f1, 'value20')]</t2>
<t3>/rs/r[contains(f0, 'typeB') and contains(f1, 'value4')]</t3>
</xp>
</xps>
I added some of the whitespace. You can modify the transform based on your needs, but this should get you started.
You cannot use a variable as an xpath selector in xslt version 1, however there are likely other ways you might accomplish this task. If you provide an idea of the problem rather than your intended solution people might be able to help :)

get xpath from xsl variable

I am trying to get some xpath from xsl variable using xsl ver 1.0 .
That’s my variable:
<xsl:variable name ="myVar">
<RefData RefTag="test1" bbb="false" />
<RefData RefTag="test2" bbb="false" />
<RefData RefTag="test3" bbb="false" />
<RefData RefTag="test4" bbb="true" />
<RefData RefTag="test5" bbb="false" />
<RefData RefTag="test6" bbb="false" />
</xsl:variable>
I am trying to get bbb attribure value using the RefTag value:
<xsl:if test="$myVar/RefData[#RefTag = 'test3']/#bbb">
this is not working.
VS XSL Debugger returns an error:
"To use a result tree fragment in a path expression, first convert it to a node-set using the msxsl:node-set() function."
I don't understand how to use msxsl:node-set() function, and anyway I prefer not to use msxsl namesapce.
Can anyone help here?
One solution that doesn't need the xxx:node-set() extension is the following:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<!-- -->
<xsl:variable name ="myVar">
<RefData RefTag="test1" bbb="false" />
<RefData RefTag="test2" bbb="false" />
<RefData RefTag="test3" bbb="false" />
<RefData RefTag="test4" bbb="true" />
<RefData RefTag="test5" bbb="false" />
<RefData RefTag="test6" bbb="false" />
</xsl:variable>
<!-- -->
<xsl:variable name="vrefVar" select=
"document('')/*/xsl:variable[#name='myVar']"
/>
<!-- -->
<xsl:template match="/">
<xsl:value-of select="$vrefVar/*[#RefTag='test3']/#bbb"/>
</xsl:template>
</xsl:stylesheet>
When the above transformation is applied on any XML document (not used), the wanted result is produced:
false
Do note the use of the XSLT document() function in order to access the required <xsl:variable> simply as an element in an xml document.
<xsl:variable name="myVariable" select="msxsl:node-set($myVar)"/>
You can avoid msxsl namespace by moving variable contents to the source xml.
Assuming this XML:
<test1>
<RefData RefTag="test1"/>
<RefData RefTag="test2"/>
<RefData RefTag="test3"/>
<RefData RefTag="test4"/>
<RefData RefTag="test5"/>
<RefData RefTag="test6"/>
</test1>
Something like this could work:
<xsl:template match="/">
<xsl:apply-templates select="test1/RefData"/>
</xsl:template>
<xsl:template match="RefData">
<xsl:variable name="myVar">
<xsl:choose>
<xsl:when test="#RefTag = 'test1'">false</xsl:when>
<xsl:when test="#RefTag = 'test2'">false</xsl:when>
<xsl:when test="#RefTag = 'test3'">false</xsl:when>
<xsl:when test="#RefTag = 'test4'">true</xsl:when>
<xsl:when test="#RefTag = 'test5'">false</xsl:when>
<xsl:when test="#RefTag = 'test6'">false</xsl:when>
<xsl:otherwise>true</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!--<text x="{$myVar}"/>-->
</xsl:template>