Map XML to custom XML and insert into DB - rbeckman-nextgen/test-mc GitHub Wiki
Created by Jacob Brauer, last modified on Mar 26, 2009
The first tab defines that XML is being read and this channel will auto start.
2.2.4XML Inbound Directory
Clicking on the Source tab will show which directory is being read to find the XML content and the file name format required to process.
Note that the directory is defined as a relative path. It is relative from where Mirth was started.
2.2.5 Destination
On the destinations tab you can see the SQL used to insert into the PatientOS interface_messages table.
Take careful note of the URL and ensure the correct database is being accessed.
insert into interface_messages(interface_message_id, state_ref_id, transaction_type_ref_Id, message_control_id, message)
values(
nextval('interface_messages_s'), (select ref_id from refs where reference_group = 'InterfaceTransactionState' and ref_key = 'UNPROCESSED'),
(select ref_id from refs where reference_group = 'InterfaceTransactionType' and ref_key = 'RECORDTRANSACTION'),
nextval('message_control_id_seq'),${message.transformedData});
Figure 6 Destination tab
Click on the Edit Transformer link to open the Transformer. Paste your XML into the top right frame and the XML elements will be parsed and displayed in a tree below. It helps if your XML is populated with sample data.
Figure 7 Transformer incoming
2.2.6 Transformer
PatientOS is setup with Javascript to map the incoming XML elements to the PatientOS XML elements. The Outgoing tab serves to display a template XML which is populated by the Javascript. For this reason we want as little data as possible (most is not needed).
However in order to see all the fields you might like to populate you can paste into the frame the RecordTransaction.xml file from http://www.patientos.org/documentation
<RecordTransaction><PatientModel>
<IdentifierModel>
<identifierTypeRef><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></identifierTypeRef>
<identifierId>0</identifierId>
<checkDigit><![CDATA[]]></checkDigit>
<idvalue><![CDATA[]]></idvalue>
<identifierSequence>0</identifierSequence>
<sourceRef><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></sourceRef>
<activeInd>1</activeInd>
</IdentifierModel>
</PatientModel>
<FormModel>
<formStatusDtOffset>0</formStatusDtOffset>
<formStatusDt><date>20080120</date><datetime>20080120211142</datetime></formStatusDt>
<formId>0</formId>
<formDtOffset>0</formDtOffset>
<formDt><date>20080120</date><datetime>20080120211142</datetime></formDt>
<activeInd>1</activeInd>
<patientId>0</patientId>
<dialogRef><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></dialogRef>
<formTypeRef><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></formTypeRef>
<formStatusRef><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></formStatusRef>
</FormModel>
<RecordModel>
<recordDt><date></date><datetime></datetime></recordDt>
<dataTypeRef><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></dataTypeRef>
<recordId>0</recordId>
<recordDtOffset>0</recordDtOffset>
<valueUnitRef><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></valueUnitRef>
<recordItemRef><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></recordItemRef>
<valueUnit2Ref><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></valueUnit2Ref>
<valueString><![CDATA[]]></valueString>
<patientId>0</patientId>
<valueInt>0</valueInt>
<valueFlagRef><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></valueFlagRef>
<activeInd>1</activeInd>
<valueDouble>0.0</valueDouble>
<resultStatusRef><id>0</id><display></display><idvalue></idvalue><identifierSource></identifierSource></resultStatusRef>
<valueDateOffset>0</valueDateOffset>
<valueDate><date></date><datetime></datetime></valueDate>
</RecordModel>
</RecordTransaction>
When have finished mapping replace the contents with the original XML You may need to tailor the XML (for example we will add RecordDetailModel rows).
Figure 8 Outgoing data tab
The goal is to populate a) Enough of the PatientModel to match the patient - name and MRN (matching is a large topic) unless you have the patient_id from the patients table. b) A single FormModel to reflect the form c) As many RecordModel has you have fields on this virtual form. d) As many RecordDetailModel if you have details for a specific Record.
The concept of Record and RecordDetail is that the Record value is the significant value e.g. height, weight, lab results etc where as the RecordDetail are child details e.g. Clothing the patient had on when weighed or comments on the lab result.
The script is written in Javascript and obviously you need some programming skills to understand the nuances of iterating through the XML elements and building up the target XML which is this tmp structure.
2.2.7 Example Mapping
We will work with this sample XML
<Message>
<patientId>4</patientId>
<measureFrequency>300</measureFrequency>
<sensorType>motion</sensorType>
<timeStart>2008-01-18T00:00:00.000+01:00</timeStart>
<timeStop>2008-01-18T00:15:00.000+01:00</timeStop>
<commaSeparatedValues>89,22,0,0,0,0,0,0,0,2,22,24,4,12,99,140,120,94,2,0,0,0,4,5,32,4,0,0,0,0,0,0,0,0,0,0,32,12,1,0,0,3,0,0,0,2,5,0,0,0,0,1,4,9,10,4,0,0,0,0</commaSeparatedValues>
<comment>commaSeparatedValues and sensorType are real:)!</comment>
<dataUnit>hammed motion</dataUnit>
<intervalUnit>minutes</intervalUnit>
</Message>
The patientId is actually the MRN and so in the javascript we first create the MRN mapping by dragging the XML patientID over to the javascript page. If you have setup a facility other than the default you would enter the reference key (your facility name uppercase with spaces removed).
Figure 9 Drag patientid to be MRN
2.2.8 Records
On version 1.6 of Mith one way of creating the many iterations of RecordModel is to have the first defined in the template and then copy it each time to add to the XML. This section of Javascript will likely change to an easier to perform this in later version of Mirth.
But essentially we know there are 8 records needed so we loop 8 times and within the loop duplicate the first RecordModel, map the values and where possible set common values outside each if statement. Not great but it works...
2.2.9 Javascript
Note PatientOS dates must be formatted YYYYMMDD and mapped to the date component or YYYYMMDDHHMMSS and mapped to the date/time format. Rather than parsing we will hard code it to get started with testing
// map the MRN to match the patient
tmp['PatientModel']['IdentifierModel']['sourceRef']['idvalue'] = "DEFAULTFACILITY";
tmp['PatientModel']['IdentifierModel']['idvalue'] = msg['patientId'].toString();
for (i=0; i<8; i++){
startTime = "20080118000000";
stopTime = "20080118001500";
if (i==0) { // measure frequency
tmp['RecordModel'][i]['recordItemRef']['idvalue'] = "MOTIONMEASUREFREQUENCY";
tmp['RecordModel'][i]['recordItemRef']['display'] = "Motion Measure Frequency";
tmp['RecordModel'][i]['dataTypeRef']['id'] = 56990; // DOUBLE
tmp['RecordModel'][i]['valueDouble'] = msg['measureFrequency'].toString();
} else if (i==1) { // sensor type
tmp['RecordModel'][i]=tmp['RecordModel'][i-i]; // create xml element
tmp['RecordModel'][i]['recordItemRef']['idvalue'] = "MOTIONSENSORTYPE";
tmp['RecordModel'][i]['recordItemRef']['display'] = "Motion Sensor Type";
tmp['RecordModel'][i]['dataTypeRef']['id'] = 79128; // STRING
tmp['RecordModel'][i]['valueString'] = msg['sensorType'].toString();
} else if (i==2) { // timestart
tmp['RecordModel'][i]=tmp['RecordModel'][i-i]; // create xml element
tmp['RecordModel'][i]['recordItemRef']['idvalue'] = "MOTIONTIMESTART";
tmp['RecordModel'][i]['recordItemRef']['display'] = "Motion Time Start";
tmp['RecordModel'][i]['dataTypeRef']['id'] = 56992; // DATE
tmp['RecordModel'][i]['valueDate']['datetime'] = startTime;
} else if (i==3) { // timestop
tmp['RecordModel'][i]=tmp['RecordModel'][i-i]; // create xml element
tmp['RecordModel'][i]['recordItemRef']['idvalue'] = "MOTIONTIMESTOP";
tmp['RecordModel'][i]['recordItemRef']['display'] = "Motion Time Stop";
tmp['RecordModel'][i]['dataTypeRef']['id'] = 56992; // DATE
tmp['RecordModel'][i]['valueDate']['datetime'] = stopTime;
} else if (i==4) { // commaseparatedvalues
tmp['RecordModel'][i]=tmp['RecordModel'][i-i]; // create xml element
tmp['RecordModel'][i]['recordItemRef']['idvalue'] = "MOTIONHAMMEDMOTION";
tmp['RecordModel'][i]['recordItemRef']['display'] = "Motion Hammed Motion";
tmp['RecordModel'][i]['dataTypeRef']['id'] = 79128; // STRING
tmp['RecordModel'][i]['valueString'] = msg['commaSeparatedValues'].toString();
} else if (i==5) { // comment
tmp['RecordModel'][i]=tmp['RecordModel'][i-i]; // create xml element
tmp['RecordModel'][i]['recordItemRef']['idvalue'] = "MOTIONCOMMENT";
tmp['RecordModel'][i]['recordItemRef']['display'] = "Motion Comment";
tmp['RecordModel'][i]['dataTypeRef']['id'] = 79128; // STRING
tmp['RecordModel'][i]['valueString'] = msg['comment'].toString();
} else if (i==6) { // dataunit
tmp['RecordModel'][i]=tmp['RecordModel'][i-i]; // create xml element
tmp['RecordModel'][i]['recordItemRef']['idvalue'] = "MOTIONDATAUNIT";
tmp['RecordModel'][i]['recordItemRef']['display'] = "Motion Data Unit";
tmp['RecordModel'][i]['dataTypeRef']['id'] = 79128; // STRING
tmp['RecordModel'][i]['valueString'] = msg['dataUnit'].toString();
} else if (i==7) { // intervalunit
tmp['RecordModel'][i]=tmp['RecordModel'][i-i]; // create xml element
tmp['RecordModel'][i]['recordItemRef']['idvalue'] = "MOTIONINTERVALUNIT";
tmp['RecordModel'][i]['recordItemRef']['display'] = "Motion Interval Unit";
tmp['RecordModel'][i]['dataTypeRef']['id'] = 79128; // STRING
tmp['RecordModel'][i]['valueString'] = msg['intervalUnit'].toString();
}
// Common
tmp['RecordModel'][i]['recordDt']['datetime'] = startTime;
tmp['RecordModel'][i]['recordItemRef']['identifierSource'] = "PORCUPINESENSOR";
}
2.2.10 Deploy
Make sure the Outgoing Data script is
After saving the transformer changes the channel is deployed
Now drop a copy of your xml file into the oru_xml_in directory identified earlier. The channel will remove the file and any errors will be written to the log.
On the dashboard we can see an error occurred.
Selecting Events will show the log, selecting the first entry and scrolling down the exception log we find the error relates to the ns1 prefix used in the XML
After attempting some javascript tricks to remove or build the namespace the event log stopped filling with error messages. Instead by right clicking on the channel in the dashboard you can select 'View Messages'
You can select the message which errored and view the different states the message went through - before and after the transformating Javascript. On the error tab we see the namespace is still an issue.
To resolve the issue on the Summary tab we deselected 'Strip namespace from messages' and as show above the message was transformed and sent i.e. written to the interface_messages table.
2.3 PatientOS Processing
We can find our interface message in the interface_messages table to be processed.
Hello, Mirth Community! I am new to the platform (and medical/EHR integration space as well) but like it a lot so far. I have a very basic question regarding MirthConnect's capabilities to implement "channels" that perform more or less "standard SOA integration" task. For example, I need to get some custom formatted XML as a channel input, extract some fields from that XML, call one external Web service passing these extracted values in a SOAP message body, get a response, Base 64 decode the <XMLresponse> node contents and call a second external Web Service with results from the first SOAP response and get second SOAP response, then parse some output values and write them to a local DB schema. I have looked at the Mirth Administrator an noticed that there is a "Web Service Listener" to call Web Services - but it looks like it will only call "local" Mirth Web Services, not an external one (for which I need to specify WSL). I noticed also that there is a Database Writer for writing data to DB and some utilities for Base 64 encoding and decoding (FileUtil.encode(data) - but I am not sure how to use them. Is there any example of MirthChannel calling an external SOAP Web Services, processing its response, fomratting data and writing it out to a locally running DB (I have mySQL, but any JDBC freindly example will do). I know that you have very string JavaScript support in the platform - is there any "visual development" environment where I could connect operators into a workflow, at least mentally Many thanks in advance, Dan Zilberman
![]() |
Document generated by Confluence on Nov 11, 2019 08:40