FHIR Chat · XSD Schemas from FHIR Profiles for Code Generation · implementers

Stream: implementers

Topic: XSD Schemas from FHIR Profiles for Code Generation


view this post on Zulip Marcus Wurlitzer (Feb 01 2021 at 15:23):

Hello all, code generation XSD schemas for the FHIR base profiles are available for download. How were these XSD schemas generated? Is there a way to generate XSD schemas programmatically for custom FHIR profiles? Thanks in advance, Marcus.

view this post on Zulip Lloyd McKenzie (Feb 01 2021 at 16:26):

The schemas for FHIR core are generated by custom code that's part of the FHIR publication process. There are no schemas published for profiles, largely because such schemas are generally unhelpful. Schema has no ability to support 'slicing', which is a key aspect of profile design (setting different constraints on different repetitions in an instance). Good profiles generally don't constrain overall cardinality much because that interferes with interoperability. (A profile shouldn't say "Patient can only have one name", but instead "Patient should have one name that meets these constraints" - and XML schema has no ability to enforce the latter.)

view this post on Zulip Marcus Wurlitzer (Feb 01 2021 at 16:38):

Thanks a lot for your answer. So, while the complexity FHIR profiling allows for cannot be covered by XSD schemas, I am still thinking about whether they might be a useful tool for us to implement a specific FHIR profile. For instance, say we can safely assume that the resources we will create from our data source will always conform to a certain slicing variant of a profile; then a corresponding XSD schema might help us with code generation and avoid manual implementation of classes that conform to that profile variant. Or am I on the wrong track here?

view this post on Zulip Lloyd McKenzie (Feb 01 2021 at 16:46):

XSD can't enforce slices - it just doesn't have the technical capacity for multiple elements with the same name to have different constraints.

view this post on Zulip Marcus Wurlitzer (Feb 01 2021 at 16:54):

But one XSD could reflect a single slicing variant?

view this post on Zulip Michele Mottini (Feb 01 2021 at 17:04):

You should not generate custom classes corresponding to a profile, class structure cannot capture the important things that profile specifies (slicing, binding)

view this post on Zulip Rik Smithies (Feb 01 2021 at 17:07):

Schematron can do some of that however, if the intention is validation rather than say code generation

view this post on Zulip Lloyd McKenzie (Feb 01 2021 at 17:09):

Slicing means different rules for different repetitions. Schema can't define rules for one repetition unless you prohibit all other repetitions.

view this post on Zulip Marcus Wurlitzer (Feb 01 2021 at 17:13):

So how is profile slicing and binding handled programmatically then? is it all "soft-coded" and not backed by any structures native to the language of implementation (e. g. classes)?

view this post on Zulip Marcus Wurlitzer (Feb 01 2021 at 17:15):

(note that I am viewing from the perspective of a "data owner" so to say. we have medical data that we want to represent as FHIR resources which should conform to national profiles.)

view this post on Zulip Michele Mottini (Feb 01 2021 at 17:17):

All 'soft coded'. Most of the work is in mapping codes

view this post on Zulip Michele Mottini (Feb 01 2021 at 17:19):

That's the part where profiles are more relevant, the rest of the work is mostly just to generate FHIR structures, that do not really depend on profiles (and for which classes matching the general FHIR structure are very useful)

view this post on Zulip Marcus Wurlitzer (Feb 01 2021 at 17:26):

Hmm. So, for example, when the German FHIR profile for Patient defines several custom fields as extensions - you would advise against creating a derived class that contains these fields so I can map them just like any other regular field?

view this post on Zulip Lloyd McKenzie (Feb 01 2021 at 17:30):

What you do with your internal software is totally up to you. However, the XML and JSON are going to represent extensions as all one element and there's no chance of having a schema that will differentiate different extension requirements.

view this post on Zulip Marcus Wurlitzer (Feb 01 2021 at 17:37):

Right. I better understand the limitations now. Thank you. :)

view this post on Zulip Michele Mottini (Feb 01 2021 at 17:38):

would advise against creating a derived class that contains these fields

Yes, I advise against it, because if you do that you will also have to write custom serializer / de-serializer for your derived class

