Skip to main content

Defining DTL Data Transformations for HL7

Each interface may require some number of data transformations. When you create transformations, do not use reserved package names; see Reserved Package Names.


Do not manually change HL7 escape sequences in the data transformation; InterSystems handles these automatically.

You use the Data Transformation Builder page to create data transformations (select Interoperability > Build > Data Transformations from the Home page). For general information on using this page, see Developing DTL Transformations.

The following figure shows the Data Transformation Builder page:

Source and target messages in the Data Transformation Builder

Note the following tips:

  • Have Developing DTL Transformations handy.

  • Be clear about the value you want for the create option of the data transformation class. create may have one of the following values:

    • new — Create a new object of the target type, before executing the elements within the data transformation. Any source segments that you do not explicitly assign to the target object are ignored. This is the default.

    • copy — Create a copy of the source object to use as the target object, before executing the elements within the transform.

    • existing — Use an existing object, provided by the caller of the data transformation, as the target object.

    To create a target object that is an exact copy of the source, do not use an action like this:

    <assign property='target' value='source' />

    Instead use the create='copy' attribute in the data transformation class.

  • Ensure that the data transformation class identifies the correct schema category for:

    • The sourceDocType attribute

    • The targetDocType attribute

    The schema category may be the same or different for the source and target objects.

  • Ensure that the Transform tab specifies the scripting language that you want to use for all the expressions and code actions within that data transformation. ObjectScript is the default language.

  • Use assign actions to assign HL7 segments from the source message to the target message by drag and drop operations.

    Possibly, you will want to use a combination of techniques. You can first generate a line of code by dragging, then fine-tune the code by editing the text.

  • The data transformation can refer to properties of the HL7 message object, including:

    • The class properties DocType, TypeCategory, BuildMapStatus, and Name.

    • Virtual document properties as described in Curly Bracket {} Syntax.

    Within the data transformation class code, the special variables source and target represent the respective HL7 message objects, as in the following examples:






  • Assign any literal string values, or make conditional assignments. See Syntax Rules.


    A string literal cannot include XML reserved characters. It also cannot include separator characters used by HL7.

    Also, the HL7 null mapping code "" requires special handling. The following example tests for the null mapping code in the source message and replaces it with a blank string in the target message:

    <if condition='source.{PV1:7().4}=""""""'>
    <assign property='target.{PV1:7().4}' value='""' />

    For details, see Null Mapping Codes.

  • For simple calculations, the DTL data transformation can:

    • Invoke ObjectScript utility functions

    • Use ObjectScript expressions in the DTL code action

    • Invoke the DTL sql action

    For more complex calculations, you can write your own class methods and invoke them from a code action, or from the value string in another DTL element.

  • Include descriptions of the transformation and each action.

  • Compiling the data transformation also saves it.

  • To use the DTL data transformation in the production, simply enter its full package and class name in the Transform field of a routing rule set.

Transforming Long Segment Fields

The ObjectScript method used by DTL transformations, GetValueAt, truncates HL7 segment fields at 3.6MB. Therefore, when transforming a field that is longer than 3.6MB, you cannot use the left-to-right drag action in the DTL Editor. For example, if an OBX:5 field exceeds 3.6MB, you cannot use the DTL Editor to drag the source field to the target because it will be truncated. Similarly, custom code should not call GetValueAt if you are transforming fields longer than 3.6MB.

To transform fields longer than 3.6MB, you need to use a Code action to add custom code to the transformation. This custom code must include one of the following methods in the EnsLib.HL7.SegmentOpens in a new tab class to read the value of the field from the source into a stream: GetFieldStreamRaw(), GetFieldStreamUnescaped(), or GetFieldStreamBase64()

These Get methods take 3 arguments: the stream output argument, the VDoc path of the field, and the pRemainder output argument. The pRemainder argument will be filled with all fields that come after the one being extracted. For example:

/// Segment:  OBX|1|2|3|4|5|6|7
do source.GetFieldStreamRaw(.stream, "OBX:5", .rem)
/// rem contains: |6|7

