UNPKG

@redoxengine/redox-hl7-v2

Version:

A parser for hl7 version 2 messages. Creates json from v2 messages, and creates v2 messages from json.

213 lines (171 loc) 7.62 kB
/** * Parses an hl7 v2 message and converts to json representation * @param {String} hl7 * @return {JSON} */ module.exports = function (hl7) { var schema, messageEventKey, messageType, eventType; var segmentArray, delimiters = {}, MSHArray; var ret = {}, messageDef, segmentIndex = {}; var self = this; if (hl7.indexOf('\r\n') !== -1) { segmentArray = hl7.split('\r\n'); } else { segmentArray = hl7.split('\r'); if (segmentArray.length < 2) { segmentArray = hl7.split('\n'); } } //if delimiters other than the defaults are being used, find them //assume MSH is first segment - it's not necessarilly the first segment, but should be unless BHS segments are in play //if a customer is sending us BHS segments, we should write a batch message V2, then call this function on each message if (segmentArray[0] && segmentArray[0].substring(0,3) === 'MSH') { delimiters.field = segmentArray[0].substring(3,4); // | delimiters.component = segmentArray[0].substring(4,5); // ^ delimiters.repetition = segmentArray[0].substring(5,6); // ~ delimiters.escape = segmentArray[0].substring(6,7); // \ delimiters.subComponent = segmentArray[0].substring(7,8); // & MSHArray = segmentArray[0].split(delimiters.field); MSHArray.splice(1, 0, delimiters.field); // Add field delimiter back in to make length correct messageType = MSHArray[9].split(delimiters.component)[0]; if (messageType === 'ACK') { eventType = 'ACK'; } else { eventType = MSHArray[9].split(delimiters.component)[1]; } } else { throw new Error('Could not read MSH segment of HL7 v2 message.'); } schema = self._schema; messageEventKey = schema.structure[messageType][eventType]; messageDef = schema.messages[messageEventKey]; if (hl7.indexOf('\r\n') !== -1) { segmentArray = hl7.split('\r\n'); } else { segmentArray = hl7.split('\r'); if (segmentArray.length < 2) { segmentArray = hl7.split('\n'); } } /** * Builds an index with the segment names as the keys and the groups that segment is in as the values * @param {Object} segmentIndex The segmentIndex so far * @param {Object} messageDef The message definition we're building * @param {String} groupName The name of the current group we're processing * @return {Object} Implicitly returns the index in teh segmentIndex variable */ var buildGroupIndex = function (segmentIndex, messageDef, groupName) { for (var i = 0; i < messageDef[groupName].elements.length; i++) { if (messageDef[groupName].elements[i].segment) { if (!segmentIndex[messageDef[groupName].elements[i].segment]) { segmentIndex[messageDef[groupName].elements[i].segment] = {}; } segmentIndex[messageDef[groupName].elements[i].segment][groupName] = true; } else { buildGroupIndex(segmentIndex, messageDef, messageDef[groupName].elements[i].group); for (var segment in segmentIndex) { if (segmentIndex[segment][messageDef[groupName].elements[i].group]) { segmentIndex[segment][groupName] = true; } } } } }; buildGroupIndex(segmentIndex, messageDef, messageEventKey); // Remove any empty segments or segments not defined for this message type for (var i = 0; i < segmentArray.length; i++) { if (!segmentArray[i]) { segmentArray.splice(i, 1); i--; continue; } // If the segment is not defined in this message type, remove it if (!segmentIndex[segmentArray[i].substring(0, 3)] || !segmentIndex[segmentArray[i].substring(0,3)][messageEventKey]) { segmentArray.splice(i, 1); i--; } } /** * Unpacks a group of segments - defined as a separate function becuase we have to use recursion * @param {Array} segmentArray Array of segments * @param {Object} messageDef The definition of this message type * @param {String} groupName The name of the group we are unpacking * @return {Object} The unpacked group */ var parseGroup = function (segmentArray, messageDef, groupName) { var ret = {}, segment, segmentName, element, moveOn = true, completedKeys = {}; for (var i = 0; i < messageDef[groupName].elements.length; i++) { if (segmentArray.length === 0 && moveOn) { for (var j = i; j < messageDef[groupName].elements.length; j++ ) { element = messageDef[groupName].elements[j]; // If we're missing some required group or segment that we haven't gotten to yet if (parseInt(element.minOccurs) > 0) { if (element.segment) { throw new Error('Message is missing required segment ' + element.segment + '.'); } else { throw new Error('Message is missing required group ' + element.group + '.'); } } } return ret; } element = messageDef[groupName].elements[i]; if (moveOn) { segment = segmentArray.shift(); segmentName = segment.substring(0, 3); } // If we've already seen this key, then we must be starting a group over if (completedKeys[segmentName]) { segmentArray.unshift(segment); return ret; } // If this segment is not part of the group, add it back on and return if (!segmentIndex[segmentName] || !segmentIndex[segmentName][groupName]) { segmentArray.unshift(segment); return ret; } if (element.segment === segmentName) { // We have a match completedKeys[segmentName] = true; if (parseInt(element.maxOccurs) !== 1) { if (!ret[segmentName]) { ret[segmentName] = []; } segmentArray.unshift(segment); while (segmentArray[0] && segmentArray[0].substring(0, 3) === element.segment) { segment = segmentArray.shift(); ret[segmentName].push(self.parseSegment(segment, segmentName, delimiters)); } } else { ret[segmentName] = self.parseSegment(segment, segmentName, delimiters); } moveOn = true; } else if (element.segment && parseInt(element.minOccurs) > 0) { // We've missed a required segment throw new Error('Message is missing required segment ' + element.segment + '.'); } else if (element.segment && parseInt(element.minOccurs) === 0) { // optional segment that we can skip moveOn = false; } else if (element.group) { // We need to check the group for the segment if (segmentIndex[segmentName] && segmentIndex[segmentName][element.group]) { segmentArray.unshift(segment); // stick segment back on the array and recur if (parseInt(element.maxOccurs) !== 1) { if (!ret[element.group]) { ret[element.group] = []; } while (segmentArray.length > 0 && segmentIndex[segmentArray[0].substring(0, 3)] && segmentIndex[segmentArray[0].substring(0, 3)][element.group]) { ret[element.group].push(parseGroup(segmentArray, messageDef, element.group)); } } else { ret[element.group] = parseGroup(segmentArray, messageDef, element.group); } moveOn = true; } else if (parseInt(element.minOccurs) > 0) { throw new Error('Message is missing required group ' + element.group + '.'); } else { moveOn = false; } } } return ret; }; ret = parseGroup(segmentArray, messageDef, messageEventKey); return ret; };