view this post on Zulip Rik Smithies (Feb 01 2021 at 17:41):

@Marcus I guess that is a different question :-) That seems reasonable to me, but whether the best route to that is via XSD, I am not so sure.

But at any rate, in general, there are not XSDs created for profiles. The reason is that they can only cover some aspects of profiles. But there is nothing to stop you manually extending your XSD to add these extensions, and then code generating. You just cannot currently get the XSDs from the profiles automatically.

If you want code generation then a better route is perhaps to use the FHIR base structure definitions, plus the profile structure definitions. All the information is there. But I don't know of code generation that works directly from those. Some of the existing code frameworks (e.g. C#, java) have code generation from the base structure defs, but I don't know about from profiles. I have not heard of that existing but it seems plausible.

Yet another route is to change the base FHIR structures to add the extensions, as if they were normal named elements (cheat, basically), and then run that as a new custom build. Then you would get XSDs generated with the extra elements. Just don't check it back in :-)

view this post on Zulip Marcus Wurlitzer (Feb 01 2021 at 17:55):

@Rik Smithies Yes, these are very much my thoughts. Actually I have been thinking about a "canonical" way to generate code (such as a classes) from FHIR profiles. The XSD route was one approach, but due to its limitation it seems as though it will never become canonical anyway.

So I might go the 'cheat' route, but probably class-based rather than XSD-based – I still have to work out the implications of that approach (such as possibly the need for custom serialization routines as Michele pointed out, for instance).

view this post on Zulip Grahame Grieve (Feb 02 2021 at 21:28):

@Marcus Wurlitzer it's on my todo list to write a generator for HAPI that takes a profile and generates a series of facade classes that provide an interface to the underlying FHIR POJOs but express the semantics of the profile

e.g. extensions get native accessors, there's no accessor for things that aren't allowed, and slices become named accessors.

it hasn't happened because the last item - which is necessary to make it work - is super hard.

view this post on Zulip Marcus Wurlitzer (Feb 03 2021 at 08:54):

@Grahame Grieve great to know, thanks. I agree it is a sophisticated task, like building a compiler, basically.

view this post on Zulip Marc Günther (Feb 08 2021 at 14:16):

This is very interesting.

I am currently looking for exactly this, how to create Resources for custom Profiles:

One idea was to implement a code generator, which takes the profiles and outputs all the necessary accessor methods to create resources on the fly. Or preferably not even code generation, but at least some dynamic way to create Resources for a given Profile, without hardcoding every special case.

Apparently, this has never been done before, and described here as "super hard". So I guess the manual hardcoding is the way to go?

view this post on Zulip Lloyd McKenzie (Feb 08 2021 at 16:38):

Be aware that if you're using custom resources, you're not FHIR conformant and won't be interoperable with FHIR systems. The interoperable approach is to use Basic.

view this post on Zulip Grahame Grieve (Feb 08 2021 at 22:40):

that wasn't what @Marc Günther meant. He was talking about "resources" as custom generated POJOs in a HAPI framework

view this post on Zulip Grahame Grieve (Feb 08 2021 at 22:41):

the attraction of doing it manually is that you can pick and choose how much complexity to get involved with

view this post on Zulip Grahame Grieve (Feb 08 2021 at 22:42):

I do think it's possible, if you have a good FHIRPath engine, and a solid fast terminology server supporting you, to do it entirely automatically, but I'm not sure it's possible to do it fast enough for production without being able to pick and choose

view this post on Zulip Marc Günther (Feb 09 2021 at 11:30):

Sorry, I don't want to hijack this thread, but this sounded at least somewhat related to my problem...

I am currently looking for a way to dynamically create Resources that conform to the _German Corona Consensus Data Set_ (Gecco), as I would like to avoid writing 68 Methods like the following example:

Apparently, no one has ever done this before, and given that I am very new to this entire FHIR thing (for example I do not have the slightest idea what is a terminology server, why I would need one, nor why it would need to be fast), I guess I need to bite the bullet, and write custom code for all 68 Profiles by hand...

Regarding FHIRPath, I found a FHIRPathResourceGenerator, that has just recently been merged into HAPI, which sounded very interesting:

