Giter VIP home page Giter VIP logo

clear-hl7-net's Introduction

clear-hl7-net

GitHub GitHub release (latest by date) GitHub last commit Nuget

.NET HL7® message generation and parsing library for the version 2.x standard

Understanding and programmatically building HL7 messages can be difficult. clear-hl7-net takes pain out of this process by exposing an object hierarchy that is well structured, clearly named, and (most importantly) strongly-typed.

HL7® is the registered trademark of Health Level Seven International, and the use of this trademark does not constitute an endorsement by HL7.

Important Notes About Version 2 of clear-hl7-net

Upgrading from Version 1.x to Version 2? Please read some important notes about Version 2 first.

Getting Started

Building clear-hl7-net

  1. Clone the source code: git clone https://github.com/davebronson/clear-hl7-net.git
  2. Open ClearHl7.sln in Visual Studio for Windows or Mac, or build from the command line

Download From NuGet

Targets

  • .NET Standard 2.0
  • .NET Standard 2.1
  • .NET 5
  • .NET 6
  • .NET 7

Supported HL7 Versions

Several versions exist for the HL7 Version 2.x messaging standard. clear-hl7-net supports the following:

  • 2.3
  • 2.31
  • 2.4
  • 2.5
  • 2.51
  • 2.6
  • 2.7
  • 2.71
  • 2.8
  • 2.81
  • 2.82
  • 2.9

Use the matching namespace to target your desired HL7 version:

using ClearHl7.V282;
using ClearHl7.V282.Segments;
using ClearHl7.V282.Types;

HL7 Messages, Conceptually

HL7 Version 2.x messages consist of a collection of segments, with the MSH segment being required and appearing first. Each segment is presented as a string on a single line, as a series of field values that are delimited with special characters indicating the position and nesting of each value. Conceptually simple enough, but not very human-readable, and confusing to build correctly without the right tool. clear-hl7-net represents this composition using a hierarchical class structure that is easy to interact with. Pseudo example:

  • Message
    • MSH Segment
      • Type
      • Type
    • PID Segment
      • Type
      • Type Collection
      • Type
    • PV1 Segment
      • Type
      • Type Collection
      • Type
        • Sub-Type (just a nested Type)
      • Type
        • Sub-Type

Example: Building a Message

Let's build a sample Message containing the standard MSH Segment, and accompanied by PID, PVI, and Zxx Segments.

using ClearHl7;
using ClearHl7.Helpers;
using ClearHl7.Serialization;
using ClearHl7.V282;
using ClearHl7.V282.Segments;
using ClearHl7.V282.Types;

// [...]

// Build a new HL7 message
var message = new Message
{
    Segments = new ISegment[]
    {
        // Add an MSH segment - Message Header
        new MshSegment
        {
            SendingApplication = new HierarchicDesignator { NamespaceId = "Company EHR" },
            SendingFacility = new HierarchicDesignator { NamespaceId = "Company ABC" },
            ReceivingApplication = new HierarchicDesignator { NamespaceId = "HIE Application" },
            ReceivingFacility = new HierarchicDesignator { NamespaceId = "State HIE" },
            DateTimeOfMessage = DateTime.UtcNow,
            MessageControlId = "1234567890",

            // ADT message, Admit/visit notification
            MessageType = new MessageType { MessageCode = "ADT", TriggerEvent = "A01", MessageStructure = "ADT_A01" },

            // Production, Current processing transmitted at intervals
            ProcessingId = new ProcessingType { ProcessingId = "P", ProcessingMode = "T" }
        },

        // Add a PID segment - Patient Identification
        new PidSegment
        {
            Ordinal = 1, // Set the ordinal in each segment to indicate the ordering within a message
            SetIdPid = 1,
            PatientIdentifierList = new ExtendedCompositeIdWithCheckDigit[]
            {
                // Patient internal identifier
                new ExtendedCompositeIdWithCheckDigit { IdNumber = "P12345", IdentifierTypeCode = "PI" },

                // Patient external identifier
                new ExtendedCompositeIdWithCheckDigit { IdNumber = "A98765", IdentifierTypeCode = "PT" }
            },
            PatientName = new ExtendedPersonName[]
            {
                new ExtendedPersonName
                {
                    FamilyName = new FamilyName { Surname = "Anderson" },
                    GivenName = "Andy"
                }
            },
            DateTimeOfBirth = DateTime.Parse("1/1/1950 4:52:00"),
            PatientAddress = new ExtendedAddress[]
            {
                // Home address
                new ExtendedAddress
                {
                    StreetAddress = new StreetAddress { StreetOrMailingAddress = "123 Main Street" },
                    City = "Anywhere",
                    StateOrProvince = "FL",
                    ZipOrPostalCode = "33333"
                },

                // Alternate address
                new ExtendedAddress
                {
                    StreetAddress = new StreetAddress { StreetOrMailingAddress = "987 1st Avenue" },
                    City = "Somewhere",
                    StateOrProvince = "FL",
                    ZipOrPostalCode = "33333"
                }
            },
            PatientDeathIndicator = "N", // No
            PatientTelecommunicationInformation = new ExtendedTelecommunicationNumber[]
            {
                // Telephone
                new ExtendedTelecommunicationNumber { TelephoneNumber = "777-888-9999", TelecommunicationEquipmentType = "PH" },

                // Cellular or Mobile Phone
                new ExtendedTelecommunicationNumber { TelephoneNumber = "333-444-5555", TelecommunicationEquipmentType = "CP" }
            }
        },

        // Add a PV1 segment - Patient Visit
        new Pv1Segment
        {
            Ordinal = 2,
            PatientClass = new CodedWithExceptions { Identifier = "I" }, // Inpatient
            AdmissionType = new CodedWithExceptions { Identifier = "E" }, // Emergency
            ReadmissionIndicator = new CodedWithExceptions { Identifier = "R" }, // Re-admission
            AmbulatoryStatus = new CodedWithExceptions[]
            {
                new CodedWithExceptions { Identifier = "A2" }, // Ambulates with assistive device
                new CodedWithExceptions { Identifier = "A5" }, // Vision impaired
                new CodedWithExceptions { Identifier = "A6" }, // Hearing impaired
                new CodedWithExceptions { Identifier = "B3" }, // Amputee
            },
            AdmitDateTime = DateTime.Parse("1/1/2020 13:35:12"),
            CurrentPatientBalance = 1432.87M
        },

        // Add a Zxx segment (custom segment -> "ZPD")
        new ZxxSegment
        {
            Ordinal = 3,
            SegmentSuffix = "PD", // Use PD suffix for 'Patient Details'
            SegmentItems = new IType[]
            {
                new CodedWithExceptions { Identifier = "GREEN" },
                new ExtendedTelecommunicationNumber { CommunicationAddress = "[email protected]" },
                new CodedWithExceptions { Identifier = "Code1234" },
                new Text { Value = StringHelper.Escape("Anderson & Sons ~ Piano & Drywall Repair") } // String input? -> Then escape HL7 delimiters
            }
        }
    }
};

