Saturday, February 9, 2008

Creating C# Bindings for CCR and CCD

Introduction

ASTM’s Continuity of Care Record (CCR) and HL7’s Continuity of Care Document (CCD) are two different XML schemas designed to store patient clinical summaries. While they are identical in scope (i.e., they contain the same data elements: demographics, medications, labs, etc.), the structures of the two formats are really quite different.

An analog in the Web 2.0 world is the functional overlap between RSS and Atom, the two XML-based syndication formats. Despite having the same purpose, the two formats use completely different XML tags to represent their data. Both have been widely adopted. I suspect the same will be true for CCR and CCD, so I’m not really interested in arguing their relative merits.

Instead, my interest lies in using these formats to shuttle information around between doctors and patients.

One way to facilitate this process is the creation and use of programmer-friendly objects that are bound to particular XML schemas (e.g., CCR and CCD). XML data binding, as this process is known, obviates the need for cumbersome object-to-XML conversion routines.

Here, then, is a quick tutorial on creating C# classes for both the CCR and CCD schemas…

Get the Schemas (*.xsd)

I’m not really sure how Microsoft is getting away with hosting the XSD files for both the CCR and CCD formats (I paid $100 for mine), but they are.

So, download the CCR schema, then download the CCD schema.

Save these files as CCR.xsd and CCD.xsd, respectively.

Massage the CCR.xsd file

The CCR schema seems to have been created without .NET in mind (huh?), so we have to make a few changes for the next steps to work. If you’re only interested in CCD (shame!), you can skip ahead.

Remove maxOccurs attribute from Indications and Directions

The maxOccurs attribute for these two elements causes xsd.exe to create multi-dimensional arrays (e.g., public IndicationType[][] Indications;). Simply delete the maxOccurs attribute as follows:

Find: <xs:element ref="Indications" minOccurs="0" maxOccurs="unbounded"/>
Replace: <xs:element ref="Indications" minOccurs="0"/>

Find: <xs:element ref="Directions" minOccurs="0" maxOccurs="unbounded"/>
Replace: <xs:element ref="Directions" minOccurs="0"/>

Convert name attributes to ref for all IDs elements

Instead of creating a new IDs element each time, we just want to reference the root-level IDs element. The type attribute is not required when using ref, so we can remove that as well:

Find: <xs:element name="IDs" type="IDType" minOccurs="0"
Replace: <xs:element ref="IDs" minOccurs="0"

Delete redundant IDs elements

Some elements inherit from CCRCodedDataObjectType. Delete or comment out the redundant IDs references in the OrderRxHistoryType and StructuredProductType elements.

Create Classes using xsd.exe

.NET’s secret Xml data binding weapon is a command line utility named xsd.exe, which comes with Visual Studio.

Run the following on your newly downloaded .xsd files to create corresponding C# classes:

xsd CCR.xsd /c /n:CCR
xsd CCD.xsd /c /n:CCD

The /c means generate a class (as opposed to a dataset) and the /n:CCR specifies the namespace for the code (which can be anything, really). C# code (as opposed to VB) is the default language, so we don’t have to specify that.

Massage the Generated Classes

This step is optional, but using generics makes life a lot easier.

Fire up a text editor with regular-expression search/replace and run the following:

Find: public {[^\[]+}\[\] {[^;]+};
Replace: public List<\1> \2;

This will accomplish the following, enabling the use of all the methods in .NET’s List object:

// Old (Bad)
public ContinuityOfCareRecordPatient[] Patient;
// New (Good)
public List<ContinuityOfCareRecordPatient> Patient;

Don’t forget to add using System.Collections.Generic; to the top of your .cs file.

Proof of Concept Application

Just to show how this all comes together, here’s a simple command line application that uses our newly-created, XML schema-bound, C# object in action. You can download sample CCR files from AAFP’s website.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using CCR;

namespace CCRTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ContinuityOfCareRecord ccr = Deserialize<ContinuityOfCareRecord>(@"C:\sampleCCR.xml");
                System.Console.WriteLine(ccr.CCRDocumentObjectID);
                foreach (ActorType at in ccr.Actors)
                {
                    System.Console.WriteLine("  " + at.ActorObjectID);
                }
            }
            catch (Exception ex)
            {
                while (ex != null)
                {
                    System.Console.WriteLine(ex.ToString());
                    ex = ex.InnerException;
                }
            }
            System.Console.ReadLine();
        }

        public static void Serialize<T>(T value, string pathName)
        {
            using (TextWriter writer = new StreamWriter(pathName))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(T));
                serializer.Serialize(writer, value);
            }
        }

        public static T Deserialize<T>(string pathName)
        {
            using (TextReader reader = new StreamReader(pathName))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(T));
                return (T)serializer.Deserialize(reader);
            }
        }
    }
}

19 comments:

CCR Technical Tips & Learnings said...

Great post. I have been using XML Binding in Java (JAXB) to marshall and unmarshall CCR XML. One other tip (at least it works in Java) is to change the xs:type for "ExactDateTime" from xs:string to xs:datetime. xs:string was used in the standard as the ISO8601 standard format allows for partial dates and times, yet xs:datetime does not allow partials, although this is not enforced in the JAXB binding. I will make sure those XSD changes for the CCR are part of V2.0