Unfortunately, it doesn't seem to support lists of values (slicing?).

view this post on Zulip Michele Mottini (Feb 09 2021 at 14:29):

My suggestion would be _not_ to create any custom class, just use the standard FHIR classes for the resources in whatever library you are using. Not worth the trouble

view this post on Zulip Marc Günther (Feb 09 2021 at 14:34):

Yes, that's what I did in the example I posted above. I use normal _Observation_ class from HAPI library. But I still need to write 68 of these methods, and I would like to avoid that...

view this post on Zulip Michele Mottini (Feb 09 2021 at 14:38):

You have to code the mapping rules _somewhere_. If you have the mapping rules in some sort of standard format that is not code you could write mapping code that use them - just creating the standard FHIR classes

view this post on Zulip Michele Mottini (Feb 09 2021 at 14:43):

But in my experience as soon as you get in the details of each individual mapping you'll find that a general approach is hard to use - because mappings vary depending on different values, terminology mismatch etc

view this post on Zulip Michele Mottini (Feb 09 2021 at 14:49):

(our server implementation has 47K lines of code handling the mapping to / from FHIR of our data model)

view this post on Zulip Marc Günther (Feb 09 2021 at 15:12):

Well, I have no control over the format of the source data. It could be anything.

As I wrote in that other thread (sorry for hijacking this one), other people (with the neccessary domain knowledge) will define the mapping from their specific set of source data into FHIR.

My task is to provide the system (a UI to configure the mapping, and a process to execute it), which allows those other people to define their mappings.

I was hoping that the StructureDefinitions are enough to implement this in some generic way, without hardcoding the Gecco Profiles into my source code. This would allow us to support other profiles later, by simply uploading their StructureDefinitions.

But, unfortunately, it seems that this is not possible, or at least very hard. So, I guess I have to implement 68 different methods (one for each Gecco Resource), with a huge switch/case statement in front.

Is this really the way to go about this? It feels so wrong in every possible way.

view this post on Zulip Lloyd McKenzie (Feb 09 2021 at 16:19):

If you're talking about mappings, that's StructureMap, not StructureDefinition.

view this post on Zulip Marc Günther (Feb 09 2021 at 17:48):

From the Gecco Downloads page, I can download a zip file which contains lots of StructureDefinitions:

I have nowhere seen a download that does contain a StructureMap.

view this post on Zulip Lloyd McKenzie (Feb 09 2021 at 18:32):

There won't be any standard StructureMaps. The StructureMaps need to be created based on what you're mapping to or from - and of course that's not "standard" and can't be published as part of the spec.

view this post on Zulip Marc Günther (Feb 09 2021 at 20:03):

Well, as I said, I don't know from what I'm mapping from.

I need to implement a mechanism that allows others to define some mapping from their arbitrary data structure to FHIR/Gecco.

For that, the question was, if there is some generic way to generate the FHIR Resources based on the Gecco StructureDefinitions, without reimplementing and hardcoding entire of Gecco in Java. The answer seems to be, it has never been done, at least not with HAPI, it's not clear that it can be done, and if it is, it probably is super hard to implement.

Which sounds strange, as there seems to be a Validation mechanism in Hapi, which can read StructureDefinitions and validate Resources against them (at least that's what I understood from the docs?). So if there is enough information to validate resources, there should be enough information to generate them, no? And the code which does all the heavy lifting of understanding those StructureDefinitions (eg. applying differentials) could be easily adapted to that use case.

The reason I wrote here, is because of Grahame's comment:

it's on my todo list to write a generator for HAPI that takes a profile and generates a series of facade classes that provide an interface to the underlying FHIR POJOs but express the semantics of the profile

which sounds very much like what I am looking for.

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:05):

I don't really understand what you are trying to do

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:06):

yes, the HAPI code includes a validator that uses the structure definitions to validate json or xml and see that it conforms to the definitions.

view this post on Zulip Marc Günther (Feb 09 2021 at 20:06):

Oh ;)

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:06):

but it doesn't inject any content from anywhere. It just knows what should be in the resources

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:06):