// there are two ways that you can serialize the structure into HL7 'pipehat' format:

// 1) Call MessageSerializer.Serialize()
// **********************************
string output = MessageSerializer.Serialize(message);

// 2) Call ToDelimitedString() on the Message instance
// **********************************
string output = message.ToDelimitedString();

// Outputs the following:
//     MSH|^~\&|Company EHR|Company ABC|HIE Application|State HIE|20201209022616||ADT^A01^ADT_A01|1234567890|P^T|2.8.2
//     PID|1||P12345^^^^PI~A98765^^^^PT||Anderson^Andy||19500101045200||||123 Main Street^^Anywhere^FL^33333~987 1st Avenue^^Somewhere^FL^33333|||||||||||||||||||N||||||||||777-888-9999^^PH~333-444-5555^^CP
//     PV1||I||E|||||||||R||A2~A5~A6~B3|||||||||||||||||||||||||||||20200101133512||1432.87
//     ZPD|GREEN|^^^[email protected]|Code1234|Anderson \T\ Sons \R\ Piano \T\ Drywall Repair

Example: Parsing an HL7 'pipehat' String into a ClearHl7 Message

We've received an HL7 message string. Let's deserialize it into a Message instance for integration elsewhere.

using ClearHl7;
using ClearHl7.Serialization;
using ClearHl7.V282;

// [...]

string messageString = YourApp.ReceiveMessage();

// There are two ways that you can deserialize:

// 1) Call MessageSerializer.Deserialize(), which returns
//    a new Message instance
// **********************************
//    I don't know the HL7 version:
IMessage message = MessageSerializer.Deserialize(messageString);
//    I do know the HL7 version, or want to force a specific version:
IMessage message = MessageSerializer.Deserialize<V282.Message>(messageString);

// 2) Call FromDelimitedString() on an existing Message
//    instance, which performs the conversion in-place
// **********************************
IMessage message = new Message();
message.FromDelimitedString(messageString);

Example: Detecting the HL7 Version for a Message String

Let's detect the HL7 version that is being used in an HL7 message string.

using ClearHl7;
using ClearHl7.Helpers;

// [...]

// Where messageString = "MSH|^~\&||||||||||2.3||[...]"
string messageString = YourApp.ReceiveMessage();

// Get the version
Hl7Version version = MessageHelper.DetectVersion(messageString);

// Returns:
//     The enumeration value Hl7Version.V230

Example: Creating an Instance of a ClearHl7 Message for a Specific Version

Let's create a Message instance with a specific HL7 version.

using ClearHl7;
using ClearHl7.Helpers;

// [...]

// Create a new Message instance for V2.8.1
IMessage message = MessageHelper.NewInstance(Hl7Version.V281);

// Returns:
//      An instance of ClearHl7.V281.Message

Customizing

Delimiter Characters

The HL7 specification calls out default delimiters to use for fields (pipe |), components (caret ^), subcomponents (ampersand &), escaping (backslash \), and repetition (tilde ~). Most will use these defaults. But if the consumer of your messages supports it, it is possible to define your own delimiters.