Unknown Waters said...

Good Afternoon.

The post seems to be really useful for implementing interoperability as per the CCHIT standers. But I am not able to Download the CCD.xsd and CCR.xsd from the suggested link. Can you please give me the proper links to download the same.
To add up, Can u give the further steps to implement the Interoperability, once the required .cs class is generated from the ccd.xsd ???
Urgent help will be highly appreciated. Urgent !!

Ford said...

I've updated the links to HealthVault's XSD files. Good luck!

Unknown Waters said...

Thanks Ford Sir for the update.

After taking the over view of the CCD.xsd provided by HealthVault, I have come up with some more questions. I hope you would help me out. I am working on the Interoperability module for CCHIT, which tests the CCD/C32 XML document generated from our system using LAIKA. As far as my understanding, they too use some type of CCD/C32 Schema to validate the data (may be a CCD.XSD for Laika). But I am not able to get such type of schema any where which is required for LAIKA. I am not able to generate the .cs class bcs of it.
Can u please guide me !
Again Urgent help will be highly appreciated. Urgent !!

Brad Hehe said...

I've recently begun working with the CCD/CCR schemas (n00b at best) and stumbled upon this posting.

I've used XSD.exe in the past to both generate classes from an XSD as well as generate an XSD from my classes...

Recently I've been using SGEN.exe to speed up Xml serialization and tried including the SGEN post-build step (a MSBUILD target) but the classes generated for the CCR are not playing nicely.

I get erros such as below:

error CS0030: Cannot convert type 'Entities.CCR.IndicationType[]' to 'Entities.CCR.IndicationType'

So that freaks me out just a tad bit to see a single entity being assigned into what should be an array... And The inverse messages appear as well...

error CS0029: Cannot implicitly convert type 'Entities.CCR.IndicationType' to 'Entities.CCR.Indication
Type[]'

Any insight? There so much noise out there on CCR/CCD but your approach was exactly what I had already planned...

I end with 2 questions.... Is the generated set of CCR entities actually valid? Seems questionable...

And can you get SGEN to work on your end? I think if the 1st issue is addressed - SGEN would fall in place thereafter...

Tom Winans said...

Any thought to creating an XSLT transformation between the two?

nipun said...

I have downloaded the CCD and CCR schemas and converted them to classes. Now, I am stuck up without knowing how to go ahead. My aim to implement interoperability feature in my application. Please guide me on how to go ahead with exporting and importing CCD documents. I would like to start from the basics please. Any help is greatly appreciated. This is very urgent.

Thank you.

alejandro_martinez_falcon said...

Hi, great post. I tried it and worked. How to get Address? I received null references

I tried for example to bind the ccr.Body.Alerts to a gridview and the column's values are:

COLUMNS
Agent
Reaction
CCR Data Object ID
Date Time
IDs
Type
Description
Status
Source
Internal CCR Link
Reference ID
Comment ID
Signature

CCR.Agent[]
CCR.Reaction[]
AL.1000590019
CCR.IDType[] CCR.CodedDescriptionType CCR.CodedDescriptionType CCR.CodedDescriptionType CCR.SourceType[]

What am I missing? Best regards.

Isha said...

that is good sample.I need further steps to convert it into html files.

mentee111 said...

Can anyone help me as to where can I get EDI file or DTD for both HL7 CCD and ASTM CCR standard documents.
Thank you.

mentee111 said...

Thanks for the post. It is really helpful.

I am trying to generate the xml based on the ccr standard. As per the instruction given in the post, i am able to create the bindings for CCR as the base.

Now, As the next step, I have to generate the final output xml. For this, what should I do?

Sumathi said...
This comment has been removed by the author.
Mike Bosch said...

I am looking for html files.

Don Rule said...

I am having a problem with the conversion to generics. First I had an issue with Lists of enums or List that the serializer threw exceptions on. When I converted those back to arrays the serialization still fails with object null reference exceptions. I've found a couple references that say that you cannot serialize generic lists that are not initialized so I wonder if you need to initialize everything -even unused segments - before serialization.

Sreenibabu said...

I am having problem with downloading the CCD schema. I searched the net but i couldn't get the schema. Can any body give the url from where I can download the Schema

Brent Miller said...

Sreenibabu , The updated url is http://developer.healthvault.com/pages/types/type.aspx?id=9c48a2b8-952c-4f5a-935d-f3292326bf54

However, I think these instructions are quite outdated since the schema no longer contains a lot of what Ford is referencing.

Any chance we could get updated instructions?

m rana said...

Medical assistants must be capable of handling intense emergency situations placed
against chaotic emotions, flying egos and anxieties. It is absolutely essential for
them to maintain a calm, patient and positive attitude to focus on keeping their
nerves intact and getting the job done.


medical assistant
medical assistant classes
medical assistant certification
medical assistant programs
medical assistant school
medical assistant courses
medical assistant courses online
medical assistant classes online

Mike Socha said...
This comment has been removed by the author.
Mike Socha said...

You can get all the working xsd's to generate your POCO via the cmd prompt xsd tool at http://www.hl7.org/documentcenter/private/standards/cda/r2/cda_r2_normativewebedition2010.zip