MohawkMEDIC / everest

The Everest Framework is designed to ease the creation, formatting, and transmission of HL7v3 and CDA structures with remote systems.
Apache License 2.0
38 stars 22 forks source link

Multiple Race Codes in CDA #2

Open LRFalk01 opened 7 years ago

LRFalk01 commented 7 years ago

I'm having a difficult time getting this to work. I've reviewed the following pages: https://everest.codeplex.com/discussions/653908 http://te.marc-hi.ca/forums/default.aspx?g=posts&t=58

In my source XML I have the following:

<raceCode code="2106-3" displayName="White" codeSystem="2.16.840.1.113883.6.238" codeSystemName="Race &amp; Ethnicity - CDC"/>
<sdtc:raceCode code="2108-9" displayName="White European" codeSystem="2.16.840.1.113883.6.238" codeSystemName="Race &amp; Ethnicity - CDC"/>

Which fails to parse with the this error: System.Xml.XmlException: Can't parse 'raceCode' in namespace 'urn:hl7-org:sdtc'. The data does not appear to be an HL7v3 instance

This is my test that is blowing up:

        [TestMethod]
        public void ParseCcd()
        {
            var formatter = new XmlIts1Formatter();
            formatter.ValidateConformance = false;
            formatter.GraphAides.Add(new ClinicalDocumentDatatypeFormatter());
            formatter.RegisterXSITypeName("POCD_MT000040UV.Patient", typeof(MyPatientMultipleRaceCodes));
            formatter.Settings |= SettingsType.SuppressNullEnforcement | SettingsType.AlwaysCheckForOverrides;
            formatter.ValidateConformance = false;

            ClinicalDocument cda;
            using (var xr = new XmlStateReader(XmlReader.Create(@"C:\CDA_Tests\170.315_b1_toc_amb_ccd_r21_sample1_v8.xml")))
            {
                var result = formatter.Parse(xr, typeof(ClinicalDocument));
                cda = result.Structure as ClinicalDocument;
            }
        }

When I am rendering XML I have a different problem in that only one of the race codes is being assigned to the sdtc schema:

<sdtc:raceCode code="2106-3" codeSystem="2.16.840.1.113883.6.238" displayName="White" />
<raceCode code="2106-3" codeSystem="2.16.840.1.113883.6.238" displayName="White" />
<raceCode code="2108-9" codeSystem="2.16.840.1.113883.6.238" displayName="White European" />

Generated from this method:

        private MemoryStream RenderCda(ClinicalDocument cda)
        {
            XmlIts1Formatter fmtr = new XmlIts1Formatter();
            fmtr.RegisterXSITypeName("POCD_MT000040UV.Patient", typeof(MyPatientMultipleRaceCodes));
            fmtr.Settings |= SettingsType.SuppressNullEnforcement | SettingsType.AlwaysCheckForOverrides;
            fmtr.GraphAides.Add(new DatatypeFormatter());
            fmtr.ValidateConformance = false;

            // Prepare the output 
            var stream = new MemoryStream();
            XmlStateWriter xsw = new XmlStateWriter(XmlWriter.Create(stream, new XmlWriterSettings() { Indent = true }));
            xsw.WriteStartElement("ClinicalDocument", "urn:hl7-org:v3");
            xsw.WriteAttributeString("xmlns", "sdtc", null, "urn:hl7-org:sdtc");

            fmtr.Graph(xsw, cda);
            xsw.WriteEndElement();

            xsw.Flush();
            stream.Position = 0;

            return stream;
        }

I've been banging my head against this for quite awhile now. Any help would be welcomed.

LRFalk01 commented 7 years ago

I was able to get the parse to work with a custom build of Everest. I added the and clause on line 12 (code is from XmlIts1Formatter.cs):


        public IFormatterParseResult Parse(XmlReader r, Type t)
        {
            if (!(r is XmlStateReader))
                r = new XmlStateReader(r);

            // Initial state
            if (r.ReadState == ReadState.Initial)
                while (r.NodeType != XmlNodeType.Element)
                    r.Read();

            // Detect if we can parse this...
            if (r.NamespaceURI != "urn:hl7-org:v3" && r.NamespaceURI != "urn:hl7-org:sdtc")
                throw new XmlException(string.Format("Can't parse '{0}' in namespace '{1}'. The data does not appear to be an HL7v3 instance", r.LocalName, r.NamespaceURI), null);

            var resultContext = new XmlIts1FormatterParseResult(ResultCode.Accepted, null);
            resultContext.Structure = ParseObject(r, t, null, resultContext);
            return resultContext;
        }
LRFalk01 commented 7 years ago

I think the render issue is a bug where SET only applies the NamespaceUri from the property attribute on the first item in the collection:

[Property(Name = "raceCode", PropertyType = PropertyAttribute.AttributeAttributeType.NonStructural, NamespaceUri = "urn:hl7-org:sdtc")]
public new SET<CE<string>> RaceCode { get; set; }
LRFalk01 commented 7 years ago

So, I was able to get this to work with a custom formatter:

        public class DlEverestFormatter : XmlIts1Formatter
        {
            protected override void GraphObject(XmlWriter s, IGraphable o, Type useType, IGraphable context, XmlIts1FormatterGraphResult resultContext)
            {
                var writer = (XmlStateWriter)s;
                if (writer.CurrentElement.Name == "raceCode" && useType.GetGenericTypeDefinition().FullName.Contains(".SET"))
                {
                    var raceCodes = (SET<CE<string>>)o;
                    var raceCodeCount = 0;
                    foreach (var raceCode in raceCodes)
                    {
                        if (raceCodeCount > 0)
                        {
                            s.WriteStartElement("raceCode", "urn:hl7-org:sdtc");
                        }

                        s.WriteAttributeString("code", raceCode.Code);
                        s.WriteAttributeString("codeSystem", raceCode.CodeSystem);
                        s.WriteAttributeString("displayName", raceCode.DisplayName);

                        raceCodeCount = raceCodeCount + 1;

                        if (raceCodeCount < raceCodes.Count)
                        {
                            s.WriteEndElement();
                        }
                    }
                    return;
                }

                base.GraphObject(s, o, useType, context, resultContext);
            }
        }

I also removed the NamespaceUri from my Patient class override RaceCode property. The override GraphObject takes care of setting the correct namespace for any raceCode past the first one. I will be doing the same for ethnicity which also has multiple in MU2015.