InComputero
InComputero

Reputation: 1

XSLT Validation: using for-each

Firstly, I will start by saying that I am not a programmer. Yet, somehow, I have been saddled with writing XSLT validation templates. I am doing the best I can with what little I know and have been able to get from searching the web. However, I am stuck on one piece of logic using the 'for-each' construct. Anyway, if you are kind enough to answer, please speak slowly and use small words!

I am trying to write an XSLT validation rule that will analyze an XML message. The basic construct of the part of the message I want to validate is

<VXU_V04.ORDER> One or more may be present, not a required group; I’m calling this “OrderGroup”
<ORC> Required; only one ORC Segment per OrderGroup allowed
<ORC.1> Required; only one ORC.1 field per ORC Segment allowed

In my sample message, I have three <VXU_V04.ORDER> groups. I have written the validation template below. When I test my validation template against my message with three order groups, I am not getting any output. I had another version of this template with another 'for-each' check at the ORC Segment level, but then I was getting the output "Only one Order Segment (ORC) allowed per Order Group." three times (presumably for each Order Group). The test message is correct in that I only have one ORC Segment per Order Group, so obviously my logic is wrong. (Some of the ORC.1 values are incorrect so I expected to get a message on the check that the value of ORC.1 is not 'RE'.)

Here is my validation template code:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:a="urn:hl7-org:v2xml" xmlns:dil="http://www.aegis.net/msg/filter/jaxb/rule" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!--      
                Must be 'RE'
         -->
    <xsl:variable name="OrderGroup" select="/a:VXU_V04/a:VXU_V04.ORDER"/>
    <xsl:variable name="ORCSegment" select="/a:VXU_V04/a:VXU_V04.ORDER/a:ORC"/>
    <xsl:variable name="ORC1Field" select="/a:VXU_V04/a:VXU_V04.ORDER/a:ORC/a:ORC.1"/>
    <xsl:template match="/">
        <dil:filterruleresult>
             <xsl:choose>
                 <xsl:when test= "count($OrderGroup)= 0">
                    <dil:message type="info">VXU Order Group not found.</dil:message>
                 </xsl:when>
             </xsl:choose>
             <xsl:for-each select="$OrderGroup">
                     <xsl:choose>
                       <xsl:when test="count(./a:ORC)= 0">  
                          <dil:message type="error">When Order Group present, ORC Segment is required.</dil:message>
                       </xsl:when>
                       <xsl:otherwise>
                          <xsl:for-each select="./a:VXU_V04.ORDER">
                             <xsl:choose>
                                 <xsl:when test="count(./a:ORC)> 0">  
                        <dil:message type="error">Only one Order Segment (ORC) allowed per Order Group.</dil:message>
                                 </xsl:when>
                                 <xsl:when test="count(./a:ORC.1)= 0">
                                <dil:message type="error">ORC-1 Order Control not found but is required.</dil:message>
                                 </xsl:when> 
                                 <xsl:when test="count(./a:ORC.1)> 1">
                                <dil:message type="error">Only one Order Control (ORC-1) allowed per Order Segment.</dil:message>
                                 </xsl:when> 
                                 <xsl:when test="./a:ORC.1!='RE'">
                                <dil:message type="error">ORC-1 Order Control must be equal to 'RE'.  Found '<xsl:value-of select="./a:ORC.1"/>' in message.</dil:message>
                                 </xsl:when>                      
                                 <xsl:when test="./a:ORC.1='RE'">
                                     <dil:message type="info">Valid value of '<xsl:value-of select="./a:ORC.1"/>' found for Order Control (ORC-1).</dil:message>
                                 </xsl:when>
                             </xsl:choose>
                          </xsl:for-each>  
                       </xsl:otherwise>
                      </xsl:choose> 
             </xsl:for-each>      
        </dil:filterruleresult>
    </xsl:template>
</xsl:stylesheet>

