Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

I want to get GRD with lowest InternalPartyID? 1

Status
Not open for further replies.

momo2000

Programmer
Jan 2, 2015
63
0
0
US
I want to get a Guardian (GRD) that has the lowest CaseParty@InternalPartyID and where Case/CaseParty/Connection/RemoveDate does not exist.
How do I do this because it has the lowest InternalPartyID = 1614672231
Expected result should be like this
XML:
<CaseParty ID="16769136" InternalCasePartyID="1634788175" InternalPartyID="1614672231">
			<Connection Word="GRD" BaseConnection="GD" ID="36381880" InternalCasePartyConnectionID="1636470565">
				<Description>Guardian</Description>
				<TimestampCreate>2/18/2015 4:27:54 PM</TimestampCreate>
				<DateAdded>02/18/2015</DateAdded>
			</Connection>
			<CasePartyName Current="true" ID="10052180" InternalNameID="1615525304">
				<NameType>Standard</NameType>
				<NameFirst>Cindee</NameFirst>
				<NameLast>Truesdell</NameLast>
				<FormattedName>Truesdell, Cindee</FormattedName>
			</CasePartyName>
		</CaseParty>

my xml doc
XML:
<Integration xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:tsg="[URL unfurl="true"]http://tsgweb.com"[/URL] xmlns:IXML="[URL unfurl="true"]http://tsgweb.com"[/URL] xmlns:CMCodeQueryHelper="urn:CMCodeQueryHelper" PackageID="BCA PO Notification" MessageID="66595889" xmlns="">
	<Case InternalID="1617088771" ID="12122866" xmlns:user="[URL unfurl="true"]http://tylertechnologies.com">[/URL]
		<CaseParty ID="16769136" InternalCasePartyID="1634788175" InternalPartyID="1614672231">
			<Connection Word="GRD" BaseConnection="GD" ID="36381880" InternalCasePartyConnectionID="1636470565">
				<Description>Guardian</Description>
				<TimestampCreate>2/18/2015 4:27:54 PM</TimestampCreate>
				<DateAdded>02/18/2015</DateAdded>
			</Connection>
			<CasePartyName Current="true" ID="10052180" InternalNameID="1615525304">
				<NameType>Standard</NameType>
				<NameFirst>Cindee</NameFirst>
				<NameLast>Truesdell</NameLast>
				<FormattedName>Truesdell, Cindee</FormattedName>
			</CasePartyName>
		</CaseParty>
		<CaseParty ID="16769137" InternalCasePartyID="1634788180" InternalPartyID="1614672232">
			<SendNotice>true</SendNotice>
			<RestrictView>false</RestrictView>
			<ObservedRace Word="H">Native Hawaiian or Other Pacific Islander</ObservedRace>
			<Connection Word="GRD" BaseConnection="GD" ID="36381885" InternalCasePartyConnectionID="1636470570">
				<Description>Guardian</Description>
				<TimestampCreate>2/18/2015 4:35:31 PM</TimestampCreate>
				<DateAdded>02/18/2015</DateAdded>
			</Connection>
			<CasePartyName Current="true" ID="10052181" InternalNameID="1615525305">
				<NameType>Standard</NameType>
				<NameFirst>Regina</NameFirst>
				<NameLast>Sedar</NameLast>
				<FormattedName>Sedar, Regina</FormattedName>
			</CasePartyName>
		</CaseParty>
	</Case>
</Integration>
 
This seems to be another application of the technique I illustrated in thread426-1744439.

Code:
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform"[/URL] 
				xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
				xmlns:tsg="[URL unfurl="true"]http://tsgweb.com"[/URL] 
				xmlns:IXML="[URL unfurl="true"]http://tsgweb.com"[/URL] 
				xmlns:CMCodeQueryHelper="urn:CMCodeQueryHelper"
                exclude-result-prefixes="tsg IXML msxsl CMCodeQueryHelper">

	<xsl:template match="/">
		<xsl:for-each select="//CaseParty[Connection/@Word = 'GRD'][count(Connection/RemoveDate) = 0]">
			<xsl:sort select="@InternalPartyID " data-type="number" order="ascending"/>
			<xsl:if test="position() = 1">
				<xsl:copy-of select="."/>
			</xsl:if>
		</xsl:for-each>
	</xsl:template>
</xsl:stylesheet>
The xsl:for-each, selects all CaseParty nodes that have within them a Connection node that has itsWord attribute equal GRD, and also hase no child element named RemoveDate. (Inside the for-each, the context will be a CaseParty node.)

