Skip to content

Instantly share code, notes, and snippets.

@ewoutkramer
Created March 1, 2017 15:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ewoutkramer/f10e36a51747759f7cca5d54e9cd44e6 to your computer and use it in GitHub Desktop.
Save ewoutkramer/f10e36a51747759f7cca5d54e9cd44e6 to your computer and use it in GitHub Desktop.
Overview of differences found in the snapshot generators for the Java and .NET FHIR stack

Comparison of .NET versus Java generated core snapshots

Include the <representation> on primitives

Whenever we encounter a primitive element (like value in all datatypes, and id in all stuctures), the Java generator will emit a <representation> element:

<element id="Account.coverage.id">
      <path value="Account.coverage.id"/>
      <representation value="xmlAttr"/>

This is in fact the only way to discover that this element is indeed a primitive value (rendered as an attribute in FHIR XML), since the <type> for this element will say:

<type>
	<code value="string"/>
</type>

It has been agreed that this is a bug (see GF#12060 - the id element is in fact not a FHIR 'string' but a real primitive), which however could not be fixed in the STU3 timeframe. This means that having the <representation> element is currently essential for downstream tooling using the snapshot to distinguish between a primitive string and a FHIR string.

My conclusion: although possibly superfluous when the <type> element would specify the correct primitive type, the .NET generator needs to repeat this element here on primitives.

Describe the slicing on extension explicitly

The <extension> element is implicitly an open slice on url. The Java generator leaves this implicit and does not provide a <slicing> on an <extension>, whereas the .NET generator does:

<element>
	<path value="Account.coverage.extension"/>
    <slicing>
    	<discriminator>
        	<type value="value"/>
        	<path value="url"/>
        </discriminator>
        <description value="Extensions are always sliced by (at least) url"/>
        <rules value="open"/>
    </slicing>

Note that the .NET generator will currently not provide this explicit slicing on the modifierExtension element.

My conclusion: We have decided to make this explicit as per GF#12061, so the Java generator needs to be updated to do this and the .NET generator will need to add this to modifierExtension as well.

Include the constraints on Element on each element

The .NET generator includes the <mapping> and the <constraint> on the Element type in each and every element:

<element>
    <path value="Account.id"/>
    <!-- stuff left out -->
    <type>
        <code value="id"/>
    </type>

    <!-- all this is included on each element of Account, inherited from the
    root element of Element -->
    <condition value="ele-1"/>
    <constraint>
        <key value="ele-1"/>
        <severity value="error"/>
        <human value="All FHIR elements must have a @value or children"/>
        <expression value="children().count() &gt; id.count()"/>
        <xpath value="@value|f:*|h:div"/>
    </constraint>
    <isSummary value="true"/>
    <mapping>
        <identity value="rim"/>
        <map value="n/a"/>
    </mapping>
</element>

My conclusion: these constraints are already part of the referred type id (it is mentioned on the root element of the definition of id) and do not need to be repeated here for the same reasons we don't include the children of the id type. Note however that we should make sure this constraint is repeated on BackboneElements and this may be the reason why the .NET generator repeats these items on both Element and BackboneElement.

In fact, the .NET generator will include the root elements on present on all structures in the type hierarchy, such that an element of, say, type Extension will have both repeat the constraints/mappings on Element and on Extension.

It turns out that this has been a source of fierce discussions between Grahame, Chris and Michel, so this behaviour of the .NET generator is intentional. Still, it would be nice if we could converge here.

Repeat the <comment> element on derived elements

The .NET generator inherits the comment element from the base, wherease the Java generator does not.

My conclusion: this element should not be treated any differently than say <description> and the Java generator should include this element as well.

Generating the new ElementDefinition.constraint.source element

This is a new element, and the .NET API does not provide it in its output yet. It is supposed to contain the source of the constraint, where the 'source' is the typename/resource name where the constraint was found first within the hierarchy of inherited constraints.

E.g.

 <path value="Account.guarantor"/>
      <!-- ...stuff left out... -->
      <constraint>
        <key value="ele-1"/>
        <severity value="error"/>
        <human value="All FHIR elements must have a @value or children"/>
        <expression value="children().count() &gt; id.count()"/>
        <xpath value="@value|f:*|h:div"/>
        <!-- ... this constraint was introduced in the Element datatype -->
        <source value="Element"/>
      </constraint>

My conclusion: .NET generator needs to add this.

Generating a <base> for the root element

The .NET generator includes a <base> element for every part of the snapshot, including the root element. Java omits this element on the root element. So, the .NET does include this snippet, Java does not:

<snapshot>
    <element>
        <path value="Account"/>
        <base>
            <path value="Resource"/>
            <min value="0"/>
            <max value="*"/>
        </base>

My conclusion: the .NET generator is more consistent, since the root element of Resource does indeed specify this cardinality, and on Extensions this might even be relevant, since the cardinality on the root level specifies whether an Extension may repeat or not.

Actually, what is the base?

A related discussion to the point above is: what is the right base? Differences become visible when we generate snapshots for a profile on a resource, in which case the Java generator will include the root element as a base:

This snippet is from the snapshot done by the Java generator for http://hl7.org/fhir/StructureDefinition/bodyweight

<element id="Observation:vitalsignsprofile">
	<path value="Observation"/>
    <short value="FHIR Body Weight Profile"/>
    <min value="0"/>
    <max value="*"/>
    <base>
      <path value="Observation"/>
      <min value="0"/>
      <max value="*"/>
    </base>

while the .NET generator has this:

<element id="Resource">
	<path value="Observation"/>
    <short value="FHIR Body Weight Profile"/>
    <min value="0"/>
    <max value="*"/>
    <base>
    	<path value="Resource"/>
        <min value="0"/>
        <max value="*"/>
    </base>

It is clear both generators have a point: The Java generator considers the last "core" type in the inheritance chain to be the base, in this case that's Observation, not DomainResource, not Resource. The .NET generator takes the "bottom" of the chain to be the base.

My conclusion: The documentation is actually pretty clear about what base.path should be:

The Path that identifies the base element - this matches the ElementDefinition.path for that element. Across FHIR, there is only one base definition of any element - that is, an element definition on a StructureDefinition without a StructureDefinition.base.

Ignoring the fact that this description is outdated - the element is now called StructureDefinition.baseDefinition - it's clear that only Resource qualifies, since Observation and DomainResource both have their baseDefinition set. The .NET generator seems to be right in this case.

Generation of 'id' attributes on elements

The Java toolkit seems to regenerate the id's, while the .NET API is currently broken. Java has:

<element id="Account">
      <path value="Account"/>
<element id="Account.id">
      <path value="Account.id"/>
<element id="Account.coverage.priority">
      <path value="Account.coverage.priority"/>

whereas .NET does:

<element id="Resource">
      <path value="Account"/>
<element id="Element">
      <path value="Account.id"/>
<element id="Element">
      <path value="Account.meta"/>

My conclusion: the .NET generator is obviously wrong.

@wmrutten
Copy link

wmrutten commented Mar 6, 2017

Note: while implementing the DSTU2 snapshot generator, I tried to include logic to generate element id's based on the proposed method for STU3. However in DSTU2, this is problematic because the id field still has a max-length of 64 characters. So I didn't complete this feature and disabled it by default. For the upcoming STU3 API library version, I'll revisit and fix this logic.

@wmrutten
Copy link

wmrutten commented Mar 6, 2017

As for the aggregation of constraints, Chris and I discussed this in detail and decided to fully complete each ElementDefinition that is included in the snapshot, regardless of the presence of child element constraints. For each element with a type, we resolve the external type profile and merge the root element properties (including but not limited to mappings and constraints) into the referencing profile. This way, a client application can always rely on each ElementDefinition in the snapshot to provide a complete and stable representation of all the associated element properties. The chosen approach moves complexity away from client developers to API implementers, which seems in line with general FHIR guidelines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment