Pages

Wednesday, February 22, 2017

Transforming from a fhir:identifier to V3:II

So this blog entry has been sitting here waiting for me to post something about how to transform FHIR Identifiers into CDA II elements in a way that works 100%.  The DocumentReference resource has an example that shows this content:

<masterIdentifier>
  <system value='urn:ietf:rfc:3986'/>
  <value value='urn:oid:129.6.58.92.88336'/>
</masterIdentifier>

What this content means is that the master identifier for the document being referenced is a URI as defined by RFC 3986.  For CDA geeks it should be obvious that the above implies that the content of the CDA document would have this in it:

<ClinicalDocument>
   ...
   <id root='129.6.58.92.88336'/>

But what about other URIs?  Well, if the URL format is urn:uuid:some-uuid-string, that's also pretty straight forward.  The output for id is simply:

  <id root='some-uuid-string'/>

OK, good.  What if the value is some other form of url?  What do we do now?  Grahame fixed this one for us by registering a new OID.

  <id root='2.16.840.1.113883.4.873' extension='some other form of url'/>

So, great, now we know how to handle all the cases where identifier.system = 'urn:ietf:rfc:3986'

What about the rest of the possibilities?
There are only three URLs you need to map to an OID described in the FHIR Identifier registry.  You can set up those lookups in an XML file somewhere.

How then, would you map an identifier containing a url in root whose corresponding OID you don't know?

There are three ways to handle this case:
1. Come up with something that produces a valid II datatype in CDA, recognizing that it might not be total cool.
2. Set the II as being not fully specified (and so requiring use of nullFlavor), perhaps committing the URL to another attribute so that the receiver might have some chance of fixing it on their end.
3. Giving up completely

Option 3 is not an option I like, so I discarded it.  You can do what you want, I almost never give up.
Option 2 is what I decided to use for my purposes.  It works, is technically valid, and might cause some complaints, but I can say, well, that URL is NOT KNOWN to my system, so we produce something technically correct with all the info the receiver might use to fix it.  So for:
  <identifier>
    <system value='http://example.com'/>
    <value value='my example id'/>
  <identifier>

I would use something like:
  <id nullFlavor='UNK' extension='my example id'
      assigningAuthorityName='http://example.com'/>
This says, I don't know the full II, I've told you what I know, you deal with it.

But, you could cheat, and say something like: "We know that url#fragment-id means the thing inside url with fragment identifier fragment-id.  So we'll treat it like that.  This is what you would get then:

  <id root='2.16.840.1.113883.4.873
      extension='http://example.com#my example id'/>

I'm not fond of this, because it isn't a totally legitimate answer, although most geeks might understand your reasoning, and what you generated.  It also means that your can no longer trust the content of any id that uses the value of 2.16.840.1.113883.4.873, and they really should be able to.

Fortunately for my uses, the nullFlavor case will only show up in exceptional circumstances.  All the naming systems I use in FHIR have an associated OID mapping.

For what it's worth, the same challenges exist with code system mapping, and I do the same thing: Make sure all my FHIR code systems map to an OID as well.

Wednesday, February 15, 2017

An XSLT Design Pattern (and not just for FHIR to CDA conversions)

I have a couple of XSLT design patterns that I've been using and improving over the past decade, which I pulled out again last night to do some FHIR to CDA transformations.  XSLT isn't what you'd call a strongly object oriented language, nor a strongly typed one.  However, the design patterns I used borrow from experiences I've had with strongly typed, object oriented languages such as Java.

The design pattern is a general one which I've used for a number of strongly typed schema to schema conversions.  FHIR to CDA is just one example.  V2 XML to CDA (or back) is another one, and I've also done this for CDA to ebXML (XDS related work), and at one point for CCR and CDA, even though I'd never really call CCR strongly typed.

The first design pattern is in how I define my transformations.  There are two ways in which you can call a template, and they have different benefits.  Sometimes you want to use one, and sometimes you want to use the other.  When going from one format to another in XML, usually there is a target element that you are trying to create, and one or more source elements you are trying to create it from.

My first method uses apply-templates.
<xsl:apply-templates select='...' mode='target-element-name'/>

You can also call apply-templates with parameters.
<xsl:apply-templates select='...' mode='target-element-name'>
  <xsl:with-param name='some-param' select='some-value'/>
</xsl:apply-templates>

This is pretty straight forward.  I use mode on my transformation templates for a variety of reasons, one of which is that it allows you to build transformation trees that the XSLT engine automates processing with.  More often that not, the name of the mode is the output element I'm targeting to generate.

The second method works more like a traditional function call, using call-template:
<xsl:call-template name='target-element-name'>
  <xsl:with-param name='this' select='the-source-element-to-transform'/>
  <xsl:with-param name='some-param' select='some-value'/>
</xsl:call-template>

Now, if the source element is always the same for a given target element (e.g., a FHIR address going to a CDA addr element), you create your actual template signature thus:

<xsl:template name='addr' mode='addr' match='address'>
  <xsl:param name='this' select='.'/>
   ...