Also, the hapi code includes the code generator that creates the classes in org.hl7.fhir.r4.model etc

view this post on Zulip Michele Mottini (Feb 09 2021 at 20:07):

So if there is enough information to validate resources, there should be enough information to generate them, no

Definitely not

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:07):

this knows how to create java classes that read and write the base FHIR resource classes.

view this post on Zulip Marc Günther (Feb 09 2021 at 20:07):

Ah

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:07):

the dotnet stack has equivalent code

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:08):

the gecco structure definitions are a statement of additional rules over the top of these underlying definitions. They are a set of invariants on other class definitions, not a set of class definitions.

view this post on Zulip Marc Günther (Feb 09 2021 at 20:10):

Yes, they specify various differentials over stuff that is in FHIR Core, eg. Gecco-BodyWeight has some constraints on Core-BodyWeight.

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:10):

so generating 'classes' that represent them isn't simple transform. The code generator doesn't know how to do that. (That's what on my todo list). But if you want a sense of the challenges involved in class generation, you could look at the validator code. Maybe, say, here: https://github.com/hapifhir/org.hl7.fhir.core/blob/master/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java#L3457

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:11):

but even if you had solved all that, and had a working class generator, I still don't know how that would "implement a mechanism that allows others to define some mapping from their arbitrary data structure"

view this post on Zulip Marc Günther (Feb 09 2021 at 20:12):

No, that part I can do.

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:14):

so you could implement that on the base Resources, such as Observation, instead of on generated classes for the profiles themselves. Most of the challenges are in the mapping part, and generating the classes is low yield.

view this post on Zulip Marc Günther (Feb 09 2021 at 20:14):

I can say, ok, this field corresponds to BodyWeight, that field corresponds to BloodPressure.systolic, etc... And I need to take care of conversion of codings and units...

The thing I don't know is how to create Gecco conformant FHIR Resources.

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:16):

I'm not understanding this question. You just use the FHIR resources in the hapi classes, e.g. Observation, and make sure they follow the rules

view this post on Zulip Marc Günther (Feb 09 2021 at 20:17):

Yes, I did an example of BodyWeight by creating an Observation, it's in that Google group posting:

But what I did there is basically just taking the BodyWeight example provided, and convert that into Java code.

view this post on Zulip Marc Günther (Feb 09 2021 at 20:18):

So I need to write methods like this for all the 68 Gecco Resources?

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:20):

so this is mostly about the fixed values, and not manually coding them?

view this post on Zulip Marc Günther (Feb 09 2021 at 20:22):

Well, the fixed stuff, but also of course the values that come from the source data. BloodPressure for example has two, systolic and diastiolic.

view this post on Zulip Marc Günther (Feb 09 2021 at 20:24):

And, of course, getting the information, that there ARE two values for BloodPressure. Which also is information that is available in the StructureDefinitions, that I don't want to replicate into Java.

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:27):

the thing about the values that come from the source data is that the data acquisition is so variable...

Here's how I've seen people handle this:

  • create a json/xml template that uses something like ST.template to fill out the content (xml is actually easier than json here, because of stinking commas, but the json parser in hapi can ignore commas) (also, much more sophisticated libraries that ST.template can be used - .e.g I've seen liquid, full blown PHP, etc)

  • create a template with doesn't contain the empty elements, load it in hapi, fill out the missing bits with your actual data, and then you're done

  • use a code generator to generate a factory the spits up the constructed resources/parts of resources, and then you orchestrate that from your code that is providing the data

  • just build it manually as you do in your example there, on the basis that mostly, the boilerplate is less than the code that matters anyway

view this post on Zulip Marc Günther (Feb 09 2021 at 20:29):

Yea, point 3 is basically what I had in mind. Either a code generator, or just something that reads and understands the StructureDefinitions at runtime and acts accordingly.

view this post on Zulip Marc Günther (Feb 09 2021 at 20:31):

But, it seems, this is super complicated to implement, so I probably will have to figure out something else.

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:31):

well, it has to be interactive - the code generator doesn't know which slices or optional elements you're going to want, based on which data you have at hand.

view this post on Zulip Marc Günther (Feb 09 2021 at 20:32):

