ARTICLE

Application to Convert HL7 to XML

Posted by Scott Lysle Articles | XML October 22, 2012
This article describes an application used to convert HL7 messages from their raw format into a more useful XML format.
Reader Level:
Download Files:
 

Introduction

The project makes use of a class written by T. Clark to execute the actual conversion, I found the project containing this very useful class located here on the Code Project website: http://www.codeproject.com/Articles/29670/Converting-HL7-to-XML. The application included here takes this class and wraps it up in a Windows Form application. This application allows the user to open up the HL7 message in raw format, uses T. Clark's class to convert it to XML, and then displays the XML in a tree view control. The application also provides the means to save the XML file as a true XML document and, based upon a query of the derived XML, sets the default file name to the message control ID associated with the raw HL7 document.

Fake-HL7-Message.jpg
Figure 1: Application Displaying a Fake HL7 Message in a Tree View

Getting Started

Download and unzip the attached project to get started. Open the project up into Visual Studio 2012 and review its contents. In general you will find a Windows Forms application with a main application form cleverly entitled, "Form1.cs". There is also an about box listed as, "AboutApp.cs". Aside from the two forms, there is a single class, "HL7ToXmlConverter.cs" that wraps up the class written by T. Clark and mentioned earlier in the article.

The main form contains the code necessary to open the raw files, convert the files to XML using Clark's class, and to then save the raw HL7 messages having been converted into true XML format. The conversion has worked flawlessly with the HL7 messages I have encountered but you may need to alter the conversion class if you have any problems working with different sources for the raw HL7 messages.

The Code: ConvertHL7ToXml Class

As was mentioned, this class was written by a T. Clark and is sited from the URL provided previously. Clark has annotated his code and you may open the class and read that annotation to study the approach used to handle the conversion.

The Code: Form1 Class

This class puts Clark's conversion class into the context of a Windows Form based application. This form allows the user to open up the raw (non-XML format) HL7 messages (which oddly enough carry the .xml extension). Once opened and converted to XML, the class temporarily stores the true XML file, and then displays the XML document in a tree view control as is shown in Figure 1 of this document. The class also allows the user to save the file in proper XML format to the file system; in so doing, the code queries the XML document to obtain the message control ID and uses that message control ID as the default file name. The system I am using now initially saves the HL7 messages using a GUID as the file name, this GUID does not identify the records in any meaningful way and it makes evaluating message errors more difficult as finding the file needed cannot be accomplished using the GUID file name. The GUID provided as a file name never appears in any part of the message itself and therefore it is never captured nor stored into the database built from the messages; at that, if you wish to look at the raw message.

The code for the entire class is presented below; it is annotated to describe the purpose of each part of the code:

using System;
using System.Linq;
using System.Windows.Forms;
using System.Xml;
using System.IO;
using
 System.Xml.XPath; 

namespace ConvertHL7ToXml
{
    public partial class Form1 : Form
    {
        // the file to be converted; this is the HL7 message in its raw format; even
        // though it uses an xml file extension, is it not xml but rather is a 
        // pipe and character delimited file with a specific definition
        String sourceFilePath;
 
        // a temporary place to save the file after it is converted to true xml format
        String tempFilePath;
 
        // the actual xml document to be
        XmlDocument doc = new XmlDocument();  
 