The xsl:sort modifies the default node order for the for-each (which is document order), and gets the node having the lowest InternalPartyID numeric value to the first position in the node set.

The xsl:if causes only the first node, viz the one with the lowest InternalPartyID, to be further processed.

The xsl:copy-of copies the selected node(s) to the output result tree. In this case, since select=".", the single CaseParty that has a guardian (GRD) with the lowest InternalPartyID value is copied in its entirety.


Tom Morrison
Hill Country Software
 
Tom thanks for the help. The only problem is that (I should have stated it before hand) I am calling a Guardian template) when the conditions are met. I have attached the xslt code because it is long to past here.
Here is the output I get when using what you suggested.
Let me know if you suggested anything more from me.
XML:
<CaseParty ID="16769136" InternalCasePartyID="1634788175" InternalPartyID="1614672231" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:tsg="[URL unfurl="true"]http://tsgweb.com"[/URL] xmlns:IXML="[URL unfurl="true"]http://tsgweb.com"[/URL] xmlns:CMCodeQueryHelper="urn:CMCodeQueryHelper" xmlns:user="[URL unfurl="true"]http://tylertechnologies.com">[/URL]
				<SendNotice>true</SendNotice>
				<RestrictView>false</RestrictView>
				<ObservedRace Word="H">Native Hawaiian or Other Pacific Islander</ObservedRace>
				<Connection Word="GRD" BaseConnection="GD" ID="36381880" InternalCasePartyConnectionID="1636470565">
					<Description>Guardian</Description>
					<TimestampCreate>2/18/2015 4:27:54 PM</TimestampCreate>
					<DateAdded>02/18/2015</DateAdded>
				</Connection>
				<CasePartyName Current="true" ID="10052180" InternalNameID="1615525304">
					<NameType>Standard</NameType>
					<NameFirst>Cindee</NameFirst>
					<NameLast>Truesdell</NameLast>
					<FormattedName>Truesdell, Cindee</FormattedName>
				</CasePartyName>
				<TimestampCreate>2/18/2015 4:27:54 PM</TimestampCreate>
			</CaseParty>
			<ext:Guardian>
				<ext:PersonBirthDate/>
				<ext:PersonRaceCode>H</ext:PersonRaceCode>
			</ext:Guardian>
			<ext:Guardian>
				<ext:PersonBirthDate/>
				<ext:PersonRaceCode>H</ext:PersonRaceCode>
			</ext:Guardian>

Here is what I want instead
XML:
<ext:Guardian>
	<ext:AddressReference ext:currentIndicator="true">
		<nc:LocationReference s:ref="INT17101998"/>
			</ext:AddressReference>
			<ext:PersonBirthDate ext:approximateDateIndicator="false" ext:currentIndicator="true">1963-07-29</ext:PersonBirthDate>
			<nc:PersonName>
				<nc:PersonGivenName>Kimberly</nc:PersonGivenName>
				<nc:PersonMiddleName>Jean</nc:PersonMiddleName>
				<nc:PersonSurName>Yuhala</nc:PersonSurName>
				<nc:PersonNameSuffixText/>
				<nc:PersonFullName>Yuhala, Kimberly Jean</nc:PersonFullName>
			</nc:PersonName>
	<ext:PersonRaceCode>B</ext:PersonRaceCode>
</ext:Guardian>
 
[Sorry, the data in the last post seems not to match anything in earlier posts.]

My example shows how to obtain the context of the desired node (namely, CaseParty). Without more time to study your XSLT, which I don't have, I am not sure where the requirement stated in the original post, or this most recent post, might fit in.

However, once you are in the context of CaseParty, you can use normal XSLT techniques to pull information into the output result tree. For example, from the context of CaseParty, the XPath expression (that you might use in <xsl:value-of>, for example) for the first name would be:
Code:
CasePartyName/NameFirst

Also, the sorting technique can apply to <xsl:apply-templates> (typed, not tested):
Code:
<xsl:apply-templates mode="outputGuardian" select="//CaseParty[Connection/@Word = 'GRD'][count(Connection/RemoveDate) = 0]">
[indent]<xsl:sort select="@InternalPartyID " data-type="number" order="ascending"/>[/indent]
</xsl:apply-templates>
...
<xsl:template match="CaseParty" mode="outputGuardian">
[indent]<xsl:if test="position() = 1">
[indent]<!-- place code here to output the desired info from the selected CaseParty node, for example -->
<nc:PersonName>
[indent]<nc:PersonGivenName><xsl:value-of select="CasePartyName/NameFirst"/></nc:PersonGivenName>[/indent]
</nc:PersonName>[/indent]
</xsl:if>[/indent]
</xsl:template>