Note that name and mode are the same.  What you want to create here is an <addr> element in CDA. The input you want to convert is a FHIR address element (I checked several dozen places, just about everywhere the Address datatype is used, it is always called address in FHIR resources).  So that explains the first line.  The name of the template, and its mode, identity what you are trying to create.

The second line is critical, and it has some serious impact on how you write your templates.  Instead of assuming a default context for your transformation, the <xsl:param name='this' select='.'/> sets up a parameter in which you can explicitly pass the transformation context, but which will implicitly use the current context if no such parameter is passed.  When you write your transformation, you have to be careful to use $this consistently, or you'll have some hard to find bugs, because sometimes it might work (e.g., when called using apply-templates), but other times not (e.g., when called using call-template).  It takes some diligence to write templates this way, but after a while you get used to it.

Here's the example for converting a FHIR Address to a CDA addr element.  It's pretty obvious:

<xsl:template name='addr' mode='addr' match='address'>
  <xsl:param name='this' select='.'/>
  <xsl:variable name='use'><xsl:choose>
    <xsl:when test='$this/use/@value="home"'>H</xsl:when>
    <xsl:when test='$this/use/@value="work"'>WP</xsl:when>
    <xsl:when test='$this/use/@value="temp"'>TMP</xsl:when>
  </xsl:choose></xsl:variable>
  <addr>
    <xsl:if test='$use!=""'>
      <xsl:attribute name='use'><xsl:value-of select='$use'/></xsl:attribute>
    </xsl:if>
    <xsl:for-each select='$this/line'>
      <streetAddressLine><xsl:value-of select='@value'/></streetAddressLine>
    </xsl:for-each>
    <xsl:for-each select='$this/city'>
      <city><xsl:value-of select='@value'/></city>
    </xsl:for-each>
    <xsl:for-each select='$this/state'>
      <state><xsl:value-of select='@value'/></state>
    </xsl:for-each>
    <xsl:for-each select='$this/postalCode'>
      <postalCode><xsl:value-of select='@value'/></postalCode>
    </xsl:for-each>
    <xsl:for-each select='$this/country'>
      <country><xsl:value-of select='@value'/></country>
    </xsl:for-each>
  </addr>
</xsl:template>

[There's a little XSLT shorthand nugget in the above transform, another design pattern I use a lot:
  <xsl:for-each select='x'> ... </xsl:for-each>

 is a much simpler way to say:
  <xsl:if test='count(x) != 0'> ... transform each x ... </xsl:if>

especially when x is a collection of items.]

Now, if you want to drop an address in somewhere, you can do something like this:
  <xsl:apply-templates select='address' mode='addr'/>

Or, you can also do it this way:
  <xsl:call-template name='addr'>
    <xsl:with-param name='this' select='$patient/address'/>
  </xsl:call-template>

Either works, and in developing large scale transformations, I often find myself using the same template in different ways.  The elegance of this design pattern in XSLT extends further when you have two or more source elements going to the same target element.

In that case, you have two templates with the same mode, but different match criteria.  AND, you have a named template.  Let's presume in the FHIR case, you want to map Resource.id and Resource.identifier to an id data type (it's not to far-fetched an idea, even if not one I would use). You then write:

  <xsl:template mode='id' match='id'>
    ...
  </xsl:template>
  <xsl:template mode='id' match='identifier'>
    ...
  </xsl:template>

  <xsl:template name='id'>
    <xsl:param name='this'/>
    <xsl:apply-templates select='$this' mode='id'/>
  </xsl:template>

And the final named template simply uses the match type to automatically select the appropriate template to use based on your input value.

Sometimes you want to create an output element but it isn't always called the same thing, even though it uses the same internal structure.  Using default parameters, you can set this up.  Let's look into example above.  Not EVERY id element is named id.  Sometimes in CDA the have a different name depending on what is being identified.  For example, in the CDA header, you have setId (in fact it's nearly the only case for id).  A more obvious case is code.  Most of the time, code is just code, but sometimes it's ____Code (e.g., priorityCode), but the general structure of a priorityCode (CE CWE [0..1]) is pretty much the same as for code (CD CWE [0..1]).  So, if you were going to convert a FHIR CodableConcept or Coding to a CDA CD/CE data type, you might use the same transformation.

  <xsl:template mode='code' match='code' name='code'>
    <xsl:param name='element' select='"code"'/>
    <xsl:element name='$name'>
      ...
    </xsl:element>
  </xsl:template>

You get the idea.  Usually, you want to generate <code>, and so you say nothing.  Sometimes you want to generate something different, and so you add a parameter.
  <xsl:call-template name='code'>
    <xsl:with-param name='this' select='$that/code'/>
    <xsl:with-param name='element' select='"priorityCode"'/>
  </xsl:call-template>

Remember you can also pass parameters using apply-templates, so this also works:
  <xsl:apply-templates select='$that/code'>
    <xsl:with-param name='element' select='"priorityCode"'/>
  </xsl:apply-templates>
  
Enough chatter, back to work.  HIMSS is only a couple of days away.

   - Keith

P.S.  I've missed being able to post while I've been heads down working towards HIMSS and the Interop showcase.  Hopefully I'll get more time when I get back.