0
votes

I am generating the following XML (fragment shown here):

<ns1:OBX>
  <ns1:OBX.1>2</ns1:OBX.1>
  <ns1:OBX.2>CE</ns1:OBX.2>
  <ns1:OBX.3>
     <ns1:CE_OBS.1>
        <ns1:CM_OBS.1>aaa</ns1:CM_OBS.1>
     </ns1:CE_OBS.1>
  </ns1:OBX.3>
  <ns1:OBX.5>
     <ns1:CE.1>N</ns1:CE.1>
  </ns1:OBX.5>
</ns1:OBX>
<ns1:OBX>
  <ns1:OBX.1>2</ns1:OBX.1>
  <ns1:OBX.2>DT</ns1:OBX.2>
  <ns1:OBX.3>
     <ns1:CE_OBS.1>
        <ns1:CM_OBS.1>bbb</ns1:CM_OBS.1>
     </ns1:CE_OBS.1>
  </ns1:OBX.3>
  <ns1:OBX.5>
     <ns1:CE.1>20011231</ns1:CE.1>
  </ns1:OBX.5>
</ns1:OBX>

I am trying to populate ns1:OBX.1 with the sequence number of the OBX segments (there can be about 15-25 of these nodes). In the XSL, in each OBX mapping I put:

<ns1:OBX>
  <ns1:OBX.1>
    <xsl:value-of select='count("preceding:://ns1:OBX")+1'/>
  </ns1:OBX.1>
 ........

However, this always generates a value of 2. Could you please let me know what is wrong here?

Including more of the stylesheet here:

<xsl:template match="/">
<ns1:ADT_A01>
  <xsl:attribute name="Standard">
    <xsl:text disable-output-escaping="no">HL7</xsl:text>
  </xsl:attribute>
  <xsl:attribute name="Version">
    <xsl:text disable-output-escaping="no">2.3</xsl:text>
  </xsl:attribute>
   ...........   
   ........... 
  <xsl:for-each select="/inp1:XXXProfileMsg/inp1:XXXProfileData/inp1:exStayIndicator">
    <ns1:OBX>
      <ns1:OBX.1>
        <xsl:number/>
      </ns1:OBX.1>
      <ns1:OBX.2>
        <xsl:text disable-output-escaping="no">CE</xsl:text>
      </ns1:OBX.2>
      <ns1:OBX.3>
        <ns1:CE_OBS.1>
          <ns1:CM_OBS.1>
            <xsl:text disable-output-escaping="no">aaa</xsl:text>
          </ns1:CM_OBS.1>
        </ns1:CE_OBS.1>
      </ns1:OBX.3>
      <ns1:OBX.5>
        <ns1:CE.1>
          <xsl:value-of select="."/>
        </ns1:CE.1>
      </ns1:OBX.5>
    </ns1:OBX>
  </xsl:for-each>
  <xsl:for-each select="/inp1:XXXProfileMsg/inp1:XXXProfileData/inp1:exStayDate">
    <xsl:if test=".">
      <ns1:OBX>
        <ns1:OBX.1>
            <xsl:number/>
        </ns1:OBX.1>
        <ns1:OBX.2>
          <xsl:text disable-output-escaping="no">DT</xsl:text>
        </ns1:OBX.2>
        <ns1:OBX.3>
          <ns1:CE_OBS.1>
            <ns1:CM_OBS.1>
              <xsl:text disable-output-escaping="no">bbb</xsl:text>
            </ns1:CM_OBS.1>
          </ns1:CE_OBS.1>
        </ns1:OBX.3>
        <ns1:OBX.5>
          <ns1:CE.1>
            <xsl:value-of select='xp20:format-dateTime(.,"[Y0001][M01][D01]")'/>
          </ns1:CE.1>
        </ns1:OBX.5>
      </ns1:OBX>
    </xsl:if>
  </xsl:for-each>
  <xsl:for-each select="/inp1:XXXProfileMsg/inp1:XXXProfileData/inp1:releaseType">
    <ns1:OBX>
       <ns1:OBX.1>
          <xsl:value-of select='count(preceding::ns1:OBX) + 1'/>
        </ns1:OBX.1>
      <ns1:OBX.2>
        <xsl:text disable-output-escaping="no">CE</xsl:text>
      </ns1:OBX.2>
      <ns1:OBX.3>
        <ns1:CE_OBS.1>
          <ns1:CM_OBS.1>
            <xsl:text disable-output-escaping="no">reltyp</xsl:text>
          </ns1:CM_OBS.1>
        </ns1:CE_OBS.1>
      </ns1:OBX.3>
      <ns1:OBX.5>
        <ns1:CE.1>
          <xsl:value-of select="."/>
        </ns1:CE.1>
      </ns1:OBX.5>
    </ns1:OBX>
  </xsl:for-each>
  ............
  ............
