1 /** 2 * The contents of this file are subject to the Mozilla Public License Version 1.1 3 * (the "License"); you may not use this file except in compliance with the License. 4 * You may obtain a copy of the License at http://www.mozilla.org/MPL/ 5 * Software distributed under the License is distributed on an "AS IS" basis, 6 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 7 * specific language governing rights and limitations under the License. 8 * 9 * The Original Code is "ExampleUseTerser.java". Description: 10 * "Example Code" 11 * 12 * The Initial Developer of the Original Code is University Health Network. Copyright (C) 13 * 2001. All Rights Reserved. 14 * 15 * Contributor(s): James Agnew 16 * 17 * Alternatively, the contents of this file may be used under the terms of the 18 * GNU General Public License (the �GPL�), in which case the provisions of the GPL are 19 * applicable instead of those above. If you wish to allow use of your version of this 20 * file only under the terms of the GPL and not to allow others to use your version 21 * of this file under the MPL, indicate your decision by deleting the provisions above 22 * and replace them with the notice and other provisions required by the GPL License. 23 * If you do not delete the provisions above, a recipient may use your version of 24 * this file under either the MPL or the GPL. 25 * 26 */ 27 28 package ca.uhn.hl7v2.examples; 29 30 import ca.uhn.hl7v2.DefaultHapiContext; 31 import ca.uhn.hl7v2.HapiContext; 32 import ca.uhn.hl7v2.model.GenericMessage; 33 import ca.uhn.hl7v2.model.Message; 34 import ca.uhn.hl7v2.parser.CanonicalModelClassFactory; 35 import ca.uhn.hl7v2.parser.DefaultModelClassFactory; 36 import ca.uhn.hl7v2.parser.GenericModelClassFactory; 37 import ca.uhn.hl7v2.parser.PipeParser; 38 import ca.uhn.hl7v2.util.Terser; 39 40 /** 41 * Example code illustrating how to handle multiple versions of HL7 from one codebase 42 * 43 * @author <a href="mailto:jamesagnew@sourceforge.net">James Agnew</a> 44 * @version $Revision: 1.1 $ updated on $Date: 2011-05-22 16:52:21 $ by $Author: jamesagnew $ 45 */ 46 public class HandlingMultipleVersions { 47 48 public static void main(String[] args) throws Exception { 49 50 /* 51 * Often, you will need to handle multiple versions of HL7 from a sending system 52 * from within the same code base. Because HAPI uses different model classes for 53 * each version, this can seem difficult. 54 * 55 * Before the first example, a bit of background information that is useful. 56 * HL7 v2 is a backwards compatible standard, for the most part. New versions 57 * of the standard will deprocate old fields and segments and groups, but they never 58 * remove them entirely. They will also rename fields and groups, but this has 59 * no effect on encoded messages if they are encoded using ER7 (pipe and hat) 60 * encoding, only on the message structure objects themselves. 61 * 62 * Unfortunately, because of this renaming, it is not possible for the 63 * HAPI library to create a single version of a structure JAR which covers 64 * all versions of HL7 v2 (v2.1, v2.2, v2.3, etc). That said, it is always 65 * possible to use a HAPI message structure object to parse or encode a 66 * message of the same type from an earlier version of the standard. In 67 * other words, if you have a v2.2 ADT^A01 message, you can use the v2.3 68 * ADT_A01 structure class to parse it, and you can also use the v2.3 ADT_A01 69 * structure class to create a new v2.2 message if you are not planning on 70 * XML encoding it. 71 * 72 * The following example shows two ways of dealing with this situation. First, 73 * for this example, consider the following messages. Each is identical, aside 74 * from the version string: "2.5" and "2.3". 75 */ 76 77 String v25message = "MSH|^~\\&|ULTRA|TML|OLIS|OLIS|200905011130||ORU^R01|20169838-v25|T|2.5\r" 78 + "PID|||7005728^^^TML^MR||TEST^RACHEL^DIAMOND||19310313|F|||200 ANYWHERE ST^^TORONTO^ON^M6G 2T9||(416)888-8888||||||1014071185^KR\r" 79 + "PV1|1||OLIS||||OLIST^BLAKE^DONALD^THOR^^^^^921379^^^^OLIST\r" 80 + "ORC|RE||T09-100442-RET-0^^OLIS_Site_ID^ISO|||||||||OLIST^BLAKE^DONALD^THOR^^^^L^921379\r" 81 + "OBR|0||T09-100442-RET-0^^OLIS_Site_ID^ISO|RET^RETICULOCYTE COUNT^HL79901 literal|||200905011106|||||||200905011106||OLIST^BLAKE^DONALD^THOR^^^^L^921379||7870279|7870279|T09-100442|MOHLTC|200905011130||B7|F||1^^^200905011106^^R\r" 82 + "OBX|1|ST|||Test Value"; 83 84 String v23message = "MSH|^~\\&|ULTRA|TML|OLIS|OLIS|200905011130||ORU^R01|20169838-v23|T|2.3\r" 85 + "PID|||7005728^^^TML^MR||TEST^RACHEL^DIAMOND||19310313|F|||200 ANYWHERE ST^^TORONTO^ON^M6G 2T9||(416)888-8888||||||1014071185^KR\r" 86 + "PV1|1||OLIS||||OLIST^BLAKE^DONALD^THOR^^^^^921379^^^^OLIST\r" 87 + "ORC|RE||T09-100442-RET-0^^OLIS_Site_ID^ISO|||||||||OLIST^BLAKE^DONALD^THOR^^^^L^921379\r" 88 + "OBR|0||T09-100442-RET-0^^OLIS_Site_ID^ISO|RET^RETICULOCYTE COUNT^HL79901 literal|||200905011106|||||||200905011106||OLIST^BLAKE^DONALD^THOR^^^^L^921379||7870279|7870279|T09-100442|MOHLTC|200905011130||B7|F||1^^^200905011106^^R\r" 89 + "OBX|1|ST|||Test Value"; 90 91 /* 92 * The first (and probably better in most ways) technique is as follows. Use a model class 93 * factory called the CanonicalModelClassFactory. This class forces a specific version of 94 * HL7 to be used. Because HL7 v2.x is a backwards compatible standard, you can choose the 95 * highest version you need to support, and the model classes will be compatible with 96 * messages from previous versions. 97 */ 98 99 HapiContext context = new DefaultHapiContext(); 100 101 // Create the MCF. We want all parsed messages to be for HL7 version 2.5, 102 // despite what MSH-12 says. 103 CanonicalModelClassFactoryy.html#CanonicalModelClassFactory">CanonicalModelClassFactory mcf = new CanonicalModelClassFactory("2.5"); 104 context.setModelClassFactory(mcf); 105 106 // Pass the MCF to the parser in its constructor 107 PipeParser parser = context.getPipeParser(); 108 109 // The parser parses the v2.3 message to a "v25" structure 110 ca.uhn.hl7v2.model.v25.message.ORU_R01 msg = (ca.uhn.hl7v2.model.v25.message.ORU_R01) parser.parse(v23message); 111 112 // 20169838-v23 113 System.out.println(msg.getMSH().getMessageControlID().getValue()); 114 115 // The parser also parses the v2.5 message to a "v25" structure 116 msg = (ca.uhn.hl7v2.model.v25.message.ORU_R01) parser.parse(v25message); 117 118 // 20169838-v25 119 System.out.println(msg.getMSH().getMessageControlID().getValue()); 120 121 /* 122 * The second technique is to use the Terser. The Terser allows you 123 * to access field values using a path-like notation. For more information 124 * on the Terser, see the example here: 125 * http://hl7api.sourceforge.net/xref/ca/uhn/hl7v2/examples/ExampleUseTerser.html 126 */ 127 128 // This time we just use a normal ModelClassFactory, which means we will be 129 // using the standard version-specific model classes 130 context.setModelClassFactory(new DefaultModelClassFactory()); 131 132 // 20169838-v23 133 Message v23Message = parser.parse(v23message); 134 Terserhtml#Terser">Terser t23 = new Terser(v23Message); 135 System.out.println(t23.get("/MSH-10")); 136 137 // 20169838-v25 138 Message v25Message = parser.parse(v25message); 139 Terserhtml#Terser">Terser t25 = new Terser(v25Message); 140 System.out.println(t25.get("/MSH-10")); 141 142 /* 143 * Note that this second technique has one major drawback: Although 144 * message definitions are backwards compatible, some group names 145 * change between versions. If you are accessing a group within 146 * a complex message structure, this can cause issues. 147 * 148 * This is less of an issue for some message types where groups are 149 * not used much (e.g. ADT) 150 */ 151 152 // This works and prints "Test Value" 153 System.out.println(t23.get("/RESPONSE/ORDER_OBSERVATION/OBSERVATION(0)/OBX-5")); 154 155 // This fails... 156 // System.out.println(t25.get("/RESPONSE/ORDER_OBSERVATION/OBSERVATION(0)/OBX-5")); 157 158 // ...because this would be required to extract the OBX-5 value from a v2.5 message 159 System.out.println(t25.get("/PATIENT_RESULT/ORDER_OBSERVATION/OBSERVATION(0)/OBX-5")); 160 161 /* 162 * A third technique which may occasionally be useful is to simply use 163 * a "Generic" message structure. Generic message structures can 164 * represent anything within an HL7 message, but they don't actually 165 * model all of the intricacies of the structure within the message, 166 * but rather just model all of the data in an unstructured way. 167 */ 168 169 // Create a new context using a Generic Model Class Factory 170 context = new DefaultHapiContext(); 171 context.setModelClassFactory(new GenericModelClassFactory()); 172 173 v25message = "MSH|^~\\&|ULTRA|TML|OLIS|OLIS|200905011130||ORU^R01|20169838-v25|T|2.5\r" 174 + "PID|||7005728^^^TML^MR||TEST^RACHEL^DIAMOND||19310313|F|||200 ANYWHERE ST^^TORONTO^ON^M6G 2T9||(416)888-8888||||||1014071185^KR\r" 175 + "PV1|1||OLIS||||OLIST^BLAKE^DONALD^THOR^^^^^921379^^^^OLIST\r" 176 + "ORC|RE||T09-100442-RET-0^^OLIS_Site_ID^ISO|||||||||OLIST^BLAKE^DONALD^THOR^^^^L^921379\r" 177 + "OBR|0||T09-100442-RET-0^^OLIS_Site_ID^ISO|RET^RETICULOCYTE COUNT^HL79901 literal|||200905011106|||||||200905011106||OLIST^BLAKE^DONALD^THOR^^^^L^921379||7870279|7870279|T09-100442|MOHLTC|200905011130||B7|F||1^^^200905011106^^R\r" 178 + "OBX|1|ST|||Test Value\r" 179 + "NTE||Note for OBX(1)\r" 180 + "OBX|2|ST|||Value number 2"; 181 182 // The parser will always parse this as a "GenericMessage" 183 GenericMessage../ca/uhn/hl7v2/model/GenericMessage.html#GenericMessage">GenericMessage message = (GenericMessage) context.getPipeParser().parse(v25message); 184 185 /* 186 * A generic message has a flat structure, so you can ask for any 187 * field by only its segment name, not a complex path 188 */ 189 Terserr.html#Terser">Terser t = new Terser(message); 190 System.out.println(t.get("/OBX-5")); 191 // Prints: Test Value 192 193 /* 194 * This technique isn't great for messages with complex structures. For 195 * example, the second OBX in the message above is a part of the base structure 196 * because GenericMessage has no groups. 197 * 198 * It can be accessed using a new segment name (OBX2 instead of OBX) 199 * but this is error prone, so use with caution. 200 */ 201 System.out.println(t.get("/OBX2-5")); 202 // Prints: Value number 2 203 204 } 205 206 }