Repository: FHIR/sushi
Pull request #289 (2020-03-19 17:52:57)

Primitive slice on instance
This addresses https://standardhealthrecord.atlassian.net/browse/CIMPL-213 by adding handling of slicing on primitives to instances.

When we are adding slices to an instance, we need to track which value is being used to represent which slice. This could be done using the discriminator for that slicing, but that is difficult, so instead we chose to add a `_sliceName` field to the slice which contains the name of the slice that is being represented by that object. The `_sliceName` field would then be removed before final export of the Instance. This works well when the slices are objects, but this approach was failing when the slices are primitives, because we cannot add a `_sliceName` field on a primitive. This PR fixes this by temporarily turning primitives into objects, so that the `_sliceName` field can be added, and then turning the objects back into primitives in the same `cleanInstance` function that removes the extra `_sliceNames`.

You can test this by checking out 5816e2d648f53eb7b4c8e80faa9d9b6f070b5032 and running the tests. You will see that some of the tests added by this commit fail, since they test adding slicing on primitives on an instance. Next, check out 08c79677b74c5fec8c2915d4c72986556a65379d, which adds the functionality described above, and you should see that the tests pass now.

jafeltra: I didn't notice anything else beyond what Mint pointed out and I think has been resolved! This looks good to me, but I'll let Mint review again, so I'll hold off on merging!

Patch:
diff --git a/src/export/InstanceExporter.ts b/src/export/InstanceExporter.ts
index d788762..2dfd826 100644
--- a/src/export/InstanceExporter.ts
+++ b/src/export/InstanceExporter.ts
@@ -258,6 +258,13 @@ export class InstanceExporter implements Fishable {
       (o, p) => typeof o[p] === 'object' && o[p] !== null && isEmpty(o[p]),
       (o, p) => (o[p] = null)
     );