        /// <summary>
        /// Constructor
        /// </summary>
        public Form1()
        {
            InitializeComponent();
 
            // define the temporary xml file storage path
            tempFilePath = Application.StartupPath + "\\temp.xml";
        }
 
 
        /// <summary>
        /// Exit the application 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ExitToolStripMenuItemClick(object sender, EventArgs e)
        {
            Application.Exit();
        }
 
 
        /// <summary>
        /// Open an existing HL7 message file and convert it to actual XML, 
        /// Then display the document in a tree view
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OpenToolStripMenuItemClick(object sender, EventArgs e)
        {
            try
            {
                // define the open file dialog, since HLy messages are saved with
                // an xml extension (even though they are not xml) we will set up
                // the dialog to allow the user to open a file with that extension
                openFileDialog1.Title = "Open HL7 Message File";
                openFileDialog1.Filter = "XML Files|*.xml";
                openFileDialog1.DefaultExt = "XML";
                openFileDialog1.FileName = string.Empty;
 
                // or you can use the dialog result
                openFileDialog1.ShowDialog();
 
                // make sure we get a file name
                if (String.IsNullOrEmpty(openFileDialog1.FileName))
                    return;
 
                // set the file path member variable
                sourceFilePath = openFileDialog1.FileName;
 
                // read the contents of the file into a stream reader
                StreamReader sr = new StreamReader(sourceFilePath);
                string hl7Message = sr.ReadToEnd();
                sr.Close();
 
                // define a string to hold the xml returned from the convert to xml call, 
                // refer to the HL7ToXmlConverter class by T. Clark for details on how 
                // the conversion works
                string xmlFileContents = HL7ToXmlConverter.ConvertToXml(hl7Message);
 
                // save the returned XML into a temporary xml file for now, then we can 
                // work with the xml to get it displayed in the treeview
                using (StreamWriter outfile = new StreamWriter(tempFilePath))
                {
                    outfile.Write(xmlFileContents);
                }
 
 
                // Clear out the treeview in case it has another xml file displayed in it
                treeXml.Nodes.Clear();
 
                // set the wait cursor
                this.Cursor = Cursors.WaitCursor;
 
                // create a new xml doc
                XmlDocument doc = new XmlDocument();
 
                try
                {
                    // load the xml doc from the temporary xml file
                    doc.Load(tempFilePath);
 
                    // set the form text to display the file name
                    this.Text = "File - " + tempFilePath;
 
                    // return the cursor to normal
                    this.Cursor = Cursors.Default;
                }
                catch (Exception ex1)
                {
                    // return the cursor to normal
                    this.Cursor = Cursors.Default;
 
                    // and describe the error to the user
                    MessageBox.Show(ex1.Message, "Error Opening File");
                    
                    // since we had an error, don't try to display the file
                    // in the treeview
                    return;
                }
 
                // open the xml doc into the treeview for inspection
                PushToTreeView(doc, treeXml.Nodes);
 
                // open all the nodes after the treeview is populated
                treeXml.ExpandAll();
 
                // restore the cursor to normal
                this.Cursor = Cursors.Default;
 
            }
            catch (Exception ex2)
            {
                // describe what went wrong in the event of an error
                MessageBox.Show(ex2.Message, "Unable to Open the Document");
            }
        }
 
 
        /// <summary>
        /// Save the current open file as an xml document
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SaveToolStripMenuItemClick(object sender, EventArgs e)
        {
            // configure the save file dialog to allow the user to save an xml file
            saveFileDialog1.Title = "Save XML File";
            saveFileDialog1.Filter = "XML Files|*.xml";
            saveFileDialog1.DefaultExt = "XML";
 
            // leave the initial file name as an empty string, we 
            // will replace that with the message control ID in 
            // a minute but we will give the user the option of saving it 
            // with a name of their choice
            saveFileDialog1.FileName = string.Empty; 
 
            // load the temporary xml document into an instance of
            // the XML document we defined with class scope
            doc.Load(tempFilePath);
 
            // create a navigator so we can find the message control ID and use that
            // as the initial file name
            XPathNavigator navigator = doc.CreateNavigator();
 
            // the message control ID is stored in the message header in the 
            // 9th position 
            string searchTerm = "//MSH/MSH.9";
            
            // look for the node indicated in the search term
            XPathNodeIterator nodes = navigator.Select(searchTerm);
 
            // iterate through the nodes to find the value in the 
            // selected node - should be one only
            while (nodes.MoveNext())
            {
                // create an instance of a node from the 
                // selected node
                XPathNavigator node = nodes.Current;
 
                // set the default file name to the value stored in the 
                // selected node
                saveFileDialog1.FileName = node.InnerXml;
            }
 
            // open the dialog and get the user's OK
            DialogResult result = saveFileDialog1.ShowDialog();
 
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                // copy the temporary file to the new location and rename it 
                // using the message control ID scraped from the xml document
                System.IO.File.Move(tempFilePath, saveFileDialog1.FileName);
            }
        }
 
 
        /// <summary>
        /// Show about box
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AboutConvertToTrueXmlToolStripMenuItemClick(object sender, 
         EventArgs e)
        {
            AboutApp f = new AboutApp();
            f.ShowDialog();
        }
 
 
        /// <summary>
        /// Populate the tree view using the xml obtained using T. Clark's xml converter class
        /// </summary>
        /// <param name="currentNode"></param>
        /// <param name="nodCollection"></param>
        private void PushToTreeView(XmlNode currentNode, TreeNodeCollection 
         nodCollection)
        {
            TreeNode insertNode = nodCollection.Add(currentNode.Name);
 
            switch (currentNode.NodeType)
            {
                case XmlNodeType.Element:
                    insertNode.Text = currentNode.Name;
                    insertNode.Tag = "Element";
                    insertNode.ImageIndex = 1;
                    break;
                case XmlNodeType.Attribute:
                    insertNode.Text = "@" + currentNode.Name;
                    insertNode.Tag = "Attribute";
                    insertNode.ImageIndex = 2;
                    break;
                case XmlNodeType.Text:
                    insertNode.Text = currentNode.Value;
                    insertNode.Tag = "Text";
                    insertNode.ImageIndex = 3;
                    break;
                case XmlNodeType.CDATA:
                    insertNode.Text = currentNode.Value;
                    insertNode.Tag = "CDATA";
                    insertNode.ImageIndex = 4;
                    break;
                case XmlNodeType.Comment:
                    insertNode.Text = currentNode.Value;
                    insertNode.Tag = "Comment";
                    insertNode.ImageIndex = 5;
                    break;
                case XmlNodeType.Entity:
                    insertNode.Text = currentNode.Value;
                    insertNode.Tag = "Entity";
                    insertNode.ImageIndex = 6;
                    break;
                case XmlNodeType.Notation:
                    insertNode.Text = currentNode.Value;
                    insertNode.Tag = "Notation";
                    insertNode.ImageIndex = 7;
                    break;
                default:
                    break;
            }
 
            // recursive processing
 
            // check the current node for attributes
            if (currentNode.Attributes != null && currentNode.Attributes.Count > 0)
            {
                // write out the attributes to the treeview
                foreach (XmlAttribute attribute in currentNode.Attributes)
                {
                    PushToTreeView(attribute, insertNode.Nodes);
                }
            }
 
            // check the current node for child nodes
            if (currentNode.HasChildNodes)
            {
                // write out the child nodes to the treeview
                foreach (XmlNode childNode in currentNode.ChildNodes)
                {
                    PushToTreeView(childNode, insertNode.Nodes);
                }
            }
 
        }
    }
}

COMMENT USING

Trending up