Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HL7 FHIR serialisation to json in asp.net web api

I'm using the HL7.Fhir nuget package 0.9.3 created by Ewout Kramer.

I am tying it up with ASP.NET Web API, but unfortunately the built in JSON serialization isn't generating the JSON correctly. It contains lots of this:

"<IdentifierElement>k__BackingField"

As suggested by the framework, this code works...

public HttpResponseMessage GetConformance()
    {
        var conformance = new Conformance();
        var json = FhirSerializer.SerializeResourceToJson(conformance);
        return new HttpResponseMessage{Content = new StringContent(json)};
    }

but this will become quite repetitive and isn't following the "by convention" json/xml serialization methods of Web API.

Are there any other FHIR objects packages available or should I just write my own?

like image 1000
peter.swallow Avatar asked Jan 14 '14 15:01

peter.swallow


1 Answers

Although the newer version of the HL7.Fhir NuGet package (currently in beta) will carry additional [DataContract] and [DataMember] attributes, and thus prevent these kind of errors, the standard .NET DataContract serializer will not be able to serialize in-memory POCO's to the correct FHIR XML and Json representation. The FHIR serialization has specific rules about how both XML and json are used, which is hard, if not impossible, to configure using the (limited) possibilities of the DataContract serializer.

However, it's also not necessary to invoke the FhirSerializer for each call as you showed in your codesnippet (in fact, that would be an WebApi anti-pattern). For example, our FHIR server (at http://spark.furore.com/fhir) is based on WebApi and uses a custom MediaTypeFormatter to handle this. To get a taste of what that looks like, we have created two formatters, one for json and one for xml:

public class JsonFhirFormatter : MediaTypeFormatter
{
        public JsonFhirFormatter() : base()
        {
            foreach (var mediaType in ContentType.JSON_CONTENT_HEADERS)
                SupportedMediaTypes.Add(new MediaTypeHeaderValue(mediaType));
        }
}

This tells the framework this formatter will take any of the formats in ContentType.JSON_CONTENT_HEADERS (which are application/json and some common variants) and is able to parse and read FHIR ModelTypes:

public override bool CanReadType(Type type)
{
    return type == typeof(ResourceEntry) || type == typeof(Bundle) || (type == typeof(TagList));
}

public override bool CanWriteType(Type type)
{
    return type == typeof(ResourceEntry) || type == typeof(Bundle) || (type == typeof(TagList)) || type == typeof(OperationOutcome);
}

Finally, you have to override the ReadFromStreamAsync and WriteToStreamAsync methods:

public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
    // Some code left out...
    XmlWriter writer = new XmlTextWriter(writeStream, Encoding.UTF8);

    if (type == typeof(ResourceEntry))
    {
        ResourceEntry entry = (ResourceEntry)value;
        FhirSerializer.SerializeResource(entry.Resource, writer);

        content.Headers.SetFhirTags(entry.Tags);
    }

Now, once you've done that, your Controller can simply do:

[HttpGet, Route("metadata")]
public ResourceEntry Metadata()
{
   return service.Conformance();
}

[HttpOptions, Route("")]
public ResourceEntry Options()
{
   return service.Conformance();
}

Note that our server does not use Resources as parameters and return values in the controller. Resources won't allow you to capture important metadata (like the id, version-id, last modified date etc). By using ResourceEntry in my controller, this data can be passed around with the resource data and the WebApi framework can bind this metadata to the appropriate HTTP headers.

like image 166
Ewout Kramer Avatar answered Oct 09 '22 02:10

Ewout Kramer