Here is the sample message:

  <VXU_V04.ORDER>
    <ORC>
      <ORC.1>RE</ORC.1>
      <ORC.3>
        <EI.1>197023</EI.1>
        <EI.2>DCS</EI.2>
      </ORC.3>
      <ORC.10>
        <XCN.2>
          <FN.1>Clerk</FN.1>
        </XCN.2>
        <XCN.3>Myron</XCN.3>
      </ORC.10>
      <ORC.17>
        <CE.1>DCS</CE.1>
        <CE.2>Dabig Clinical System</CE.2>
        <CE.3>StateIIS</CE.3>
      </ORC.17>
    </ORC>
    <RXA>
      <RXA.1>0</RXA.1>
      <RXA.2>1</RXA.2>
      <RXA.3>
        <TS.1>20090415132511</TS.1>
      </RXA.3>
      <RXA.4>
        <TS.1>20090415132511</TS.1>
      </RXA.4>
      <RXA.5>
        <CE.1>31</CE.1>
        <CE.2>Hep B Peds NOS</CE.2>
        <CE.3>CVX</CE.3>
      </RXA.5>
      <RXA.6>999</RXA.6>
      <RXA.9>
        <CE.1>01</CE.1>
        <CE.2>historical record</CE.2>
        <CE.3>NIP0001</CE.3>
      </RXA.9>
    </RXA>
  </VXU_V04.ORDER>
  <VXU_V04.ORDER>
    <ORC>
      <ORC.1>RE</ORC.1>
      <ORC.3>
        <EI.1>197027</EI.1>
        <EI.2>DCS</EI.2>
      </ORC.3>
      <ORC.10>
        <XCN.2>
          <FN.1>Clerk</FN.1>
        </XCN.2>
        <XCN.3>Myron</XCN.3>
      </ORC.10>
      <ORC.12>
        <XCN.2>
          <FN.1>Pediatric</FN.1>
        </XCN.2>
        <XCN.3>MARY</XCN.3>
        <XCN.10>L</XCN.10>
        <XCN.21>MD</XCN.21>
      </ORC.12>
    </ORC>
    <RXA>
      <RXA.1>0</RXA.1>
      <RXA.2>1</RXA.2>
      <RXA.3>
        <TS.1>20090531132511</TS.1>
      </RXA.3>
      <RXA.4>
        <TS.1>20090531132511</TS.1>
      </RXA.4>
      <RXA.5>
        <CE.1>48</CE.1>
        <CE.2>HIB PRP-T</CE.2>
        <CE.3>CVX</CE.3>
      </RXA.5>
      <RXA.6>999</RXA.6>
      <RXA.9>
        <CE.1>00</CE.1>
        <CE.2>new Immunization record</CE.2>
        <CE.3>NIP0001</CE.3>
      </RXA.9>
      <RXA.10>
        <XCN.2>
          <FN.1>Sticker</FN.1>
        </XCN.2>
        <XCN.3>Nurse</XCN.3>
      </RXA.10>
      <RXA.11>
        <LA2.4>
          <HD.1>DCS_DC</HD.1>
        </LA2.4>
      </RXA.11>
      <RXA.15>33k2a</RXA.15>
      <RXA.17>
        <CE.1>PMC</CE.1>
        <CE.2>sanofi</CE.2>
        <CE.3>MVX</CE.3>
      </RXA.17>
    </RXA>
    <RXR>
      <RXR.1>
        <CE.1>C28161</CE.1>
        <CE.2>IM</CE.2>
        <CE.3>NCIT</CE.3>
        <CE.4>IM</CE.4>
        <CE.5>IM</CE.5>
        <CE.6>HL70162</CE.6>
      </RXR.1>
    </RXR>
  </VXU_V04.ORDER>
  <VXU_V04.ORDER>
    <ORC>
      <ORC.1>RE</ORC.1>
      <ORC.3>
        <EI.1>197028</EI.1>
        <EI.2>DCS</EI.2>
      </ORC.3>
      <ORC.10>
        <XCN.2>
          <FN.1>Clerk</FN.1>
        </XCN.2>
        <XCN.3>Myron</XCN.3>
      </ORC.10>
      <ORC.12>
        <XCN.2>
          <FN.1>Pediatric</FN.1>
        </XCN.2>
        <XCN.3>MARY</XCN.3>
        <XCN.10>L</XCN.10>
        <XCN.21>MD</XCN.21>
      </ORC.12>
    </ORC>
    <RXA>
      <RXA.1>0</RXA.1>
      <RXA.2>1</RXA.2>
      <RXA.3>
        <TS.1>20090531132511</TS.1>
      </RXA.3>
      <RXA.4>
        <TS.1>20090531132511</TS.1>
      </RXA.4>
      <RXA.5>
        <CE.1>110</CE.1>
        <CE.2>DTAP-Hep B-IPV</CE.2>
        <CE.3>CVX</CE.3>
      </RXA.5>
      <RXA.6>999</RXA.6>
      <RXA.9>
        <CE.1>00</CE.1>
        <CE.2>new immunization record</CE.2>
        <CE.3>NIP0001</CE.3>
      </RXA.9>
      <RXA.10>
        <XCN.2>
          <FN.1>Sticker</FN.1>
        </XCN.2>
        <XCN.3>Nurse</XCN.3>
      </RXA.10>
      <RXA.11>
        <LA2.4>
          <HD.1>DCS_DC</HD.1>
        </LA2.4>
      </RXA.11>
      <RXA.15>xy3939</RXA.15>
      <RXA.17>
        <CE.1>SKB</CE.1>
        <CE.2>GSK</CE.2>
        <CE.3>MVX</CE.3>
      </RXA.17>
    </RXA>
    <RXR>
      <RXR.1>
        <CE.1>IM</CE.1>
        <CE.2>IM</CE.2>
        <CE.3>HL70162</CE.3>
        <CE.4>C28161</CE.4>
        <CE.5>IM</CE.5>
        <CE.6>NCIT</CE.6>
      </RXR.1>
    </RXR>
  </VXU_V04.ORDER>

