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 Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How do I get the right output using template? 2

Status
Not open for further replies.

momo2000

Programmer
Jan 2, 2015
63
US
I would like to get correct output but my template is returning wrong output.
Correct output should be
XML:
<EditReport>
	<MessageIdentification>Case:1097</MessageIdentification>
	<SourceLocation>111</SourceLocation>
	<SourceUser>Tester</SourceUser>
	<EditResult>
		<EditNumber>001</EditNumber>
		<ItemIdentification/>
		<Result>Fail</Result>
	</EditResult>
</EditReport>

I am getting the following output which is wrong because Result node should say Fail
XML:
<EditReport>
	<MessageIdentification>Case:1097</MessageIdentification>
	<SourceLocation>111</SourceLocation>
	<SourceUser>Tester</SourceUser>
	<EditResult>
		<EditNumber>001</EditNumber>
		<ItemIdentification/>
		<Result>Pass</Result>
	</EditResult>
</EditReport>

xml document
XML:
<Integration>
	<ControlPoint Timestamp="10/13/2016 11:00:15 AM" UserID="Tester"/>
	<Case>
		<CaseNumber>1097</CaseNumber>
		<Court>
			<NodeID>111</NodeID>
		</Court>
	</Case>
	<IntegrationConditions>
		<NotificationEvent notificationType="CasePartyUpdates">CasePartyUpdate</NotificationEvent>
	</IntegrationConditions>
</Integration>

xslt code
Code:
<xsl:stylesheet xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform"[/URL] xmlns:msxsl="urn:schemas-microsoft-com:xslt" version="1.0">
	<xsl:output method="xml"/>
	<xsl:variable name="gEditPass">Pass</xsl:variable>
	<xsl:variable name="gEditFail">Fail</xsl:variable>
	<xsl:variable name="gMessageIdentification">Case:<xsl:value-of select="Integration/Case/CaseNumber"/>
	</xsl:variable>
	<xsl:template match="/">
		<EditReport>
			<MessageIdentification>
				<xsl:value-of select="$gMessageIdentification"/>
			</MessageIdentification>
			<SourceLocation>
				<xsl:value-of select="Integration/Case/Court/NodeID"/>
			</SourceLocation>
			<SourceUser>
				<xsl:value-of select="Integration/ControlPoint/@UserID"/>
			</SourceUser>
			<!-- Edit number 001 -->
			<xsl:call-template name="EditPartyUpdate"/>
		</EditReport>
	</xsl:template>
	<!-- E D I T   N U M  B  E  R  001 -->
	<xsl:template name="EditPartyUpdate">
		<xsl:variable name="vPOSendEventCount">
			<xsl:value-of select="count(NotificationEvent[(@notificationType='ProtectionOrder') or (@notificationType='ProtectionOrderInactivation')])>0"/>
		</xsl:variable>
		<xsl:variable name="vPartyUpdateEventCount">
			<xsl:value-of select="count(NotificationEvent[@notificationType='CasePartyUpdates'])>0"/>
		</xsl:variable>
		<xsl:for-each select="Integration/IntegrationConditions/IntegrationCondition/NotificationEvent">
			<EditResult>
				<EditNumber>001</EditNumber>
				<ItemIdentification>
				</ItemIdentification>
				<Result>
					<xsl:choose>
						<xsl:when test="$vPartyUpdateEventCount=0">
							<xsl:value-of select="$gEditFail"/>
						</xsl:when>
						<xsl:otherwise>
							<xsl:value-of select="$gEditPass"/>
						</xsl:otherwise>
					</xsl:choose>
				</Result>
			</EditResult>
		</xsl:for-each>
	</xsl:template>
</xsl:stylesheet>
 
I'll assume you missed an <IntegrationCondition> element in the XML you posted.

The values you're assigning to vPartyUpdateEventCount will be "true" or "false", not 0 or any other number, since you're comparing the result from a count() function call against 0.

So, just remove the "> 0" in the select attribute.
 
atlopes. I did as you asked me to but I am still getting the same wrong output when my xml document has the following nodes
XML:
<IntegrationCondition Word="BCAPO" Description="BCA PO Notification">
			<NotificationEvent notificationType="ProtectionOrder" internalProtectionOrderID="12556" protectionOrderNumber="1606232" xmlns:xs="[URL unfurl="true"]http://www.w3.org/2001/XMLSchema"[/URL] xmlns:fn="[URL unfurl="true"]http://www.w3.org/2005/xpath-functions"[/URL] xmlns:mscef="courts.state.mn.us/extfun" xmlns:msxsl="urn:schemas-microsoft-com:xslt">Signed By Judicial Officer</NotificationEvent>
		</IntegrationCondition>
 
If I'm understanding it right, the variables must be set inside the cycle, which must be traverse all <IntegrationCondition> elements (not the NotificationEvent). Anyway, your new XML would only affect the value of vPOSendEventCount, not of vPartyUpdateEventCoun, which you are testing in the <xsl:choose> construct.
 
My result Pass or Fail should be based on whether or not vPartyUpdateEventCount vPOSendEventCount are greater than zero.
What should I change in xslt code so that it get the value of vPOSendEventCount and vPartyUpdateEventCount?
 