+
+    // Change back any primitives that have been converted into objects by setPropertyOnInstance
+    replaceField(
+      instanceDef,
+      (o, p) => typeof o[p] === 'object' && o[p] !== null && o[p]._primitive,
+      (o, p) => (o[p] = o[p].fixedValue)
+    );
   }
 
   fishForFHIR(item: string) {
diff --git a/src/fhirtypes/common.ts b/src/fhirtypes/common.ts
index 077028d..887e4cc 100644
--- a/src/fhirtypes/common.ts
+++ b/src/fhirtypes/common.ts
@@ -66,9 +66,15 @@ export function setPropertyOnInstance(
         if (current[key] == null) current[key] = [];
         sliceName = getSliceName(pathPart);
         if (sliceName) {
+          if (typeof fixedValue !== 'object') {
+            // When a fixedValue is a primitive but also a slice, we convert to an object so that
+            // the sliceName field can be tracked on the object. The _primitive field marks the object
+            // to later be converted back to a primitive by replaceField in cleanInstance
+            fixedValue = { fixedValue, _primitive: true };
+          }
           const sliceIndices: number[] = [];
           // Find the indices where slices are placed
-          current[key].forEach((el: any, i: number) => {
+          current[pathPart.base]?.forEach((el: any, i: number) => {
             if (el?._sliceName === sliceName) {
               sliceIndices.push(i);
             }


Test patch:
diff --git a/test/export/InstanceExporter.test.ts b/test/export/InstanceExporter.test.ts
index 9d5e262..4f6933a 100644
--- a/test/export/InstanceExporter.test.ts
+++ b/test/export/InstanceExporter.test.ts
@@ -10,7 +10,13 @@ import {
   FshCodeSystem,
   RuleSet
 } from '../../src/fshtypes';
-import { FixedValueRule, ContainsRule, CardRule, OnlyRule } from '../../src/fshtypes/rules';
+import {
+  FixedValueRule,
+  ContainsRule,
+  CardRule,
+  OnlyRule,
+  CaretValueRule
+} from '../../src/fshtypes/rules';
 import { loggerSpy, TestFisher } from '../testhelpers';
 import { InstanceDefinition } from '../../src/fhirtypes';
 import path from 'path';
@@ -665,6 +671,37 @@ describe('InstanceExporter', () => {
       expect(exported.address[0]._line[1].extension[0].url).toBe('bar');
     });
 
+    it('should fix children of sliced primitive arrays on an instance', () => {
+      const caretRule = new CaretValueRule('name.prefix');
+      caretRule.caretPath = 'slicing.discriminator.type';
+      caretRule.value = new FshCode('value');
+      const containsRule = new ContainsRule('name.prefix');
+      containsRule.items = [{ name: 'Dr' }];
+      const cardRule = new CardRule('name.prefix');
+      cardRule.min = 0;
+      cardRule.max = '*';
+      // * name.prefix ^slicing.discriminator.type = #value
+      // * name.prefix contains Dr 0..*
+      patient.rules.push(caretRule, containsRule, cardRule);
+      const fixedRule1 = new FixedValueRule('name[0].prefix[Dr][0]');
+      fixedRule1.fixedValue = 'Doctor';
+      const fixedRule2 = new FixedValueRule('name[0].prefix[Dr][1]');
+      fixedRule2.fixedValue = 'Mister Doctor';
+      const fixedRuleChild = new FixedValueRule('name[0].prefix[Dr][1].id');
+      fixedRuleChild.fixedValue = 'Sir Mister Doctor to you';
+      // * name[0].prefix[Dr][0] = "Doctor"
+      // * name[0].prefix[Dr][1] = "Mister Doctor"
+      // * name[0].prefix[Dr][1].id = "Sir Mister Doctor to you";
+      patientInstance.rules.push(fixedRule1, fixedRule2, fixedRuleChild);
+      const exported = exportInstance(patientInstance);
+      expect(exported.name).toEqual([
+        {
+          prefix: ['Doctor', 'Mister Doctor'],
+          _prefix: [null, { id: 'Sir Mister Doctor to you' }]
+        }
+      ]);
+    });
+
     // Fixing References
     it('should fix a reference while resolving the Instance being referred to', () => {
       const orgInstance = new Instance('TestOrganization');
@@ -742,6 +779,30 @@ describe('InstanceExporter', () => {
       expect(exported.extension).toEqual([{ url: 'level', valueCoding: { system: 'foo' } }]);
     });
 
+    it('should fix a single primitive sliced element to a value', () => {
+      const caretRule = new CaretValueRule('name.prefix');
+      caretRule.caretPath = 'slicing.discriminator.type';
+      caretRule.value = new FshCode('value');
+      const containsRule = new ContainsRule('name.prefix');
+      containsRule.items = [{ name: 'Dr' }];
+      const cardRule = new CardRule('name.prefix');
+      cardRule.min = 1;
+      cardRule.max = '1';
+      // * name.prefix ^slicing.discriminator.type = #value
+      // * name.prefix contains Dr 1..1
+      patient.rules.push(caretRule, containsRule, cardRule);
+      const fixedRule = new FixedValueRule('name[0].prefix[Dr]');
+      fixedRule.fixedValue = 'Doctor';
+      // * name[0].prefix[Dr] = "Doctor"
+      patientInstance.rules.push(fixedRule);
+      const exported = exportInstance(patientInstance);
+      expect(exported.name).toEqual([
+        {
+          prefix: ['Doctor']
+        }
+      ]);
+    });
+
     it('should fix sliced elements in an array that are fixed in order', () => {
       const fooRule = new FixedValueRule('extension[type][0].valueCoding.system');
       fooRule.fixedValue = 'foo';
@@ -756,6 +817,33 @@ describe('InstanceExporter', () => {
       ]);
     });
 
+    it('should fix a sliced primitive array', () => {
+      const caretRule = new CaretValueRule('name.prefix');
+      caretRule.caretPath = 'slicing.discriminator.type';
+      caretRule.value = new FshCode('value');
+      const containsRule = new ContainsRule('name.prefix');
+      containsRule.items = [{ name: 'Dr' }];
+      const cardRule = new CardRule('name.prefix');
+      cardRule.min = 0;
+      cardRule.max = '*';
+      // * name.prefix ^slicing.discriminator.type = #value
+      // * name.prefix contains Dr 0..*
+      patient.rules.push(caretRule, containsRule, cardRule);
+      const fixedRule1 = new FixedValueRule('name[0].prefix[Dr][0]');
+      fixedRule1.fixedValue = 'Doctor';
+      const fixedRule2 = new FixedValueRule('name[0].prefix[Dr][1]');
+      fixedRule2.fixedValue = 'Mister Doctor';
+      // * name[0].prefix[Dr][0] = "Doctor"
+      // * name[0].prefix[Dr][1] = "Mister Doctor"
+      patientInstance.rules.push(fixedRule1, fixedRule2);
+      const exported = exportInstance(patientInstance);
+      expect(exported.name).toEqual([
+        {
+          prefix: ['Doctor', 'Mister Doctor']
+        }
+      ]);
+    });
+
     it('should fix a sliced element in an array that is fixed by multiple rules', () => {
       const fooRule = new FixedValueRule('extension[type][1].valueCoding.system');
       fooRule.fixedValue = 'foo';
diff --git a/test/testhelpers/testdefs/package/StructureDefinition-HumanName.json b/test/testhelpers/testdefs/package/StructureDefinition-HumanName.json
new file mode 100644
index 0000000..32aafd7
--- /dev/null
+++ b/test/testhelpers/testdefs/package/StructureDefinition-HumanName.json
@@ -0,0 +1,1 @@
+{"resourceType":"StructureDefinition","id":"HumanName","meta":{"lastUpdated":"2019-11-01T09:29:23.356+11:00"},"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status","valueCode":"normative"},{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version","valueCode":"4.0.0"}],"url":"http://hl7.org/fhir/StructureDefinition/HumanName","version":"4.0.1","name":"HumanName","status":"active","date":"2019-11-01T09:29:23+11:00","publisher":"HL7 FHIR Standard","contact":[{"telecom":[{"system":"url","value":"http://hl7.org/fhir"}]}],"description":"Base StructureDefinition for HumanName Type: A human's name with the ability to identify parts and usage.","purpose":"Need to be able to record names, along with notes about their use.","fhirVersion":"4.0.1","mapping":[{"identity":"v2","uri":"http://hl7.org/v2","name":"HL7 v2 Mapping"},{"identity":"rim","uri":"http://hl7.org/v3","name":"RIM Mapping"},{"identity":"servd","uri":"http://www.omg.org/spec/ServD/1.0/","name":"ServD"}],"kind":"complex-type","abstract":false,"type":"HumanName","baseDefinition":"http://hl7.org/fhir/StructureDefinition/Element","derivation":"specialization","snapshot":{"element":[{"id":"HumanName","extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status","valueCode":"normative"},{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version","valueCode":"4.0.0"}],"path":"HumanName","short":"Name of a human - parts and usage","definition":"A human's name with the ability to identify parts and usage.","comment":"Names may be changed, or repudiated, or people may have different names in different contexts. Names may be divided into parts of different type that have variable significance depending on context, though the division into parts does not always matter. With personal names, the different parts might or might not be imbued with some implicit meaning; various cultures associate different importance with the name parts and the degree to which systems must care about name parts around the world varies widely.","min":0,"max":"*","base":{"path":"HumanName","min":0,"max":"*"},"condition":["ele-1"],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":false,"mapping":[{"identity":"rim","map":"n/a"},{"identity":"v2","map":"XPN"},{"identity":"rim","map":"EN (actually, PN)"},{"identity":"servd","map":"ProviderName"}]},{"id":"HumanName.id","path":"HumanName.id","representation":["xmlAttr"],"short":"Unique id for inter-element referencing","definition":"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.","min":0,"max":"1","base":{"path":"Element.id","min":0,"max":"1"},"type":[{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type","valueUrl":"string"}],"code":"http://hl7.org/fhirpath/System.String"}],"isModifier":false,"isSummary":false,"mapping":[{"identity":"rim","map":"n/a"}]},{"id":"HumanName.extension","path":"HumanName.extension","slicing":{"discriminator":[{"type":"value","path":"url"}],"description":"Extensions are always sliced by (at least) url","rules":"open"},"short":"Additional content defined by implementations","definition":"May be used to represent additional information that is not part of the basic definition of the element. To make the use of extensions safe and manageable, there is a strict set of governance  applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.","comment":"There can be no stigma associated with the use of extensions by any application, project, or standard - regardless of the institution or jurisdiction that uses or defines the extensions.  The use of extensions is what allows the FHIR specification to retain a core level of simplicity for everyone.","alias":["extensions","user content"],"min":0,"max":"*","base":{"path":"Element.extension","min":0,"max":"*"},"type":[{"code":"Extension"}],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"},{"key":"ext-1","severity":"error","human":"Must have either extensions or value[x], not both","expression":"extension.exists() != value.exists()","xpath":"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \"value\")])","source":"http://hl7.org/fhir/StructureDefinition/Extension"}],"isModifier":false,"isSummary":false,"mapping":[{"identity":"rim","map":"n/a"}]},{"id":"HumanName.use","path":"HumanName.use","short":"usual | official | temp | nickname | anonymous | old | maiden","definition":"Identifies the purpose for this name.","comment":"Applications can assume that a name is current unless it explicitly says that it is temporary or old.","requirements":"Allows the appropriate name for a particular context of use to be selected from among a set of names.","min":0,"max":"1","base":{"path":"HumanName.use","min":0,"max":"1"},"type":[{"code":"code"}],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":true,"isModifierReason":"This is labeled as \"Is Modifier\" because applications should not mistake a temporary or old name etc.for a current/permanent one","isSummary":true,"binding":{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName","valueString":"NameUse"}],"strength":"required","description":"The use of a human name.","valueSet":"http://hl7.org/fhir/ValueSet/name-use|4.0.1"},"mapping":[{"identity":"v2","map":"XPN.7, but often indicated by which field contains the name"},{"identity":"rim","map":"unique(./use)"},{"identity":"servd","map":"./NamePurpose"}]},{"id":"HumanName.text","path":"HumanName.text","short":"Text representation of the full name","definition":"Specifies the entire name as it should be displayed e.g. on an application UI. This may be provided instead of or as well as the specific parts.","comment":"Can provide both a text representation and parts. Applications updating a name SHALL ensure that when both text and parts are present,  no content is included in the text that isn't found in a part.","requirements":"A renderable, unencoded form.","min":0,"max":"1","base":{"path":"HumanName.text","min":0,"max":"1"},"type":[{"code":"string"}],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":false,"isSummary":true,"mapping":[{"identity":"v2","map":"implied by XPN.11"},{"identity":"rim","map":"./formatted"}]},{"id":"HumanName.family","path":"HumanName.family","short":"Family name (often called 'Surname')","definition":"The part of a name that links to the genealogy. In some cultures (e.g. Eritrea) the family name of a son is the first name of his father.","comment":"Family Name may be decomposed into specific parts using extensions (de, nl, es related cultures).","alias":["surname"],"min":0,"max":"1","base":{"path":"HumanName.family","min":0,"max":"1"},"type":[{"code":"string"}],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":false,"isSummary":true,"mapping":[{"identity":"v2","map":"XPN.1/FN.1"},{"identity":"rim","map":"./part[partType = FAM]"},{"identity":"servd","map":"./FamilyName"}]},{"id":"HumanName.given","path":"HumanName.given","short":"Given names (not always 'first'). Includes middle names","definition":"Given name.","comment":"If only initials are recorded, they may be used in place of the full name parts. Initials may be separated into multiple given names but often aren't due to paractical limitations.  This element is not called \"first name\" since given names do not always come first.","alias":["first name","middle name"],"min":0,"max":"*","base":{"path":"HumanName.given","min":0,"max":"*"},"type":[{"code":"string"}],"orderMeaning":"Given Names appear in the correct order for presenting the name","constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":false,"isSummary":true,"mapping":[{"identity":"v2","map":"XPN.2 + XPN.3"},{"identity":"rim","map":"./part[partType = GIV]"},{"identity":"servd","map":"./GivenNames"}]},{"id":"HumanName.prefix","path":"HumanName.prefix","short":"Parts that come before the name","definition":"Part of the name that is acquired as a title due to academic, legal, employment or nobility status, etc. and that appears at the start of the name.","min":0,"max":"*","base":{"path":"HumanName.prefix","min":0,"max":"*"},"type":[{"code":"string"}],"orderMeaning":"Prefixes appear in the correct order for presenting the name","constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":false,"isSummary":true,"mapping":[{"identity":"v2","map":"XPN.5"},{"identity":"rim","map":"./part[partType = PFX]"},{"identity":"servd","map":"./TitleCode"}]},{"id":"HumanName.suffix","path":"HumanName.suffix","short":"Parts that come after the name","definition":"Part of the name that is acquired as a title due to academic, legal, employment or nobility status, etc. and that appears at the end of the name.","min":0,"max":"*","base":{"path":"HumanName.suffix","min":0,"max":"*"},"type":[{"code":"string"}],"orderMeaning":"Suffixes appear in the correct order for presenting the name","constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":false,"isSummary":true,"mapping":[{"identity":"v2","map":"XPN/4"},{"identity":"rim","map":"./part[partType = SFX]"}]},{"id":"HumanName.period","path":"HumanName.period","short":"Time period when name was/is in use","definition":"Indicates the period of time when this name was valid for the named person.","requirements":"Allows names to be placed in historical context.","min":0,"max":"1","base":{"path":"HumanName.period","min":0,"max":"1"},"type":[{"code":"Period"}],"constraint":[{"key":"ele-1","severity":"error","human":"All FHIR elements must have a @value or children","expression":"hasValue() or (children().count() > id.count())","xpath":"@value|f:*|h:div","source":"http://hl7.org/fhir/StructureDefinition/Element"}],"isModifier":false,"isSummary":true,"mapping":[{"identity":"v2","map":"XPN.13 + XPN.14"},{"identity":"rim","map":"./usablePeriod[type=\"IVL<TS>\"]"},{"identity":"servd","map":"./StartDate and ./EndDate"}]}]},"differential":{"element":[{"id":"HumanName","extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status","valueCode":"normative"},{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version","valueCode":"4.0.0"}],"path":"HumanName","short":"Name of a human - parts and usage","definition":"A human's name with the ability to identify parts and usage.","comment":"Names may be changed, or repudiated, or people may have different names in different contexts. Names may be divided into parts of different type that have variable significance depending on context, though the division into parts does not always matter. With personal names, the different parts might or might not be imbued with some implicit meaning; various cultures associate different importance with the name parts and the degree to which systems must care about name parts around the world varies widely.","min":0,"max":"*","mapping":[{"identity":"v2","map":"XPN"},{"identity":"rim","map":"EN (actually, PN)"},{"identity":"servd","map":"ProviderName"}]},{"id":"HumanName.use","path":"HumanName.use","short":"usual | official | temp | nickname | anonymous | old | maiden","definition":"Identifies the purpose for this name.","comment":"Applications can assume that a name is current unless it explicitly says that it is temporary or old.","requirements":"Allows the appropriate name for a particular context of use to be selected from among a set of names.","min":0,"max":"1","type":[{"code":"code"}],"isModifier":true,"isModifierReason":"This is labeled as \"Is Modifier\" because applications should not mistake a temporary or old name etc.for a current/permanent one","isSummary":true,"binding":{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName","valueString":"NameUse"}],"strength":"required","description":"The use of a human name.","valueSet":"http://hl7.org/fhir/ValueSet/name-use|4.0.1"},"mapping":[{"identity":"v2","map":"XPN.7, but often indicated by which field contains the name"},{"identity":"rim","map":"unique(./use)"},{"identity":"servd","map":"./NamePurpose"}]},{"id":"HumanName.text","path":"HumanName.text","short":"Text representation of the full name","definition":"Specifies the entire name as it should be displayed e.g. on an application UI. This may be provided instead of or as well as the specific parts.","comment":"Can provide both a text representation and parts. Applications updating a name SHALL ensure that when both text and parts are present,  no content is included in the text that isn't found in a part.","requirements":"A renderable, unencoded form.","min":0,"max":"1","type":[{"code":"string"}],"isSummary":true,"mapping":[{"identity":"v2","map":"implied by XPN.11"},{"identity":"rim","map":"./formatted"}]},{"id":"HumanName.family","path":"HumanName.family","short":"Family name (often called 'Surname')","definition":"The part of a name that links to the genealogy. In some cultures (e.g. Eritrea) the family name of a son is the first name of his father.","comment":"Family Name may be decomposed into specific parts using extensions (de, nl, es related cultures).","alias":["surname"],"min":0,"max":"1","type":[{"code":"string"}],"isSummary":true,"mapping":[{"identity":"v2","map":"XPN.1/FN.1"},{"identity":"rim","map":"./part[partType = FAM]"},{"identity":"servd","map":"./FamilyName"}]},{"id":"HumanName.given","path":"HumanName.given","short":"Given names (not always 'first'). Includes middle names","definition":"Given name.","comment":"If only initials are recorded, they may be used in place of the full name parts. Initials may be separated into multiple given names but often aren't due to paractical limitations.  This element is not called \"first name\" since given names do not always come first.","alias":["first name","middle name"],"min":0,"max":"*","type":[{"code":"string"}],"orderMeaning":"Given Names appear in the correct order for presenting the name","isSummary":true,"mapping":[{"identity":"v2","map":"XPN.2 + XPN.3"},{"identity":"rim","map":"./part[partType = GIV]"},{"identity":"servd","map":"./GivenNames"}]},{"id":"HumanName.prefix","path":"HumanName.prefix","short":"Parts that come before the name","definition":"Part of the name that is acquired as a title due to academic, legal, employment or nobility status, etc. and that appears at the start of the name.","min":0,"max":"*","type":[{"code":"string"}],"orderMeaning":"Prefixes appear in the correct order for presenting the name","isSummary":true,"mapping":[{"identity":"v2","map":"XPN.5"},{"identity":"rim","map":"./part[partType = PFX]"},{"identity":"servd","map":"./TitleCode"}]},{"id":"HumanName.suffix","path":"HumanName.suffix","short":"Parts that come after the name","definition":"Part of the name that is acquired as a title due to academic, legal, employment or nobility status, etc. and that appears at the end of the name.","min":0,"max":"*","type":[{"code":"string"}],"orderMeaning":"Suffixes appear in the correct order for presenting the name","isSummary":true,"mapping":[{"identity":"v2","map":"XPN/4"},{"identity":"rim","map":"./part[partType = SFX]"}]},{"id":"HumanName.period","path":"HumanName.period","short":"Time period when name was/is in use","definition":"Indicates the period of time when this name was valid for the named person.","requirements":"Allows names to be placed in historical context.","min":0,"max":"1","type":[{"code":"Period"}],"isSummary":true,"mapping":[{"identity":"v2","map":"XPN.13 + XPN.14"},{"identity":"rim","map":"./usablePeriod[type=\"IVL<TS>\"]"},{"identity":"servd","map":"./StartDate and ./EndDate"}]}]}}
\ No newline at end of file