Any assistance would be greatly appreciated.

Thank you!

Upvotes: 0

Views: 533

Answers (1)

Tim C
Tim C

Reputation: 70598

Perhaps you should consider becoming a programmer, as your XSLT is not half-bad for a non-programmer.

Anyway, to try and address the issue, you have an xsl:for-each loop for your order group

<xsl:for-each select="$OrderGroup">

This means you are looping over VXU_V04.ORDER elements as intended. But within this loop, within one of the xsl:when conditions, you then do this

<xsl:for-each select="./a:VXU_V04.ORDER">

But this means it is looking for a child element called VXU_V04.ORDER of the current VXU_V04.ORDER element. I don't think this is what you want. You are already looping over such elements, so you don't need to carry on looping.

Perhaps you mean to look for the child ORC elements

<xsl:for-each select="a:ORC">

(Do not you don't need to prefix it with ./ here, as that is the default selector anyway).

This would been the current <xsl:when test="count(./a:ORC)> 0"> test would need to move out of the inner loop and into the outer loop, and be changed to test for being more than 1, but the remaining conditions should then work.

Try this XSLT

<xsl:stylesheet version="2.0" xmlns:a="urn:hl7-org:v2xml" xmlns:dil="http://www.aegis.net/msg/filter/jaxb/rule" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!--      
                Must be 'RE'
         -->
    <xsl:variable name="OrderGroup" select="/a:VXU_V04/a:VXU_V04.ORDER"/>
    <xsl:variable name="ORCSegment" select="/a:VXU_V04/a:VXU_V04.ORDER/a:ORC"/>
    <xsl:variable name="ORC1Field" select="/a:VXU_V04/a:VXU_V04.ORDER/a:ORC/a:ORC.1"/>
    <xsl:template match="/">
        <dil:filterruleresult>
             <xsl:choose>
                 <xsl:when test= "count($OrderGroup)= 0">
                    <dil:message type="info">VXU Order Group not found.</dil:message>
                 </xsl:when>
             </xsl:choose>
             <xsl:for-each select="$OrderGroup">
                     <xsl:choose>
                       <xsl:when test="count(a:ORC)= 0">  
                          <dil:message type="error">When Order Group present, ORC Segment is required.</dil:message>
                       </xsl:when>
                       <xsl:when test="count(a:ORC)> 1">  
                        <dil:message type="error">Only one Order Segment (ORC) allowed per Order Group.</dil:message>
                       </xsl:when>
                       <xsl:otherwise>
                          <xsl:for-each select="a:ORC">
                             <xsl:choose>
                                 <xsl:when test="count(a:ORC.1)= 0">
                                <dil:message type="error">ORC-1 Order Control not found but is required.</dil:message>
                                 </xsl:when> 
                                 <xsl:when test="count(a:ORC.1)> 1">
                                <dil:message type="error">Only one Order Control (ORC-1) allowed per Order Segment.</dil:message>
                                 </xsl:when> 
                                 <xsl:when test="a:ORC.1!='RE'">
                                <dil:message type="error">ORC-1 Order Control must be equal to 'RE'.  Found '<xsl:value-of select="./a:ORC.1"/>' in message.</dil:message>
                                 </xsl:when>                      
                                 <xsl:when test="a:ORC.1='RE'">
                                     <dil:message type="info">Valid value of '<xsl:value-of select="./a:ORC.1"/>' found for Order Control (ORC-1).</dil:message>
                                 </xsl:when>
                             </xsl:choose>
                          </xsl:for-each>  
                       </xsl:otherwise>
                      </xsl:choose> 
             </xsl:for-each>      
        </dil:filterruleresult>
    </xsl:template>
</xsl:stylesheet>

Of course, the <xsl:for-each select="a:ORC"> is slight overkill, as you would have already validated there is only one. Ideally, you would use xsl:apply-templates here. Try this XSLT too

<xsl:stylesheet version="2.0" xmlns:a="urn:hl7-org:v2xml" xmlns:dil="http://www.aegis.net/msg/filter/jaxb/rule" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!--      
                Must be 'RE'
         -->
    <xsl:variable name="OrderGroup" select="/a:VXU_V04/a:VXU_V04.ORDER"/>
    <xsl:variable name="ORCSegment" select="/a:VXU_V04/a:VXU_V04.ORDER/a:ORC"/>
    <xsl:variable name="ORC1Field" select="/a:VXU_V04/a:VXU_V04.ORDER/a:ORC/a:ORC.1"/>
    <xsl:template match="/">
        <dil:filterruleresult>
             <xsl:choose>
                 <xsl:when test= "count($OrderGroup)= 0">
                    <dil:message type="info">VXU Order Group not found.</dil:message>
                 </xsl:when>
             </xsl:choose>
             <xsl:for-each select="$OrderGroup">
                     <xsl:choose>
                       <xsl:when test="count(./a:ORC)= 0">  
                          <dil:message type="error">When Order Group present, ORC Segment is required.</dil:message>
                       </xsl:when>
                                 <xsl:when test="count(./a:ORC) > 1">  
                        <dil:message type="error">Only one Order Segment (ORC) allowed per Order Group.</dil:message>
                                 </xsl:when>
                       <xsl:otherwise>
                          <xsl:apply-templates select="a:ORC" /> 
                       </xsl:otherwise>
                      </xsl:choose> 
             </xsl:for-each>      
        </dil:filterruleresult>
    </xsl:template>

    <xsl:template match="a:ORC">
           <xsl:choose>
                <xsl:when test="count(./a:ORC.1)= 0">
              <dil:message type="error">ORC-1 Order Control not found but is required.</dil:message>
                </xsl:when> 
                <xsl:when test="count(./a:ORC.1)> 1">
              <dil:message type="error">Only one Order Control (ORC-1) allowed per Order Segment.</dil:message>
                </xsl:when> 
                <xsl:when test="./a:ORC.1!='RE'">
              <dil:message type="error">ORC-1 Order Control must be equal to 'RE'.  Found '<xsl:value-of select="./a:ORC.1"/>' in message.</dil:message>
                </xsl:when>                      
                <xsl:when test="./a:ORC.1='RE'">
                    <dil:message type="info">Valid value of '<xsl:value-of select="./a:ORC.1"/>' found for Order Control (ORC-1).</dil:message>
                </xsl:when>
            </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

If nothing else, this had nesting of the statements.

Upvotes: 1

Related Questions