diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java index 7d1dcef782f..d65c39397c3 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java @@ -101,7 +101,7 @@ public class FhirTerser { return newList; } - private ExtensionDt createEmptyExtensionDt(IBaseExtension theBaseExtension, String theUrl) { + private ExtensionDt createEmptyExtensionDt(IBaseExtension theBaseExtension, String theUrl) { return createEmptyExtensionDt(theBaseExtension, false, theUrl); } @@ -122,13 +122,13 @@ public class FhirTerser { return theSupportsUndeclaredExtensions.addUndeclaredExtension(theIsModifier, theUrl); } - private IBaseExtension createEmptyExtension(IBaseHasExtensions theBaseHasExtensions, String theUrl) { - return (IBaseExtension) theBaseHasExtensions.addExtension().setUrl(theUrl); + private IBaseExtension createEmptyExtension(IBaseHasExtensions theBaseHasExtensions, String theUrl) { + return (IBaseExtension) theBaseHasExtensions.addExtension().setUrl(theUrl); } - private IBaseExtension createEmptyModifierExtension( + private IBaseExtension createEmptyModifierExtension( IBaseHasModifierExtensions theBaseHasModifierExtensions, String theUrl) { - return (IBaseExtension) + return (IBaseExtension) theBaseHasModifierExtensions.addModifierExtension().setUrl(theUrl); } @@ -407,7 +407,7 @@ public class FhirTerser { public String getSinglePrimitiveValueOrNull(IBase theTarget, String thePath) { return getSingleValue(theTarget, thePath, IPrimitiveType.class) - .map(t -> t.getValueAsString()) + .map(IPrimitiveType::getValueAsString) .orElse(null); } @@ -487,7 +487,7 @@ public class FhirTerser { } else { // DSTU3+ final String extensionUrlForLambda = extensionUrl; - List extensions = Collections.emptyList(); + List> extensions = Collections.emptyList(); if (theCurrentObj instanceof IBaseHasExtensions) { extensions = ((IBaseHasExtensions) theCurrentObj) .getExtension().stream() @@ -505,7 +505,7 @@ public class FhirTerser { } } - for (IBaseExtension next : extensions) { + for (IBaseExtension next : extensions) { if (theWantedClass.isAssignableFrom(next.getClass())) { retVal.add((T) next); } @@ -581,7 +581,7 @@ public class FhirTerser { } else { // DSTU3+ final String extensionUrlForLambda = extensionUrl; - List extensions = Collections.emptyList(); + List> extensions = Collections.emptyList(); if (theCurrentObj instanceof IBaseHasModifierExtensions) { extensions = ((IBaseHasModifierExtensions) theCurrentObj) @@ -602,7 +602,7 @@ public class FhirTerser { } } - for (IBaseExtension next : extensions) { + for (IBaseExtension next : extensions) { if (theWantedClass.isAssignableFrom(next.getClass())) { retVal.add((T) next); } @@ -1203,7 +1203,6 @@ public class FhirTerser { public void visit(IBase theElement, IModelVisitor2 theVisitor) { BaseRuntimeElementDefinition def = myContext.getElementDefinition(theElement.getClass()); if (def instanceof BaseRuntimeElementCompositeDefinition) { - BaseRuntimeElementCompositeDefinition defComposite = (BaseRuntimeElementCompositeDefinition) def; visit(theElement, null, def, theVisitor, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } else if (theElement instanceof IBaseExtension) { theVisitor.acceptUndeclaredExtension( @@ -1562,7 +1561,7 @@ public class FhirTerser { throw new DataFormatException(Msg.code(1796) + "Invalid path " + thePath + ": Element of type " + def.getName() + " has no child named " + nextPart + ". Valid names: " + def.getChildrenAndExtension().stream() - .map(t -> t.getElementName()) + .map(BaseRuntimeChildDefinition::getElementName) .sorted() .collect(Collectors.joining(", "))); } @@ -1817,7 +1816,18 @@ public class FhirTerser { if (getResourceToIdMap() == null) { return null; } - return getResourceToIdMap().get(theNext); + + var idFromMap = getResourceToIdMap().get(theNext); + if (idFromMap != null) { + return idFromMap; + } else if (theNext.getIdElement().getIdPart() != null) { + return getResourceToIdMap().values().stream() + .filter(id -> theNext.getIdElement().getIdPart().equals(id.getIdPart())) + .findAny() + .orElse(null); + } else { + return null; + } } private List getOrCreateResourceList() { diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/4837-fix-stringify-parse-copy-contained-duplicate-bug.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/4837-fix-stringify-parse-copy-contained-duplicate-bug.yaml new file mode 100644 index 00000000000..e0bb4aec0ca --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/4837-fix-stringify-parse-copy-contained-duplicate-bug.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 4837 +title: "In the case where a resource was serialized, deserialized, copied and reserialized it resulted in duplication of + contained resources. This has been corrected." diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java index 032f807fcda..54ecc48086c 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java @@ -21,6 +21,7 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Library; import org.hl7.fhir.r4.model.MarkdownType; import org.hl7.fhir.r4.model.Medication; import org.hl7.fhir.r4.model.MedicationAdministration; @@ -28,6 +29,7 @@ import org.hl7.fhir.r4.model.MedicationRequest; import org.hl7.fhir.r4.model.Money; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Organization; +import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient.LinkType; import org.hl7.fhir.r4.model.Practitioner; @@ -1528,6 +1530,27 @@ public class FhirTerserR4Test { return retVal; } + @Test + void copyingAndParsingCreatesDuplicateContainedResources() { + var input = new Library(); + var params = new Parameters(); + var id = "#expansion-parameters-ecr"; + params.setId(id); + params.addParameter("system-version", new StringType("test2")); + var paramsExt = new Extension(); + paramsExt.setUrl("test").setValue(new Reference(id)); + input.addContained(params); + input.addExtension(paramsExt); + final var parser = FhirContext.forR4Cached().newJsonParser(); + var stringified = parser.encodeResourceToString(input); + var parsed = parser.parseResource(stringified); + var copy = ((Library) parsed).copy(); + assertEquals(1, copy.getContained().size()); + var stringifiedCopy = parser.encodeResourceToString(copy); + var parsedCopy = parser.parseResource(stringifiedCopy); + assertEquals(1, ((Library) parsedCopy).getContained().size()); + } + /** * See http://stackoverflow.com/questions/182636/how-to-determine-the-class-of-a-generic-type */