Sunday 14 May 2023

Manipulating XML Messages with C# in HL7 Soup’s Integration Host

Are you looking for a way to manipulate XML messages using HL7 Soup’s Integration Host using c#? Look no further! In this post, we’ll show you how to use the code below to do just that.

In this sample, we'll demonstrate with XDocument, but XmlDocument will work fine too.

First, make sure that the System.Xml.Linq.dll file is correctly referenced in your code. If you can’t find it, don’t worry - the code will still work, but you may see red squiggly lines in the editor. These can be ignored.

Next, get the destination message using the activityInstance.Message property. Then, load the incoming message into an XDocument object using System.Xml.Linq.XDocument.Parse().

From there, you can update the XML as needed. In our example, we add a new ValidFrom element to the root of the document. Once you’ve made your changes, get the updated XML using the ToString() method and set it as the new text for the root element.

Here’s the full code:

//Note, the next line might need redirecting to the file System.Xml.Linq.dll on your computer. 
//Another common location is C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.Xml.Linq.dll
//Even if you can't find it, don't worry. The code will still work, but you will just see red squiggly lines in the editor.  These can be ignored - it's only the editor that cannot find them.
#r "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\System.Xml.Linq.dll"

//Get the destination message
var destinationMessage = activityInstance.Message; //Use IHL7Message for HL7 Messages and IMessage for other message types

string xmlIn = workflowInstance.ReceivingActivityInstance.Message.Text;
workflowInstance.SetVariable("xmlIn",xmlIn); //put into variable to help debugging.  Remove once working

//Load incoming message into an XDocument object
System.Xml.Linq.XDocument doc = System.Xml.Linq.XDocument.Parse(xmlIn);

//Update as you see fit.
doc.Root.Add(new System.Xml.Linq.XElement("ValidFrom", "2021-06-25T11:45:22Z"));

//Get the updated XML
string xmlString = doc.ToString();

workflowInstance.SetVariable("xmlOut",xmlString);//put into variable to help debugging.  Remove once working

//Set the root element to be the new XML text.

We hope this helps you manipulate XML messages with HL7 Soup’s Integration Host! Let us know if you have any questions or comments. 

Thursday 23 March 2023

Execute a database Sub Query on a Database to generate HL7 message with multiple rows of OBX's.

Are you looking to execute a database query in HL7 Soup's Integration Host and generate an HL7 message from it? Well, I have good news for you! It's super easy. But what if you needed to retrieve multiple rows of data to generate OBX results or similar (RXA, DG1 etc)? This scenario is certainly a little fiddlier as you need to execute a sub-query and loop over those results too in order to generate the segments. In addition, you don't know how many OBX fields might be needed, so you'll have to create them manually.

Thankfully, Integration Host has a very handy Add Segment Transformer, but it can only access the data in the outer query. So, it's one of those places where code is the best option.

In this post, I'll show you how to do it using a Code Transformer, but keep in mind that there are many possible ways you could also achieve this.

Let's start by executing a sub-query to retrieve the patient details. Once we have those details, we'll loop over the results and generate the OBX segments accordingly. Here's the code snippet that does just that:

#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.1\System.Data.dll"
using System.Data.SqlClient;

IHL7Message destinationMessage = (IHL7Message)activityInstance.Message; //Use IHL7Message for HL7 Messages and IMessage for other message types
IMessage sourceMessage = workflowInstance.ReceivingActivityInstance.Message;

string patientId = sourceMessage.GetValueAtPath("[0]"); //returned patient ID from my database
string connectionString = @"Server=MyServer\sqlexpress;Database=MyDB;User Id=MyUser;Password=abc123;";

using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = connection.CreateCommand())
    //Get the vax records 
    command.CommandText =$@"Select * from Vax Where PatientID = {patientId}";
    using(SqlDataReader reader = command.ExecuteReader())
        int c = 0;
        while (reader.Read())
            //Get the values from the Vax table
            string administeredDate = reader.IsDBNull(0) ? null : reader.GetString(0);
            string vaccineType = reader.IsDBNull(1) ? null : reader.GetString(1);
            decimal? amount = reader.IsDBNull(2) ? null : (decimal?)reader.GetDecimal(2);
            string units = reader.IsDBNull(3) ? null : reader.GetString(3);
            //insert the values as a new HL7 Segment line

Thursday 16 March 2023

How to update HL7 messages in a loop with HL7 Soup

 When working with HL7 messages, it is often necessary to modify certain segments within the message. However, when dealing with a large number of segments, it can be difficult to ensure that the message is not modified in unexpected ways during the modification process. This is where the BeginUpdate and EndUpdate methods come in handy.

Consider the following code snippet:

//Get the current message we are writing as an HL7 Message IHL7Message destinationMessage = (IHL7Message)activityInstance.Message; //Get all the IN1 Sgments var in1 = destinationMessage.GetSegments("IN1"); //Set BeginUpdate as we are updating multiple Segments and don't want our list to change untill we complete destinationMessage.BeginUpdate(); //Loop over all the IN1 segments foreach (var seg in in1) { //remove the individual Segment destinationMessage.RemoveSegment(seg); } //Update the message destinationMessage.EndUpdate();

In this code, we retrieve the current message being processed as an HL7 message object called destinationMessage. We then retrieve all the segments in the message that are of type IN1, which is typically used to store insurance information.