I was even thinking of cheating, and just take all the examples they provide, and put wildcards in the appropriate places... :o

view this post on Zulip Marc Günther (Feb 09 2021 at 20:32):

Yes

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:37):

I have the guts of a code generator for HAPI, but it's aimed at making examples, uses all slices etc, and is implemented in pascal, so probably not useful for you

view this post on Zulip Marc Günther (Feb 09 2021 at 20:37):

Pascal :)

view this post on Zulip Marc Günther (Feb 09 2021 at 20:40):

Well, wow, I have a lot to think about now. Seems this is harder than I expected.

Thanks Grahame for taking the time to answer all my questions, and sorry again for hijacking this topic! Hope I finally made sense in the end... ;)

view this post on Zulip Grahame Grieve (Feb 09 2021 at 20:43):

this is harder than I expected.

Everything is harder than expected. Especially when you decide to abstract

view this post on Zulip Marc Günther (Feb 09 2021 at 20:44):

Well, I like generic solutions. I absolutely hate to duplicate code or hardcode stuff. It's a maintenance nightmare.

view this post on Zulip Marc Günther (Feb 10 2021 at 09:07):

Michele Mottini said:

So if there is enough information to validate resources, there should be enough information to generate them, no

Definitely not

Ah, I missed that yesterday.

So which information is missing from the StructureDefinitions?

view this post on Zulip Michele Mottini (Feb 10 2021 at 13:29):

Where to get the data from, how to transform it

view this post on Zulip Marc Günther (Feb 10 2021 at 15:04):

But I have the data.

view this post on Zulip Marc Günther (Feb 10 2021 at 15:13):

For example, I have:

  • two data values: 120 and 80
  • a patient: c786ac3c-0579-4aa2-adda-db784b214ad6
  • a date: 2020-09-25

and I know that these two values correspond to the systolic/diastolic Bloodpressure values from this StructureDefinition:

view this post on Zulip Marc Günther (Feb 10 2021 at 15:14):

So I would think that this is enough information to produce this output:

view this post on Zulip Michele Mottini (Feb 10 2021 at 15:15):

Yes, but the information on where those two values are and what they correspond to is not inside the StructureDefinition, it is something _you_ know

view this post on Zulip Marc Günther (Feb 10 2021 at 15:15):

I'm a software developer, so I don't want to hardcode that BloodPressure Profile in Java Code (along with all the 68 others from Gecco). Instead I would like to read the existing StructureDefinition, and then produce the result.

view this post on Zulip Michele Mottini (Feb 10 2021 at 15:16):

You need to have also something else that describe where the data is and where it goes to

view this post on Zulip Marc Günther (Feb 10 2021 at 15:16):

Yes, that's what I wrote, I know that these two values correspond to systolic/diastolic bloodpressure

view this post on Zulip Marc Günther (Feb 10 2021 at 15:16):

Yes, I have that

view this post on Zulip Michele Mottini (Feb 10 2021 at 15:17):

OK, I give up

view this post on Zulip Marc Günther (Feb 10 2021 at 15:17):

Sorry, I don't understand

view this post on Zulip René Spronk (Feb 10 2021 at 15:19):

I'm aware that years ago some Danes worked on a profile-based code generator, a variation of HAPI. But AFAIK we don't have this anymore, so you're stuck with HAPI itself. Which is as good as its gets.
If you need something else, then you may have to create it - and please share whatever it is you'll be creating!

view this post on Zulip Marc Günther (Feb 10 2021 at 15:19):

I would be glad to! :)

view this post on Zulip Marc Günther (Feb 10 2021 at 15:20):

But I have the feeling that no one really understands what I'm talking about?

view this post on Zulip Marc Günther (Feb 10 2021 at 15:21):

And for me, knowing not a lot about FHIR, StructureDefinitions seem super hard to understand.

view this post on Zulip Marc Günther (Feb 10 2021 at 15:21):

For example, I wouldn't know how to apply a differential to a baseDefinition to get the actual snapshot in Hapi.

view this post on Zulip René Spronk (Feb 10 2021 at 15:25):