Once the custom code has the field value in a stream, it must use one of the following methods of EnsLib.HL7.SegmentOpens in a new tab to store the value in the target: StoreFieldStreamRaw(), StoreFieldStreamUnescaped(), or StoreFieldStreamBase64().

These Store methods accept three arguments: the stream to store in the field, the VDoc path of the field in which to store the stream, and a pRemainder argument. If the pRemainder argument is not specified, then all target fields after the field being stored are deleted. For example:

/// Before: OBX|1|2|3|4|5|6|7
do target.StoreFieldStreamRaw(stream, "OBX:5")
/// After: OBX|1|2|3|4|<stream> 
/// |6|7 are gone because a remainder was not specified.

If pRemainder is specified, then all fields after the one being stored will be replaced with what is in pRemainder.

/// Before: OBX|1|2|3|4|5|6|7
do target.StoreFieldStreamRaw(stream, "OBX:5", "|six|seven")
/// After: OBX|1|2|3|4|<stream>|six|seven

The target segment becomes immutable once a Store method is called, therefore it must be the last change made to the segment.

The following example shows how to extract the field from the source and store it in the target. It assumes that the custom code made edits to the target fields that come after the 3.6MB+ field before calling the Store method; this approach is required because the segment becomes immutable after calling the Store method. The sample code does not take the remainder from the source and store that in the target because that would revert edits that were already made to fields that come after the target’s long field. Therefore, you would take the stream field from the source, but the remainder from the target, and store both of those in the target:

/// previous code makes edits to fields that come after OBX:5 in the target
do source.GetFieldStreamRaw(.stream, "OBX:5")
do target.GetFieldStreamRaw(.dummy, "OBX:5", .rem)
do target.StoreFieldStreamRaw(stream, "OBX:5", rem)
///Segment is now immutable

Doing it this way prevents edits made to target fields that come after OBX:5 from being reverted or deleted.

Null Mapping Codes

There are HL7 applications that use a null mapping convention. According to this convention, the source application can send a field that consists of two consecutive double-quote characters ("") to signify if you have data in this field, delete it from your application.

Many target applications do not expect these instructions and are not designed to respond to them. If this is the case, and the double quotes are saved in the target application as actual patient data, the application users see double-quote characters on their screens. This can be annoying and misleading.

When your source application uses a null mapping convention, your HL7 data transformation can check for null mapping entries in HL7 fields and replace them with empty strings, or otherwise fill in data for the benefit of the target application.

The following <if> statement represents the simplest case. It checks for a null mapping in the source and replaces it with an empty string in the target. The <if> condition tests for the null mapping code "" using a string of 2 quoted quotes. This results in a total of 6 double-quote characters, not including the single quotes that wrap the entire condition value. (Count carefully!)

<if condition='source.{PV1:7().4}=""""""'>
<assign property='target.{PV1:7().4}' value='""' />

In the above example, the <assign> value indicates a empty string using 2 consecutive double-quote characters, with single quotes wrapping the entire value.

The following syntax is equally valid:

<if condition='source.{PV1:7().4}=&quot;&quot;&quot;&quot;&quot;&quot;'>
<assign property='target.{PV1:7().4}' value='&quot;&quot;' />

You can achieve more sophisticated goals for handling null mappings. The following example takes alternative actions based on whether there is actually a value in {PV1:3}. If the field contains a null mapping code the <true> element executes. Otherwise the <false> element executes.

<if condition='source.{PV1:3}=""""""'>
<assign property='target.{PV1:3.1}' value='source.{PV1:PatientType}' />
<assign property='target.{PV1:3.2}' value='source.{PV1:PatientType}' />
 // Dr Chart pulls subfields as follows:
 // 1 location, 2 desc, 3 room, 4 bed, 5 wing, 6 floor
<assign property='target.{PV1:3.1}' value='source.{PV1:3.1}' />
<assign property='target.{PV1:3.2}' value='source.{PV1:3.1}' />
<assign property='target.{PV1:3.3}' value='source.{PV1:3.2}'  />
<assign property='target.{PV1:3.4.1}' value='source.{PV1:3.3}'  />
<assign property='target.{PV1:3.5}' value='source.{PV1:3.1}'  />
FeedbackOpens in a new tab