using ClearHl7;

// [...]

Configuration.FieldRepeatSeparator = "%"; // Use percent symbol
Configuration.SubcomponentSeparator = "/"; // Use forward slash

// Reset the separator configuration properties to their
// default, recommended values
Configuration.ResetSeparators();

Using the ClearHl7.Codes Component

There are code systems published as part of the HL7 specification, which are recommended for use in your messages. You have flexibility, of course, to use any coded values that you and your message consumer might agree upon. To easily access the defined codes you may utilize the ClearHl7.Codes component, which contains enumerations for each.

using ClearHl7.Codes.V282;
using ClearHl7.Codes.V282.Helpers;

// [...]

var helper = new EnumHelper();

// Example using the MaritalStatus and YesNoIndicator codes
// You must call EnumHelper.EnumToCode() to receive the
// actual coded string for a given enum value
pidSegment.MaritalStatus = new CodedWithExceptions
{
    Identifier = helper.EnumToCode(CodeMaritalStatus.Married)
};
pidSegment.MultipleBirthIndicator = helper.EnumToCode(CodeYesNoIndicator.No);

Anything Else?

  • Segments, Types, and collections are not automatically initialized for you. You must manually instantiate each object that you're going to read/write to. But be a good steward of machine resources, and only instantiate objects that you'll interact with.
  • Collection properties are all implemented with the IEnumerable interface to provide you with wide flexibility in the type of collection that you pass into the class. The example above shows usage of simple arrays, but you can use more complex types like List, etc.
  • Segments can be built and added to a Message in any order. Just set the Segment.Ordinal property for each to specify the ordering in the final output. And remember that the MSH Segment is required, and must appear first.
  • The HL7 spec calls out that segments shall be terminated with a single carriage return (a.k.a. \r or (char)13). ClearHl7 supports only this charater. If you receive messages that use alternate line terminators, you must perform a programmatic replace on those alternate terminators before attempting to deserialize with ClearHL7, so that you may receive the expected result.
  • Any string input that may contain one of the utilized delimiter characters should be escaped with ClearHl7.Helpers.StringHelper.Escape(). See the Zxx/ZPD segment in the example above.
  • Any Message, Segment, or Type can be serialized to an HL7 pipehat string by calling Serialize() or ToDelimitedString(). Utilize the ClearHl7.Serialization namespace.
  • Any Message, Segment, or Type can be instantiated and populated using an HL7 pipehat string by calling Deserialize() or FromDelimitedString(). Utilize the ClearHl7.Serialization namespace.

Contributing

We welcome code and documentation contributions!

Complex Changes

Please discuss with the repo owners before making the change. We would like to talk about motivations for the change, and go over your design decisions.

Simple Changes

Fork davebronson/clear-hl7-net.git and begin development from your own fork.

License

clear-hl7-net is licensed under the MIT License.

clear-hl7-net's People

Contributors

davebronson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

clear-hl7-net's Issues

Improve Segment and Type property documentation to include a pointer the relevant Codes enum, where appropriate

Property documentation in the Segment and Type classes should be updated to include a pointer to the relevant ClearHl7.Fhir.Codes enumeration, where appropriate. This would be a good opportunity to verify and update the existing hl7.org URL that's currently there. Example:

/// <summary>
/// ABS.2 - Transfer Medical Service Code.
/// </summary>
/// <remarks>
/// Suggested Value:
/// ClearHl7.Fhir.Codes.V282.CodeHospitalService enum
/// https://www.hl7.org/fhir/v2/0010
/// </remarks>
public CodedWithExceptions TransferMedicalServiceCode { get; set; }

Implement methods for HL7 Type classes that aid in initializing instance values

It would be helpful in some scenarios to simplify the initialization of an HL7 Type class instance. Take the following snippet where we add a new address to a PatientAddress collection:

PatientAddress = new ExtendedAddress[]
{
    new ExtendedAddress
    {
        StreetAddress = new StreetAddress { StreetOrMailingAddress = "123 Main Street" },
        City = "Anywhere",
        StateOrProvince = "FL",
        ZipOrPostalCode = "33333"
    }
}

In predictable scenarios it would be simpler to:

PatientAddress = new ExtendedAddress[]
{
    new ExtendedAddress().WithInit("123 Main Street", "Anywhere", "FL", "33333")
}

or:

PatientAddress = new ExtendedAddress[]
{
    extendedAddressRepo.Create("123 Main Street", "Anywhere", "FL", "33333")
}

Other obvious examples that could benefit from this include: ExtendedPersonName (first + last name), PersonLocation (Room + Bed + Facility).

V251.ObxSegment.ObservationValue uses an incorrect type

In V251, the property ObxSegment.ObservationValue is incorrectly defined as ChannelDefinition. This needs to be changed to IEnumerable<string>.

  • Update property type.
  • Update ToDelimitedString() method.
  • Add new section to the project markdown file (close to the top) that will contain release notes. Since this is going to be a breaking change, it will need to be mentioned there.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.