Next, we call the BeginUpdate method on the message object to indicate that multiple segments will be updated, and the message should not be changed until the updates are complete. We then use a foreach loop to iterate over each IN1 segment in the message, and call the RemoveSegment method on the message object to remove each individual IN1 segment from the message.

Finally, after all the IN1 segments have been removed, we call the EndUpdate method on the message object to indicate that the updates are complete, and the message can be modified again.

Without the BeginUpdate and EndUpdate methods, it is possible that the message could be modified unexpectedly during the loop, causing issues with subsequent iterations. For example, if we did not use these methods, only the first item would be deleted as the foreach would then reference another message, and the remaining IN1 segments would not be removed.

In summary, when working with HL7 messages and modifying segments within them, it is important to use the BeginUpdate and EndUpdate methods to prevent unexpected modifications to the message. These methods ensure that the message is not updated until all modifications are complete, allowing for safe and efficient modification of large numbers of segments within the message.

Remove multiple segments from an HL7 message with HL7 Soup

 If you're working with HL7 messages, you may need to remove multiple segments from a message.

However, if you do it in a loop, you find that only the first item is removed.  This is because the delete causes the message to be recreated, and all remaining segments in the loop reference a different message instance.

Try this instead:

//Get the current message we are writing as an HL7 Message
IHL7Message destinationMessage = (IHL7Message)activityInstance.Message; 

//Get all the IN1 Sgments
var in1 = destinationMessage.GetSegments("IN1");

//Set BeginUpdate as we are updating multiple Segments and don't want our list to change until we complete

//Loop over all the IN1 segments
foreach (var seg in in1)
      //remove the individual Segment  

//Update the message

The above example removes all the IN1 segments from an HL7 message. The code first gets the current message as an HL7 message and then gets all the IN1 segments. It then sets BeginUpdate as it is updating multiple segments and doesn’t want the list to change until it completes. It then loops over all the IN1 segments and removes the individual segment. Finally, it updates the message.

Tuesday 28 February 2023

Use HL7 Soup Integration Host as an endpoint for transformations

HL7 Soup Integration Host can be used as an endpoint to perform quick transformations of HL7, JSON, FHIR, CSV & XML messages by simply configuring a few settings in your Integration Host client.

1. Configure an HTTP receiver to be your endpoint. Set up your bindings if you'd like to use variables in your transformations and configure a dummy JSON response like: {"response": "success"} in the message template.

2. Add a run code activity. We won't actually run any code in this activity but it allows us to run our transformations.

3. Empty the code window and add your transformed message to the message template. If you're not sure about transformations, the HL7 Soup team have a library of great tutorials to get you started.

4. Go back to your receiver activity. Click the transformers tab and drag the response variable from the Destination (right hand side) box into the Transformers (middle) box. Select your new transformer and right click in the Source Path down the bottom. Select Insert Activity Message and then Run Code Sent Message.

And you're done! Now when you send a request to that HTTP receiver it will perform your transformation on the posted data and return the transformed data in the HTTP response!

Saturday 25 February 2023

Chat GPT jokes about HL7

 Once upon a time, there was a hospital that decided to introduce a new soup on its menu. It was called "HL7 soup" and it promised to be a game-changer for hospital nutrition. But as it turned out, the soup was a lot more complicated than anyone had anticipated.

The first person to try the soup was Dr. Smith, who was on call that evening. He sat down at the cafeteria table and took a sip of the soup. Suddenly, he was hit with a barrage of information - patient records, lab results, medication lists - it was all flooding into his brain.

Dr. Smith's eyes widened in surprise. "What the heck is in this soup?" he exclaimed.

"It's HL7 soup!" replied the cafeteria worker with a smile.

Dr. Smith shook his head in disbelief. "I don't think I can handle this much data right now. Can I just have a regular chicken noodle soup, please?"

But the cafeteria worker insisted that HL7 soup was the way to go. "Trust me, Dr. Smith, this soup will change your life," she said.

So, Dr. Smith took another sip of the soup. And sure enough, he started to feel a strange sensation in his stomach. It was like his body was trying to parse and process all of the information that was coming in. He began to sweat, and his heart rate started to increase.

Suddenly, Dr. Smith keeled over, unconscious. The cafeteria worker rushed to his side, calling for help.

The hospital staff quickly discovered that the soup had been created using an HL7 data model, which was designed to help hospitals exchange information between systems. But somehow, the soup had gone awry, and it was causing patients to experience a deluge of data overload.

From that day forward, the hospital decided to stick with more traditional soup recipes. And Dr. Smith recovered just fine, but he still couldn't look at a bowl of soup without feeling a little bit queasy.

The moral of the story? When it comes to hospital nutrition, sometimes it's best to keep it simple. And if you're going to experiment with new recipes, maybe leave the HL7 data models out of the kitchen.

Tuesday 14 February 2023

HL7 Soup XML attributes

HL7 Soup supports attributes in the expressions, however, the binding tree doesn’t show them as bindable entities.  This means you have to manually type in the path that includes the attribute address.  You can do that with an @ followed by the attribute name.

For example:

    <ID ext=”test”>1000</ID>


The path for the extension attribute would be



Note you still need to have a binding and not just text.  So the easiest way is to bind to the field, then manually edit the path. You can do this in the transformers.