diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java index 71bbaedf1e6..bc9b105aaca 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.context.RuntimeChildChoiceDefinition; import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition; import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.context.RuntimePrimitiveDatatypeNarrativeDefinition; @@ -583,10 +584,19 @@ class ParserState { return; } - if ((child.getMax() == 0 || child.getMax() == 1) && !myParsedNonRepeatableNames.add(theChildName)) { - myErrorHandler.unexpectedRepeatingElement(null, theChildName); - push(new SwallowChildrenWholeState(getPreResourceState())); - return; + if ((child.getMax() == 0 || child.getMax() == 1)) { + String nameToCheck; + if (child instanceof RuntimeChildChoiceDefinition) { + RuntimeChildChoiceDefinition choiceChild = (RuntimeChildChoiceDefinition) child; + nameToCheck = choiceChild.getField().getName(); + } else { + nameToCheck = theChildName; + } + if(!myParsedNonRepeatableNames.add(nameToCheck)) { + myErrorHandler.unexpectedRepeatingElement(null, nameToCheck); + push(new SwallowChildrenWholeState(getPreResourceState())); + return; + } } BaseRuntimeElementDefinition target = child.getChildByName(theChildName); diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4574-fix-repeating-choice-data-type-to-throw-error.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4574-fix-repeating-choice-data-type-to-throw-error.yaml new file mode 100644 index 00000000000..4acb27f55d8 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4574-fix-repeating-choice-data-type-to-throw-error.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 4468 +title: "When parsing a FHIR resource which has multiple value[x] choice fields, the parser does not call the IParserErrorHandler#unexpectedRepeatingElement method on the configured parser error handler. Thanks to Max Bureck for the + pull request!" diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/Dstu2_1JsonParserErrorHandlerTest.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/Dstu2_1JsonParserErrorHandlerTest.java new file mode 100644 index 00000000000..a010d90ce10 --- /dev/null +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/Dstu2_1JsonParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class Dstu2_1JsonParserErrorHandlerTest extends AbstractJsonParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forDstu2_1(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/Dstu2_1XmlParserErrorHandlerTest.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/Dstu2_1XmlParserErrorHandlerTest.java new file mode 100644 index 00000000000..7b0ac101426 --- /dev/null +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/Dstu2_1XmlParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class Dstu2_1XmlParserErrorHandlerTest extends AbstractXmlParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forDstu2_1(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2JsonParserErrorHandlerTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2JsonParserErrorHandlerTest.java new file mode 100644 index 00000000000..d3b22e24146 --- /dev/null +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2JsonParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class Dstu2JsonParserErrorHandlerTest extends AbstractJsonParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forDstu2(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2XmlParserErrorHandlerTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2XmlParserErrorHandlerTest.java new file mode 100644 index 00000000000..52315a7f847 --- /dev/null +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2XmlParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class Dstu2XmlParserErrorHandlerTest extends AbstractXmlParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forDstu2(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/Dstu3JsonParserErrorHandlerTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/Dstu3JsonParserErrorHandlerTest.java new file mode 100644 index 00000000000..8643e799595 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/Dstu3JsonParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class Dstu3JsonParserErrorHandlerTest extends AbstractJsonParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forDstu3(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/Dstu3XmlParserErrorHandlerTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/Dstu3XmlParserErrorHandlerTest.java new file mode 100644 index 00000000000..da6b1ccdc44 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/Dstu3XmlParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class Dstu3XmlParserErrorHandlerTest extends AbstractXmlParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forDstu3(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2Hl7OrgJsonParserErrorHandlerTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2Hl7OrgJsonParserErrorHandlerTest.java new file mode 100644 index 00000000000..8c2280247b0 --- /dev/null +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2Hl7OrgJsonParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class Dstu2Hl7OrgJsonParserErrorHandlerTest extends AbstractJsonParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2Hl7OrgXmlParserErrorHandlerTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2Hl7OrgXmlParserErrorHandlerTest.java new file mode 100644 index 00000000000..86adaccd6c6 --- /dev/null +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/Dstu2Hl7OrgXmlParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class Dstu2Hl7OrgXmlParserErrorHandlerTest extends AbstractXmlParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/R4JsonParserErrorHandlerTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/R4JsonParserErrorHandlerTest.java new file mode 100644 index 00000000000..f39b517a2d2 --- /dev/null +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/R4JsonParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class R4JsonParserErrorHandlerTest extends AbstractJsonParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forR4(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/R4XmlParserErrorHandlerTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/R4XmlParserErrorHandlerTest.java new file mode 100644 index 00000000000..291a676bb93 --- /dev/null +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/R4XmlParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class R4XmlParserErrorHandlerTest extends AbstractXmlParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forR4(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/parser/R4BJsonParserErrorHandlerTest.java b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/parser/R4BJsonParserErrorHandlerTest.java new file mode 100644 index 00000000000..2f848d16cef --- /dev/null +++ b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/parser/R4BJsonParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class R4BJsonParserErrorHandlerTest extends AbstractJsonParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forR4B(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/parser/R4BXmlParserErrorHandlerTest.java b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/parser/R4BXmlParserErrorHandlerTest.java new file mode 100644 index 00000000000..ec1c82e4127 --- /dev/null +++ b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/parser/R4BXmlParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class R4BXmlParserErrorHandlerTest extends AbstractXmlParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forR4B(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-r5/src/test/java/ca/uhn/fhir/parser/R5JsonParserErrorHandlerTest.java b/hapi-fhir-structures-r5/src/test/java/ca/uhn/fhir/parser/R5JsonParserErrorHandlerTest.java new file mode 100644 index 00000000000..c825cc02988 --- /dev/null +++ b/hapi-fhir-structures-r5/src/test/java/ca/uhn/fhir/parser/R5JsonParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class R5JsonParserErrorHandlerTest extends AbstractJsonParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forR5(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-structures-r5/src/test/java/ca/uhn/fhir/parser/R5XmlParserErrorHandlerTest.java b/hapi-fhir-structures-r5/src/test/java/ca/uhn/fhir/parser/R5XmlParserErrorHandlerTest.java new file mode 100644 index 00000000000..ce00190b342 --- /dev/null +++ b/hapi-fhir-structures-r5/src/test/java/ca/uhn/fhir/parser/R5XmlParserErrorHandlerTest.java @@ -0,0 +1,13 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public class R5XmlParserErrorHandlerTest extends AbstractXmlParserErrorHandlerTest { + + private static FhirContext ourCtx = FhirContext.forR5(); + + @Override + protected FhirContext getFhirContext() { + return ourCtx; + } +} diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index beda69ce8b4..69a962cd75d 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -15,6 +15,13 @@ HAPI FHIR Test Utilities http://jamesagnew.github.io/hapi-fhir/ + + + ${maven.compiler.testSource} + ${maven.compiler.testTarget} + ${maven.compiler.testRelease} + + diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/parser/AbstractJsonParserErrorHandlerTest.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/parser/AbstractJsonParserErrorHandlerTest.java new file mode 100644 index 00000000000..ca60b03155e --- /dev/null +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/parser/AbstractJsonParserErrorHandlerTest.java @@ -0,0 +1,28 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +abstract public non-sealed class AbstractJsonParserErrorHandlerTest extends AbstractParserErrorHandlerTest { + + private static String PATIENT_DUPLICATE_CHOICE = + """ + { + "resourceType": "Patient", + "deceasedBoolean": "true", + "deceasedDateTime": "2022-02-07T13:28:17-05:00" + } + """; + + protected abstract FhirContext getFhirContext(); + + @Override + protected IParser createParser() { + return getFhirContext().newJsonParser(); + } + + @Override + protected String createResourceWithRepeatingChoice() { + return PATIENT_DUPLICATE_CHOICE; + } + +} diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/parser/AbstractParserErrorHandlerTest.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/parser/AbstractParserErrorHandlerTest.java new file mode 100644 index 00000000000..30ee45d9b8d --- /dev/null +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/parser/AbstractParserErrorHandlerTest.java @@ -0,0 +1,41 @@ +package ca.uhn.fhir.parser; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +/** + * Defines FHIR version independent tests for testing parser error handling. In version dependent + * projects, the sub-types {@link AbstractXmlParserErrorHandlerTest}, {@link + * AbstractJsonParserErrorHandlerTest} can be sub-classed to create a complete test. + */ +public abstract sealed class AbstractParserErrorHandlerTest + permits AbstractXmlParserErrorHandlerTest, AbstractJsonParserErrorHandlerTest { + + protected abstract IParser createParser(); + + protected abstract String createResourceWithRepeatingChoice(); + + @Test + public void testRepeatingChoiceHandled() { + + // Let error handler throw custom exception on unexpectedRepeatingElement + @SuppressWarnings("serial") + class RepeatingChoiceHandledException extends RuntimeException {} + IParserErrorHandler errorHandler = new ErrorHandlerAdapter() { + @Override + public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) { + throw new RepeatingChoiceHandledException(); + } + }; + + IParser parser = createParser(); + parser.setParserErrorHandler(errorHandler); + + String resourceStr = createResourceWithRepeatingChoice(); + assertThrows( + RepeatingChoiceHandledException.class, + () -> { + parser.parseResource(resourceStr); + }); + } +} diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/parser/AbstractXmlParserErrorHandlerTest.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/parser/AbstractXmlParserErrorHandlerTest.java new file mode 100644 index 00000000000..c14469b3330 --- /dev/null +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/parser/AbstractXmlParserErrorHandlerTest.java @@ -0,0 +1,26 @@ +package ca.uhn.fhir.parser; + +import ca.uhn.fhir.context.FhirContext; + +public abstract non-sealed class AbstractXmlParserErrorHandlerTest extends AbstractParserErrorHandlerTest { + + private static String PATIENT_DUPLICATE_CHOICE = + """ + + + + """; + + protected abstract FhirContext getFhirContext(); + + @Override + protected IParser createParser() { + return getFhirContext().newXmlParser(); + } + + @Override + protected String createResourceWithRepeatingChoice() { + return PATIENT_DUPLICATE_CHOICE; + } + +} diff --git a/pom.xml b/pom.xml index d8285670982..73ee58660a3 100644 --- a/pom.xml +++ b/pom.xml @@ -878,6 +878,11 @@ Dominique Villard Doctolib + + boereck + Max Bureck + Fraunhofer FOKUS +