Integration with Multiple Versions of BizTalk HL7 Accelerator Schemas
- by Paul Petrov
Microsoft BizTalk Accelerator for HL7 comes with multiple versions of the HL7 implementation. One of the typical integration tasks is to receive one format and transmit another. For example, system A works HL7 v2.4 messages, system B with v2.3, and system C with v2.2. The system A is exchanging messages with B and C. The logical solution is to create schemas in separate namespaces for each system and assign maps on send ports. Schematic diagram of the messaging solution is shown below:
Nothing is complex about that conceptually. On the implementation level things can get nasty though because of the elaborate nature of HL7 schemas and sheer amount of message types involved. If trying to implement maps directly in BizTalk Map Editor one would quickly get buried by thousands of links between subfields of HL7 segments. Since task is repetitive because HL7 segments are reused between message types it's natural to take advantage of such modular structure and reduce amount of work through reuse. Here's where it makes sense to switch from visual map editor to old plain XSLT. The implementation is done in three steps.
First, create XSL templates to map from segments of one version to another. This can be done using BizTalk Map Editor subsequently copying and modifying generated XSL code to create one xsl:template per segment. Group all segments for format mapping in one XSL file (we call it SegmentTemplates.xsl). Here's how template for the PID segment (Patient Identification) would look like this:
<xsl:template name="PID">
<PID_PatientIdentification>
<xsl:if test="PID_PatientIdentification/PID_1_SetIdPatientId">
<PID_1_SetIdPid>
<xsl:value-of select="PID_PatientIdentification/PID_1_SetIdPatientId/text()" />
</PID_1_SetIdPid>
</xsl:if>
<xsl:for-each select="PID_PatientIdentification/PID_2_PatientIdExternalId">
<PID_2_PatientId>
<xsl:if test="CX_0_Id">
<CX_0_Id>
<xsl:value-of select="CX_0_Id/text()" />
</CX_0_Id>
</xsl:if>
<xsl:if test="CX_1_CheckDigit">
<CX_1_CheckDigitSt>
<xsl:value-of select="CX_1_CheckDigit/text()" />
</CX_1_CheckDigitSt>
</xsl:if>
<xsl:if test="CX_2_CodeIdentifyingTheCheckDigitSchemeEmployed">
<CX_2_CodeIdentifyingTheCheckDigitSchemeEmployed>
<xsl:value-of select="CX_2_CodeIdentifyingTheCheckDigitSchemeEmployed/text()" />
</CX_2_CodeIdentifyingTheCheckDigitSchemeEmployed>
. . . // skipped for brevity
This is the most tedious and time consuming part. Templates can be created for only those segments that are used in message interchange. Once this is done the rest goes much easier.
The next step is to create message type specific XSL that references (imports) segment templates XSL file. Inside this file simple call segment templates in appropriate places. For example, beginning of the mapping XSL for ADT_A01 message would look like this:
<xsl:import href="SegmentTemplates_23_to_24.xslt" /> <xsl:output omit-xml-declaration="yes" method="xml" version="1.0" /> <xsl:template match="/"> <xsl:apply-templates select="s0:ADT_A01_23_GLO_DEF" /> </xsl:template> <xsl:template match="s0:ADT_A01_23_GLO_DEF"> <ns0:ADT_A01_24_GLO_DEF> <xsl:call-template name="EVN" /> <xsl:call-template name="PID" /> <xsl:for-each select="PD1_PatientDemographic"> <xsl:call-template name="PD1" /> </xsl:for-each> <xsl:call-template name="PV1" /> <xsl:for-each select="PV2_PatientVisitAdditionalInformation"> <xsl:call-template name="PV2" /> </xsl:for-each>
This code simply calls segment template directly for required singular elements and in for-each loop for optional/repeating elements.
And lastly, create BizTalk map (btm) that references message type specific XSL. It is essentially empty map with Custom XSL Path set to appropriate XSL:
In the end, you will end up with one segment templates file that is referenced by many message type specific XSL files which in turn used by BizTalk maps. Once all segment maps are created they are widely reusable and all the rest work is very simple and clean.