That's a good question for the #hapi stream..

view this post on Zulip René Spronk (Feb 10 2021 at 15:25):

(not to discourage you, but that's where the hapi experts hang out)

view this post on Zulip Alexander Zautke (Feb 10 2021 at 15:27):

As @Lloyd McKenzie said before you need some sort of mapping information on how to get the data values (e.g., your two data values: 120 and 80) to the correct element. You would need to have the information from where you can get that information in your source data and where to place it. That can be generically represented in a StructureMap (not SteructureDefinition!) resource.

view this post on Zulip Alexander Zautke (Feb 10 2021 at 15:27):

Just based on the information present in the StructureDefinition there is no way of generically build a mapping and thus generate the wanted output.

view this post on Zulip Alexander Zautke (Feb 10 2021 at 15:30):

And if you have that information present, there is no real added value in having generated classes...

view this post on Zulip Marc Günther (Feb 10 2021 at 15:32):

Yea, I'm sorry, I think my questions didn't belong here in the first place...

view this post on Zulip Lloyd McKenzie (Feb 10 2021 at 15:40):

You need to know what LOINC codes for blood pressure, systolic and diastolic and you need to know that the systolic and diastolic values go in Observation.component.value rather than separate Observation instances. The only ways to capture that are to write custom code for each type of measurement (or perhaps a generic set of code that uses a table or something to map) or to write your own StructureMaps. FHIR can't know what your values mean and map them for you.

view this post on Zulip Marc Günther (Feb 10 2021 at 16:57):

You need to know what LOINC codes for blood pressure, systolic and diastolic

Aren't these available in the StructureDefinition in:

  • Observation.code.coding:loinc.code
  • Observation.component:systolicBloodPressure.code.coding:loinc.code
  • Observation.component:diastolicBloodPressure.code.coding:loinc.code

?

and you need to know that the systolic and diastolic values go in Observation.component.value rather than separate Observation instances.

That I hoped would be apparent from the cardinality of the Observation.component which is 2..* and the slicing information in:

  • Observation.component:systolicBloodPressure.value[x]
  • Observation.component:diastolicBloodPressure.value[x]

in the StructureDefinition.

The only ways to capture that are to write custom code for each type of measurement (or perhaps a generic set of code that uses a table or something to map) or to write your own StructureMaps.

Yes, that's what I hear. It seems rather unfortunate.

FHIR can't know what your values mean and map them for you.

Yes, of course it doesn't know that, and cannot map them for me automagically. I'm a little bit at a loss as to why this topic is brought up all the time, as I never asked about it.

The system I'm currently writing will allow the source data owner to specify that:

  • this source data value X will map to BloodPressure Profile with element path Observation.component:systolicBloodPressure.value[x], and
  • that source data value Y will map to BodyWeight Profile with element path Observation.value[x]"

etc...

That mapping mechanism is easy to implement, and will be completely generic. What I don't understand, how to generate the FHIR Resources in a generic way, without hardcoding/reimplementing entire of Gecco in Java.

view this post on Zulip Marc Günther (Feb 10 2021 at 17:16):

Alexander Zautke said:

Just based on the information present in the StructureDefinition there is no way of generically build a mapping and thus generate the wanted output.

I don't want to build a mapping automatically out of the StructureDefinitions. Of course this is not possible. They have no knowledge of the source data.

What I need is:

  • given a mapping that someone already defined (see above, that mapping knows everything about the source data, and where each value should end up in which Gecco Resource),
  • to create the desired resources in a generic way.

view this post on Zulip Marc Günther (Feb 10 2021 at 17:17):

Sorry, I don't really know how to explain this any better. I feel there is some fundamental misunderstanding here, which might entirely my fault, as I don't know FHIR and its use cases very much...

view this post on Zulip Alexander Zautke (Feb 10 2021 at 17:38):

Marc Günther said:

That mapping mechanism is easy to implement, and will be completely generic. What I don't understand, how to generate the FHIR Resources in a generic way, without hardcoding/reimplementing entire of Gecco in Java.

You could allow the source data owner to look at the Gecco profiles and define a mapping between the source data elements and the final path to the corresponding element in FHIR. That's a manually process where the StructureDefinition doesn't help you a lot in the sense that you can't extract such information out of the profiles. Such a mapping can be captured and interpreted in a StructureMap resource. A StructureMap can be used if your source data elements can be uniquely identified and accessed in some way. The path of the target where you want to map to is not required to be bound to the profile. The slices and other details defined in the profile are important for validation but not for creating the profiles. It's sufficient to use the base classes. It's only necessary to look into the profile to see what elements are "hard-coded" like Observation.code.

view this post on Zulip Alexander Zautke (Feb 10 2021 at 17:39):

So the idea would be to go through the StructureDefinition, look for fixed values or patterns. Otherwise look up your mapping from the source to the target.

view this post on Zulip Alexander Zautke (Feb 10 2021 at 17:43):

If StructureMaps are too hard to generate or to interpret you can of course go with any other sort of mapping table

view this post on Zulip Alexander Zautke (Feb 10 2021 at 17:44):

And as an extra workaround you could also add the fixed elements from the profiles into your mapping table as well, so you only need to look at one source.

view this post on Zulip Lloyd McKenzie (Feb 10 2021 at 18:48):

Typically mappings are done to Observation, not to a particular profile. Profiles, instead, simply guide where you choose to map. So you won't map to an element called "systolicValue", instead, you'll map to Observation.component and assert that the code needs to have a specific LOINC coding and then map to the Observation.component.value.

view this post on Zulip Grahame Grieve (Feb 10 2021 at 20:28):

@Marc Günther you can generate the snapshot using org.hl7.fhir.r5.conformance.ProfileUtilities.

I could certainly write a resource generator that takes a profile and map of name / value pairs. It would construct the resource as specified in the profile, and marge in the data from the map, where the map name is the element id in the profile (in the snapshot).

Here's the problems I would anticipate:

  • the generator wouldn't know how to decide whether to include an optional complex element or not (unless there was an arbitrary rule that it's only included if there's matching data elements)
  • for any repeating complex, you'd need to somehow match the data elements in the sub-elements to their matching pairs
  • the values would have to be complex values, in some cases, but you wouldn't know how to generate the complex values (because the profiles don't always profile down to primitive elements). That's both a java typing problem, and a more fundamental problem that the generating code has to understand the profiles inherently.
  • many profiles (don't know about the gecco) still leave elements unspecified, elements you'd need to populate them. A profile based generator wouldn't be able to generate them

view this post on Zulip Marc Günther (Feb 11 2021 at 16:32):

Thanks Grahame, ProfileUtilities.generateSnapshot() looks exactly like what I would need. That processPaths() method in there, which does the real work, looks like some serious piece of programming, there is absolutely no way I could understand how it works.

I fear, what I had in mind (which is exactly what you describe, thanks, I'm glad someone finally understands what I'm talking about), requires some similar complexity, even without the problems you mentioned, so while I have no doubt that you could write something like this, I guess this is out of my league.

So for now, I will try to hardcode Gecco in Java, I hope it should be possible without replicating too much code. Maybe later, when I got a better understanding of all the little details, I can try a more generic approach. That generateSnapshot() will definitely be very helpful.

Thanks all for your help, I hope I didn't steal too much of your time.

view this post on Zulip Grahame Grieve (Feb 11 2021 at 19:33):

no problems. processPaths has grown slowly over the years. I wish I had taken a different approach but it's never got to the 'let's rewrite from scratch' point.

view this post on Zulip Lin Zhang (Feb 16 2021 at 01:12):

@Marc Günther Integration/interface engines such as Mirth connect might provide some inspirations for your design of a generator and its UI.

view this post on Zulip Timo Rosenblatt (Feb 16 2021 at 09:06):

@Marc Günther Seems like we're in the same boat stumbling to get into FHIR.
A few days ago I found https://simplifier.net/downloads/firely-terminal. This can be used to create snapshots from CLI.

Snapshot generation is described at https://docs.fire.ly/projects/Firely-Terminal/CommonScenarios/SnapshotGeneration.html.
Maybe this helps you.


Last updated: Apr 12 2022 at 19:14 UTC