Tom Morrison
Hill Country Software
 
Thanks for the star.

Was there one piece of information you found particularly useful for solving your problem? Just would like to know...
 
k5tm...based on your last suggestion, I modified my code to the following and I am getting the GRD with the lowest ID
Here is my modified code
XML:
<xsl:for-each select="//CaseParty[Connection[(@Word='GRD') and (not(RemovedDate))]]">
	<xsl:if test="(count(//CaseParty[(Connection[(@Word='GRD') and (not(RemovedDate))]) and (@InternalPartyID&lt;current()/@InternalPartyID)]))=0">
	<xsl:for-each select="//Party[@InternalPartyID=current()/@InternalPartyID]">
	<xsl:call-template name="Guardian"/>
	</xsl:for-each>
	</xsl:if>
</xsl:for-each>
 
I am going to risk giving some unsolicited advice based on your modified code. While I hope that you might consider the reasoning and use the methods I demonstrated, I really feel that your original question is generally applicable to a wider audience, and i want others that may happen along to know why I used what I did.

XPath Predicates

My example was coded as:
Code:
//CaseParty[Connection/@Word = 'GRD'][count(Connection/RemoveDate) = 0]
You recoded this as:
Code:
//CaseParty[Connection[(@Word='GRD') and (not(RemovedDate))]]
Rather than nesting predicates (the expressions contained in square brackets), as you did, I 'stacked' the predicates one after the other. You basically used the booleand operator and where I simply added another predicate.

Consider how predicates operate in this example. First the node-set //CaseParty is created. Then the first predicate is applied to that node-set, creating another node-set. In your case, there is only one predicate operating on the //CaseParty node-set; your second predicate is evaluated multiple times (once per CaseParty node) before the outer node-set can be determined.

In my example, the first predicate is applied, creating a temporary node-set, and then the second predicate is applied to that first temporary result (presumably fewer than all the CaseParty nodes in the document), giving the final node-set.

So, you may think that this is merely a matter of style, and that there is no difference in the final result. And, for this particular situation, that is true. (In my minds eye, I imagine the 'stacked' predicates to be more efficient, and they are certainly easier to read left-to-right.)

[highlight #FCE94F]But there is a trap waiting for the unwary.[/highlight] Remember that the nodes in the node-set are in 'document order' unless sorted or otherwise coerced into a different order (more about which later). So, one has to be careful when using the position() function. To illustrate the trap, let's replace the second predicate with [position()=1], which can be abbreviated [1]:
Code:
//CaseParty[Connection/@Word = 'GRD'][1]
versus:
Code:
//CaseParty[Connection[(@Word='GRD') and position()=1]]
versus:
Code:
//CaseParty[Connection[(@Word='GRD')] and position()=1]

Note that these are not equivalent. The first returns the first //CaseParty node in document order that has a node satisfying [tt]Connection/@Word = 'GRD'[/tt]. The second example will return only those nodes where the first Connection node in document order is the qualifying Connection child node. The third example will return a node only if the first CaseParty node in document order has a qualifying Connection child node.

I strongly urge the use of the 'stacked' predicate technique to filter node-sets for readability, efficiency and predictability.

(Note: there is another use of predicates where the predicate is applied at the 'step' level of an XPath expression such as [tt]/main/subnode[predicate1]/nextlevel[predicate2]/*[1][/tt]. This is really another topic, that involves understanding the axis being applied at each step in the XPath expression.)

Use xsl:sort to re-order node-sets out of document order

Your outer xsl:for-each uses the // operator in the XPath expression. The // operator requires a full document scan. Sometimes that can't be helped, and as you observe, I used it too.

However, in order to avoid xsl:sort (apparently) you ended up using the // operator twice inside the outer xsl:for-each. Now only the xsl:if is going to be evaluated in each iteration, so you are essentially turning an order N loop into an order N[sup]2[/sup] loop. In small input documents, who cares... I have seen careless use of the // operator turn milliseconds into minutes on larger documents.

But the xsl:sort will reorder a node-set (which is merely an array of pointers in the processors memory) with far fewer comparisons.

Conclusion

XSLT is essentially a declarative language. Most programmers come to XSLT from procedural programming education and experience, and sometimes trying to force-fit procedural paradigms onto XSLT just doesn't do the language justice. Certainly some of my earlier examples on this very forum show just how ugly it can get, but a few years of experience, studying the examples of the masters, has helped me move between the two programming paradigms quite a bit more easily.


Tom Morrison
Hill Country Software
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top