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);
}
}
}
}