</ns1:ADT_A01>

1
Evidently, more information is required. The whole xsl:template element from which that fragment was extracted, at least. - John Bollinger
I suspect you want to use <xsl:value-of select="position()"/>. I cannot be sure, because (1) your code cannot be run as provided (see: stackoverflow.com/help/mcve) and (2) you are not showing us the expected result. - michael.hor257k
No, he doesn't want the context position if he wants to sequentially enumerate all the ns1:OBX elements, which he collects into three separate groups. - John Bollinger
@JohnBollinger If (as he says) he wants to numerate them by their position in the output tree, then he'd be much better off using position() - or, possibly, position() + $countPreviousGroup. This not only cuts off any dependency on the original document count and order, it's also a whole lot faster than having each item count all the preceding items all over again, in a triangular number progression. - michael.hor257k
@michael.hor257k He cannot use position(), not even with keeping track of a previous group count, to directly transform his source document. Going by the template he provided, the numbers he wants do not necessarily correspond to the context positions (in document order) of any possible sequence of nodes from the source document. He could, however, use position() in a two-pass approach similar to the one in my answer, and that would be more efficient. Since he's only dealing with numbering about 15-25 nodes, however, it probably doesn't matter. - John Bollinger

1 Answers

0
votes

My original answer pointed out problems in the posted xsl-valueof element. Commentary eventually ferreted out that the questioner wanted to count generated nodes that did not have a clear a priori sequence relationship with the input nodes. So ....

The original template is far more redundant than it needs to be, but that's not my concern. If you are transforming nodes from the input document in a way that produces result nodes in a different order than the corresponding input nodes, then the best solution I see is to perform two passes. You can do that within the same XSLT run, however, by collecting the results of the first pass into a temporary document (fragment) and then transforming that.

Something like this:

<xsl:variable name='obx-nodes'>
  <xsl:for-each select="/inp1:XXXProfileMsg/inp1:XXXProfileData/inp1:exStayIndicator">
    <ns1:OBX>
    <ns1:OBX.1/>
    <ns1:OBX.2>
      <xsl:text disable-output-escaping="no">CE</xsl:text>
    </ns1:OBX.2>
  ...
  </xsl:for-each>
  <xsl:for-each select="/inp1:XXXProfileMsg/inp1:XXXProfileData/inp1:exStayDate">
    <xsl:if test=".">
      <ns1:OBX>
      <ns1:OBX.1/>
      <ns1:OBX.2>
        <xsl:text disable-output-escaping="no">DT</xsl:text>
      </ns1:OBX.2>
      ...
    </xsl:if>
  </xsl:for-each>
  ...
</xsl:variable>

<xsl:apply-templates select='$obx-nodes' />

which would be paired with a couple of additional templates:

<xsl:template match='@*|node()'>
  <xsl:copy>
    <xsl:apply-templates select='@*|node()'/>
  </xsl:copy>
</xsl:template>

<xsl:template match='ns1:OBX.1'>
  <ns1:OBX.1>
    <xsl:value-of select='count(preceding::ns1:OBX)'/>
  </ns1:OBX1>
</xsl:template>