Your EditPartyUpdate template, rewritten (I assume that the fail occurs when both counts are 0):

Code:
  <xsl:template name="EditPartyUpdate">
    <xsl:for-each select="Integration/IntegrationConditions/IntegrationCondition">
      <xsl:variable name="vPOSendEventCount">
        <xsl:value-of select="count(NotificationEvent[(@notificationType='ProtectionOrder') or (@notificationType='ProtectionOrderInactivation')])"/>
      </xsl:variable>
      <xsl:variable name="vPartyUpdateEventCount">
        <xsl:value-of select="count(NotificationEvent[@notificationType='CasePartyUpdates'])"/>
      </xsl:variable>
      <EditResult>
        <EditNumber>001</EditNumber>
        <ItemIdentification>
        </ItemIdentification>
        <Result>
          <xsl:choose>
            <xsl:when test="$vPartyUpdateEventCount = 0 and $vPOSendEventCount = 0">
              <xsl:value-of select="$gEditFail"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$gEditPass"/>
            </xsl:otherwise>
          </xsl:choose>
        </Result>
      </EditResult>
    </xsl:for-each>
  </xsl:template>
 
Maybe my question was not clear. There are 4 scenarios, I have numbered them below.
Scenario 1. CasePartyUpdates = 0 and ProtectionOrder = 0 No EditResult (Nothing to display.

Scenario 2. CasePartyUpdates > 0 and ProtectionOrder = 0 EditResult is displayed with <Result>Fail</Result>

Scenario 3. CasePartyUpdates > 0 and ProtectionOrder > 0 EditResult is displayed with <Result>Pass</Result>

Scenario 4. CasePartyUpdates = 0 and ProtectionOrder > 0 EditResult is displayed with <Result>Pass</Result>
 
Code:
  <xsl:template name="EditPartyUpdate">
    <xsl:for-each select="Integration/IntegrationConditions/IntegrationCondition">
      <xsl:variable name="vPOSendEventCount">
        <xsl:value-of
          select="count(NotificationEvent[(@notificationType = 'ProtectionOrder') or (@notificationType = 'ProtectionOrderInactivation')])"/>
      </xsl:variable>
      <xsl:variable name="vPartyUpdateEventCount">
        <xsl:value-of select="count(NotificationEvent[@notificationType = 'CasePartyUpdates'])"/>
      </xsl:variable>
      <xsl:if test="$vPOSendEventCount != 0 or $vPartyUpdateEventCount != 0">
        <EditResult>
          <EditNumber>001</EditNumber>
          <ItemIdentification> </ItemIdentification>
          <Result>
            <xsl:choose>
              <xsl:when test="$vPOSendEventCount = 0">
                <xsl:value-of select="$gEditFail"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="$gEditPass"/>
              </xsl:otherwise>
            </xsl:choose>
          </Result>
        </EditResult>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>
 
momo2000,

First, let's go back to your original XSLT.

The first issue, and perhaps the real cause of your confusion, is that of the context node when the call-template processing instruction is encountered. The context is exactly "/". Since the root element is named [tt]Integration[/tt] the counts in the variables will always be zero. The XPath expression begins with [tt]NotificationEvent[/tt]; since the context is "/" the only way for the count to be non-zero is if [tt]NotificationEvent[/tt] were the root element of the document. Mr. Lopes takes this into account by shifting the for-each processing instruction to the beginning of the called template, and states "If I'm understanding it right, the variables must be set inside the cycle...". I have followed Mr. Lopes' lead in my example below, but I have serious reservations it is correct for all inputs (about which more later).

The lesson to be learned from this first issue: when creating a called template, use XML comments to state the expected context of the called template. This comment will then function as a reminder when you are coding the <xsl:call-template> processing instruction. If the context node is not already correct, you can use an xsl:for-each to bracket the xsl:call-template, thereby shifting the context node to that which is correct for the called template.

(Sidebar: The use of xsl:for-each simply to shift context, rather than iterate over multiple nodes, seems to be an unappreciated feature. But it is a remarkably useful feature, often vital to working in environments that have multiple input documents. For example, if you have an alternative input document that is a large table, you would want to use xsl:key so that your table references can be more efficient. But because the key reference has to be made within the context of the indexed document, you have to switch context to that document with a for-each. I have seen a couple DITA processors, for example, where such context shifting is easily as important as nodeset iteration.)​

Why do I have reservations about the correctness for all inputs? Your probelm statement seems to indicate that a single output is desired, but the presence of an element that is plural, [tt]IntegrationConditions[/tt], makes me think that something else is wrong.

Next, and probably not a real issue here, is the practice of using a single xsl:value-of inside the defintion of an xsl:variable. Use instead the [tt]select[/tt] attribute on the xsl:variable itself (as demonstrated below), with the identical value. Your construct actually creates a variable that has multiple text nodes. This usually sorts itself out as the XSLT processor coerces the multiple text nodes into a string that can be converted. But, if you are using a good XSL debugger, instead of seeing a simple integer for a count, you may see instead a tree structure as the value of a variable.

Here is my guess at an XSL approach:
Code:
<xsl:stylesheet xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform"[/URL] xmlns:msxsl="urn:schemas-microsoft-com:xslt" version="1.0">
	<xsl:output method="xml" indent="yes"/>
	<xsl:variable name="gEditPass">Pass</xsl:variable>
	<xsl:variable name="gEditFail">Fail</xsl:variable>
	<xsl:variable name="gMessageIdentification">Case:<xsl:value-of select="Integration/Case/CaseNumber"/>
	</xsl:variable>
	<xsl:template match="/">
		<EditReport>
			<MessageIdentification>
				<xsl:value-of select="$gMessageIdentification"/>
			</MessageIdentification>
			<SourceLocation>
				<xsl:value-of select="Integration/Case/Court/NodeID"/>
			</SourceLocation>
			<SourceUser>
				<xsl:value-of select="Integration/ControlPoint/@UserID"/>
			</SourceUser>
			<!-- Edit number 001 -->
			<xsl:call-template name="EditPartyUpdate"/>
		</EditReport>
	</xsl:template>
	<!-- E D I T   N U M  B  E  R  001 -->
	<xsl:template name="EditPartyUpdate"><!-- expected context is / -->
		<xsl:for-each select="Integration/IntegrationConditions/IntegrationCondition">
		<xsl:variable name="vPOSendEventCount" 
		              select="count(NotificationEvent[(@notificationType='ProtectionOrder') or (@notificationType='ProtectionOrderInactivation')])"/>
		<xsl:variable name="vPartyUpdateEventCount"
					  select="count(NotificationEvent[@notificationType='CasePartyUpdates'])"/>

		<xsl:comment>  CasePartyUpdates: <xsl:value-of select="$vPartyUpdateEventCount"/>
	    ProtectionOrder:  <xsl:value-of select="$vPOSendEventCount"/>
		Context Node:  <xsl:value-of select="local-name(.)"/></xsl:comment>
		<xsl:if test="not($vPartyUpdateEventCount=0 and $vPOSendEventCount=0)">
				<EditResult>
					<EditNumber>001</EditNumber>
					<ItemIdentification/>
					<Result><xsl:choose>
						    <xsl:when test="$vPartyUpdateEventCount &amp;gt; 0 and $vPOSendEventCount = 0"><xsl:value-of select="$gEditFail"/></xsl:when>
						    <xsl:otherwise><xsl:value-of select="$gEditPass"/></xsl:otherwise>
						    </xsl:choose></Result>
				</EditResult>
		</xsl:if>
			</xsl:for-each>
	</xsl:template>
</xsl:stylesheet>

Result below. Note that I have left in debugging XML comments. Often you can use such comments to help you determine what is happening.
XML:
<EditReport xmlns:msxsl="urn:schemas-microsoft-com:xslt">
  <MessageIdentification>Case:1097</MessageIdentification>
  <SourceLocation>111</SourceLocation>
  <SourceUser>Tester</SourceUser>
  <!--  CasePartyUpdates: 1
	    ProtectionOrder:  0
		Context Node:  IntegrationCondition-->
  <EditResult>
    <EditNumber>001</EditNumber>
    <ItemIdentification/>
    <Result>Fail</Result>
  </EditResult>
</EditReport>

Tom Morrison
Hill Country Software
 
Thanks Tom as always for helping me. I did modify your solution a little bit in order for it to work as I wanted it to.
Maybe I need to know how to clearly state in my questions what I am trying to achieve and also show the desired output. I know it makes it easier to get help is the question is clearly explained and the desired output shown/indicated in the question.
I did modify your code a little so that:
1. when (for example)NotificationEvent[notificationType="@CasePartyUpdates"]
the EditResult displays <EditNumber>001</EditNumber> and <Result>Fail</Result>
2. Whenever the NotificationEvent[notificationType="@ProtectionOrder"] the result EditResult displays <EditNumber>001</EditNumber> and <Result>Pass</Result>
3. If the XML document has both NotificationEvent[notificationType="@CasePartyUpdates"] and NotificationEvent[notificationType="@ProtectionOrder"]at the same time, the EditResult will always display <EditNumber>001</EditNumber> and <Result>Pass</Result>
I followed the code atlopes gave as solution.
 
momo200,

I have been in the business of developing and selling software for more than 40 years. For most of that time, my customers were other software vendors (that is, my company sold software tools). From that experience of supporting hundreds of customers, I have come to understand how difficult it is to separate oneself from all the bits and bytes, and be able to describe a problem clearly to someone who has not 'walked the road' with you. I still have to remind myself of that fact when I am the one seeking help on a problem. You are definitely not alone!

By the way, we are still rolling out integrations with eFileTexas, which shares the technology you are using. There are a lot of courts in Texas!

Thanks for the star. Always appreciated.


Tom Morrison
Hill Country Software
 
Tom your words are very encouraging. Sometimes I am a little frustrated when I am not able to articulate what I am trying to do. But on the bright side, you have a lot of experience and that helps you understand my questions even when they are not stated in a clear way! Besides you do help without making me feel dumb or stupid or unworthy of your time.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top