mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-02-08 05:58:27 +00:00
Fix #414: Parser should not choke with a NullPointerException if it
encounters an extension without a URL
This commit is contained in:
parent
22c2d301dc
commit
26cd316343
@ -130,6 +130,7 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa
|
|||||||
myModifier = theModifier;
|
myModifier = theModifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ExtensionDt setUrl(String theUrl) {
|
public ExtensionDt setUrl(String theUrl) {
|
||||||
myUrl = theUrl != null ? new StringDt(theUrl) : myUrl;
|
myUrl = theUrl != null ? new StringDt(theUrl) : myUrl;
|
||||||
return this;
|
return this;
|
||||||
@ -140,6 +141,7 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ExtensionDt setValue(IBaseDatatype theValue) {
|
public ExtensionDt setValue(IBaseDatatype theValue) {
|
||||||
myValue = theValue;
|
myValue = theValue;
|
||||||
return this;
|
return this;
|
||||||
|
@ -50,4 +50,9 @@ public class ErrorHandlerAdapter implements IParserErrorHandler {
|
|||||||
// NOP
|
// NOP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void missingRequiredElement(IParseLocation theLocation, String theElementName) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package ca.uhn.fhir.parser;
|
package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
@ -35,13 +37,22 @@ public interface IParserErrorHandler {
|
|||||||
*/
|
*/
|
||||||
void containedResourceWithNoId(IParseLocation theLocation);
|
void containedResourceWithNoId(IParseLocation theLocation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource was missing a required element
|
||||||
|
*
|
||||||
|
* @param theLocation
|
||||||
|
* The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change.
|
||||||
|
* @param theReference The actual invalid reference (e.g. "#3")
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
void missingRequiredElement(IParseLocation theLocation, String theElementName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when an element repetition (e.g. a second repetition of something) is found for a field
|
* Invoked when an element repetition (e.g. a second repetition of something) is found for a field
|
||||||
* which is non-repeating.
|
* which is non-repeating.
|
||||||
*
|
*
|
||||||
* @param theLocation
|
* @param theLocation
|
||||||
* The location in the document. WILL ALWAYS BE NULL currently, as this is not yet implemented, but this parameter is included so that locations can be added in the future without
|
* The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change.
|
||||||
* changing the API.
|
|
||||||
* @param theElementName
|
* @param theElementName
|
||||||
* The name of the element that was found.
|
* The name of the element that was found.
|
||||||
* @since 1.2
|
* @since 1.2
|
||||||
@ -52,8 +63,7 @@ public interface IParserErrorHandler {
|
|||||||
* Invoked when an unknown element is found in the document.
|
* Invoked when an unknown element is found in the document.
|
||||||
*
|
*
|
||||||
* @param theLocation
|
* @param theLocation
|
||||||
* The location in the document. WILL ALWAYS BE NULL currently, as this is not yet implemented, but this parameter is included so that locations can be added in the future without
|
* The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change.
|
||||||
* changing the API.
|
|
||||||
* @param theAttributeName
|
* @param theAttributeName
|
||||||
* The name of the attribute that was found.
|
* The name of the attribute that was found.
|
||||||
*/
|
*/
|
||||||
@ -63,8 +73,7 @@ public interface IParserErrorHandler {
|
|||||||
* Invoked when an unknown element is found in the document.
|
* Invoked when an unknown element is found in the document.
|
||||||
*
|
*
|
||||||
* @param theLocation
|
* @param theLocation
|
||||||
* The location in the document. WILL ALWAYS BE NULL currently, as this is not yet implemented, but this parameter is included so that locations can be added in the future without
|
* The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change.
|
||||||
* changing the API.
|
|
||||||
* @param theElementName
|
* @param theElementName
|
||||||
* The name of the element that was found.
|
* The name of the element that was found.
|
||||||
*/
|
*/
|
||||||
@ -75,8 +84,7 @@ public interface IParserErrorHandler {
|
|||||||
* it is a local reference to an unknown contained resource)
|
* it is a local reference to an unknown contained resource)
|
||||||
*
|
*
|
||||||
* @param theLocation
|
* @param theLocation
|
||||||
* The location in the document. WILL ALWAYS BE NULL currently, as this is not yet implemented, but this parameter is included so that locations can be added in the future without
|
* The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change.
|
||||||
* changing the API.
|
|
||||||
* @param theReference The actual invalid reference (e.g. "#3")
|
* @param theReference The actual invalid reference (e.g. "#3")
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
@ -88,7 +96,13 @@ public interface IParserErrorHandler {
|
|||||||
* locations can be added to the API in a future release without changing the API.
|
* locations can be added to the API in a future release without changing the API.
|
||||||
*/
|
*/
|
||||||
public interface IParseLocation {
|
public interface IParseLocation {
|
||||||
// nothing for now
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the parent element (the element containing the element currently being parsed)
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
String getParentElementName();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1299,7 +1299,20 @@ public class JsonParser extends BaseParser implements IParser {
|
|||||||
private void parseExtension(ParserState<?> theState, JsonArray theValues, boolean theIsModifier) {
|
private void parseExtension(ParserState<?> theState, JsonArray theValues, boolean theIsModifier) {
|
||||||
for (int i = 0; i < theValues.size(); i++) {
|
for (int i = 0; i < theValues.size(); i++) {
|
||||||
JsonObject nextExtObj = (JsonObject) theValues.get(i);
|
JsonObject nextExtObj = (JsonObject) theValues.get(i);
|
||||||
String url = nextExtObj.get("url").getAsString();
|
JsonElement jsonElement = nextExtObj.get("url");
|
||||||
|
String url;
|
||||||
|
if (!(jsonElement instanceof JsonPrimitive)) {
|
||||||
|
String parentElementName;
|
||||||
|
if (theIsModifier) {
|
||||||
|
parentElementName = "modifierExtension";
|
||||||
|
} else {
|
||||||
|
parentElementName = "extension";
|
||||||
|
}
|
||||||
|
getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url");
|
||||||
|
url = null;
|
||||||
|
} else {
|
||||||
|
url = jsonElement.getAsString();
|
||||||
|
}
|
||||||
theState.enteringNewElementExtension(null, url, theIsModifier);
|
theState.enteringNewElementExtension(null, url, theIsModifier);
|
||||||
for (Iterator<Entry<String, JsonElement>> iter = nextExtObj.entrySet().iterator(); iter.hasNext();) {
|
for (Iterator<Entry<String, JsonElement>> iter = nextExtObj.entrySet().iterator(); iter.hasNext();) {
|
||||||
String next = iter.next().getKey();
|
String next = iter.next().getKey();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package ca.uhn.fhir.parser;
|
package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
@ -85,4 +86,11 @@ public class LenientErrorHandler implements IParserErrorHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void missingRequiredElement(IParseLocation theLocation, String theElementName) {
|
||||||
|
if (myLogErrors) {
|
||||||
|
ourLog.warn("Resource is missing required element: {}", theElementName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
|
||||||
|
|
||||||
|
class ParseLocation implements IParseLocation {
|
||||||
|
|
||||||
|
private String myParentElementName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public ParseLocation(String theParentElementName) {
|
||||||
|
super();
|
||||||
|
myParentElementName = theParentElementName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getParentElementName() {
|
||||||
|
return myParentElementName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -830,7 +830,8 @@ class ParserState<T> {
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public void enteringNewElementExtension(StartElement theElement, String theUrlAttr, boolean theIsModifier) {
|
public void enteringNewElementExtension(StartElement theElement, String theUrlAttr, boolean theIsModifier) {
|
||||||
if (myPreResourceState != null && getCurrentElement() instanceof ISupportsUndeclaredExtensions) {
|
if (myPreResourceState != null && getCurrentElement() instanceof ISupportsUndeclaredExtensions) {
|
||||||
ExtensionDt newExtension = new ExtensionDt(theIsModifier, theUrlAttr);
|
ExtensionDt newExtension = new ExtensionDt(theIsModifier);
|
||||||
|
newExtension.setUrl(theUrlAttr);
|
||||||
ISupportsUndeclaredExtensions elem = (ISupportsUndeclaredExtensions) getCurrentElement();
|
ISupportsUndeclaredExtensions elem = (ISupportsUndeclaredExtensions) getCurrentElement();
|
||||||
elem.addUndeclaredExtension(newExtension);
|
elem.addUndeclaredExtension(newExtension);
|
||||||
ExtensionState newState = new ExtensionState(myPreResourceState, newExtension);
|
ExtensionState newState = new ExtensionState(myPreResourceState, newExtension);
|
||||||
|
@ -56,5 +56,19 @@ public class StrictErrorHandler implements IParserErrorHandler {
|
|||||||
throw new DataFormatException("Resource has invalid reference: " + theReference);
|
throw new DataFormatException("Resource has invalid reference: " + theReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void missingRequiredElement(IParseLocation theLocation, String theElementName) {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append("Resource is missing required element '");
|
||||||
|
b.append(theElementName);
|
||||||
|
b.append("'");
|
||||||
|
if (theLocation != null) {
|
||||||
|
b.append(" in parent element '");
|
||||||
|
b.append(theLocation.getParentElementName());
|
||||||
|
b.append("'");
|
||||||
|
}
|
||||||
|
throw new DataFormatException(b.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -205,16 +205,24 @@ public class XmlParser extends BaseParser implements IParser {
|
|||||||
|
|
||||||
if ("extension".equals(elem.getName().getLocalPart())) {
|
if ("extension".equals(elem.getName().getLocalPart())) {
|
||||||
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
|
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
|
||||||
|
String url;
|
||||||
if (urlAttr == null || isBlank(urlAttr.getValue())) {
|
if (urlAttr == null || isBlank(urlAttr.getValue())) {
|
||||||
throw new DataFormatException("Extension element has no 'url' attribute");
|
getErrorHandler().missingRequiredElement(new ParseLocation("extension"), "url");
|
||||||
|
url = null;
|
||||||
|
} else {
|
||||||
|
url = urlAttr.getValue();
|
||||||
}
|
}
|
||||||
parserState.enteringNewElementExtension(elem, urlAttr.getValue(), false);
|
parserState.enteringNewElementExtension(elem, url, false);
|
||||||
} else if ("modifierExtension".equals(elem.getName().getLocalPart())) {
|
} else if ("modifierExtension".equals(elem.getName().getLocalPart())) {
|
||||||
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
|
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
|
||||||
|
String url;
|
||||||
if (urlAttr == null || isBlank(urlAttr.getValue())) {
|
if (urlAttr == null || isBlank(urlAttr.getValue())) {
|
||||||
throw new DataFormatException("Extension element has no 'url' attribute");
|
getErrorHandler().missingRequiredElement(new ParseLocation("modifierExtension"), "url");
|
||||||
|
url = null;
|
||||||
|
} else {
|
||||||
|
url = urlAttr.getValue();
|
||||||
}
|
}
|
||||||
parserState.enteringNewElementExtension(elem, urlAttr.getValue(), true);
|
parserState.enteringNewElementExtension(elem, url, true);
|
||||||
} else {
|
} else {
|
||||||
String elementName = elem.getName().getLocalPart();
|
String elementName = elem.getName().getLocalPart();
|
||||||
parserState.enteringNewElement(namespaceURI, elementName);
|
parserState.enteringNewElement(namespaceURI, elementName);
|
||||||
|
@ -23,6 +23,7 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -106,8 +107,6 @@ public class JsonParserDstu2Test {
|
|||||||
assertEquals("ORG", o.getName());
|
assertEquals("ORG", o.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #308
|
* See #308
|
||||||
*/
|
*/
|
||||||
@ -125,6 +124,7 @@ public class JsonParserDstu2Test {
|
|||||||
obs = p.parseResource(ReportObservation.class, encoded);
|
obs = p.parseResource(ReportObservation.class, encoded);
|
||||||
assertEquals(true, obs.getReadOnly().getValue().booleanValue());
|
assertEquals(true, obs.getReadOnly().getValue().booleanValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #390
|
* See #390
|
||||||
@ -245,7 +245,7 @@ public class JsonParserDstu2Test {
|
|||||||
p = (Patient) ourCtx.newJsonParser().parseResource("{\"resourceType\":\"Patient\",\"language\":[\"en_CA\"]}");
|
p = (Patient) ourCtx.newJsonParser().parseResource("{\"resourceType\":\"Patient\",\"language\":[\"en_CA\"]}");
|
||||||
assertEquals("en_CA", p.getLanguage().getValue());
|
assertEquals("en_CA", p.getLanguage().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeAndParseMetaProfileAndTags() {
|
public void testEncodeAndParseMetaProfileAndTags() {
|
||||||
Patient p = new Patient();
|
Patient p = new Patient();
|
||||||
@ -302,7 +302,6 @@ public class JsonParserDstu2Test {
|
|||||||
assertEquals(new Tag("scheme2", "term2", "label2"), tagList.get(1));
|
assertEquals(new Tag("scheme2", "term2", "label2"), tagList.get(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #336
|
* See #336
|
||||||
*/
|
*/
|
||||||
@ -360,7 +359,7 @@ public class JsonParserDstu2Test {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeAndParseSecurityLabels() {
|
public void testEncodeAndParseSecurityLabels() {
|
||||||
Patient p = new Patient();
|
Patient p = new Patient();
|
||||||
@ -425,7 +424,7 @@ public class JsonParserDstu2Test {
|
|||||||
assertEquals("VERSION2", label.getVersion());
|
assertEquals("VERSION2", label.getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeBundleNewBundleNoText() {
|
public void testEncodeBundleNewBundleNoText() {
|
||||||
|
|
||||||
@ -446,6 +445,7 @@ public class JsonParserDstu2Test {
|
|||||||
assertThat(val, not(containsString("text")));
|
assertThat(val, not(containsString("text")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeBundleOldBundleNoText() {
|
public void testEncodeBundleOldBundleNoText() {
|
||||||
@ -484,7 +484,7 @@ public class JsonParserDstu2Test {
|
|||||||
"}"));
|
"}"));
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeDoesntIncludeUuidId() {
|
public void testEncodeDoesntIncludeUuidId() {
|
||||||
Patient p = new Patient();
|
Patient p = new Patient();
|
||||||
@ -535,7 +535,6 @@ public class JsonParserDstu2Test {
|
|||||||
assertThat(encoded, not(containsString("Label")));
|
assertThat(encoded, not(containsString("Label")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeExtensionInPrimitiveElement() {
|
public void testEncodeExtensionInPrimitiveElement() {
|
||||||
|
|
||||||
@ -565,7 +564,7 @@ public class JsonParserDstu2Test {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeExtensionUndeclaredNonModifier() {
|
public void testEncodeExtensionUndeclaredNonModifier() {
|
||||||
Observation obs = new Observation();
|
Observation obs = new Observation();
|
||||||
@ -603,6 +602,7 @@ public class JsonParserDstu2Test {
|
|||||||
assertEquals("ext_url_value", ((StringDt)obs.getUndeclaredExtensions().get(0).getValue()).getValue());
|
assertEquals("ext_url_value", ((StringDt)obs.getUndeclaredExtensions().get(0).getValue()).getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeExtensionUndeclaredNonModifierWithChildExtension() {
|
public void testEncodeExtensionUndeclaredNonModifierWithChildExtension() {
|
||||||
Observation obs = new Observation();
|
Observation obs = new Observation();
|
||||||
@ -1362,6 +1362,66 @@ public class JsonParserDstu2Test {
|
|||||||
assertEquals("patient family", p.getNameFirstRep().getFamilyAsSingleString());
|
assertEquals("patient family", p.getNameFirstRep().getFamilyAsSingleString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #414
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseJsonExtensionWithoutUrl() {
|
||||||
|
//@formatter:off
|
||||||
|
String input =
|
||||||
|
"{\"resourceType\":\"Patient\"," +
|
||||||
|
"\"extension\":[ {\"valueDateTime\":\"2011-01-02T11:13:15\"} ]" +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newJsonParser();
|
||||||
|
parser.setParserErrorHandler(new LenientErrorHandler());
|
||||||
|
Patient parsed = (Patient) parser.parseResource(input);
|
||||||
|
assertEquals(1, parsed.getAllUndeclaredExtensions().size());
|
||||||
|
assertEquals(null, parsed.getAllUndeclaredExtensions().get(0).getUrl());
|
||||||
|
assertEquals("2011-01-02T11:13:15", parsed.getAllUndeclaredExtensions().get(0).getValueAsPrimitive().getValueAsString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser = ourCtx.newJsonParser();
|
||||||
|
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||||
|
parser.parseResource(input);
|
||||||
|
fail();
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
assertEquals("Resource is missing required element 'url' in parent element 'extension'", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #414
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseJsonModifierExtensionWithoutUrl() {
|
||||||
|
//@formatter:off
|
||||||
|
String input =
|
||||||
|
"{\"resourceType\":\"Patient\"," +
|
||||||
|
"\"modifierExtension\":[ {\"valueDateTime\":\"2011-01-02T11:13:15\"} ]" +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newJsonParser();
|
||||||
|
parser.setParserErrorHandler(new LenientErrorHandler());
|
||||||
|
Patient parsed = (Patient) parser.parseResource(input);
|
||||||
|
assertEquals(1, parsed.getAllUndeclaredExtensions().size());
|
||||||
|
assertEquals(null, parsed.getAllUndeclaredExtensions().get(0).getUrl());
|
||||||
|
assertEquals("2011-01-02T11:13:15", parsed.getAllUndeclaredExtensions().get(0).getValueAsPrimitive().getValueAsString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser = ourCtx.newJsonParser();
|
||||||
|
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||||
|
parser.parseResource(input);
|
||||||
|
fail();
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
assertEquals("Resource is missing required element 'url' in parent element 'modifierExtension'", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParseMetadata() throws Exception {
|
public void testParseMetadata() throws Exception {
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
|
@ -68,6 +68,69 @@ public class XmlParserDstu2Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #414
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseXmlExtensionWithoutUrl() {
|
||||||
|
//@formatter:off
|
||||||
|
String input = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
|
||||||
|
" <extension>\n" +
|
||||||
|
" <valueDateTime value=\"2011-01-02T11:13:15\"/>\n" +
|
||||||
|
" </extension>\n" +
|
||||||
|
"</Patient>";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newXmlParser();
|
||||||
|
parser.setParserErrorHandler(new LenientErrorHandler());
|
||||||
|
Patient parsed = (Patient) parser.parseResource(input);
|
||||||
|
assertEquals(1, parsed.getAllUndeclaredExtensions().size());
|
||||||
|
assertEquals(null, parsed.getAllUndeclaredExtensions().get(0).getUrl());
|
||||||
|
assertEquals("2011-01-02T11:13:15", parsed.getAllUndeclaredExtensions().get(0).getValueAsPrimitive().getValueAsString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser = ourCtx.newXmlParser();
|
||||||
|
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||||
|
parser.parseResource(input);
|
||||||
|
fail();
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
assertEquals("Resource is missing required element 'url' in parent element 'extension'", e.getCause().getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #414
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseXmlModifierExtensionWithoutUrl() {
|
||||||
|
//@formatter:off
|
||||||
|
String input = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
|
||||||
|
" <modifierExtension>\n" +
|
||||||
|
" <valueDateTime value=\"2011-01-02T11:13:15\"/>\n" +
|
||||||
|
" </modifierExtension>\n" +
|
||||||
|
"</Patient>";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newXmlParser();
|
||||||
|
parser.setParserErrorHandler(new LenientErrorHandler());
|
||||||
|
Patient parsed = (Patient) parser.parseResource(input);
|
||||||
|
assertEquals(1, parsed.getAllUndeclaredExtensions().size());
|
||||||
|
assertEquals(null, parsed.getAllUndeclaredExtensions().get(0).getUrl());
|
||||||
|
assertEquals("2011-01-02T11:13:15", parsed.getAllUndeclaredExtensions().get(0).getValueAsPrimitive().getValueAsString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser = ourCtx.newXmlParser();
|
||||||
|
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||||
|
parser.parseResource(input);
|
||||||
|
fail();
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
assertEquals("Resource is missing required element 'url' in parent element 'modifierExtension'", e.getCause().getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a contained resource refers to a contained resource that comes after it, it should still be successfully
|
* If a contained resource refers to a contained resource that comes after it, it should still be successfully
|
||||||
* woven together.
|
* woven together.
|
||||||
|
@ -3,9 +3,13 @@ package org.hl7.fhir.dstu3.model;
|
|||||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
public abstract class BaseExtension extends Type implements IBaseExtension<Extension, Type>, IBaseHasExtensions {
|
public abstract class BaseExtension extends Type implements IBaseExtension<Extension, Type>, IBaseHasExtensions {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Extension setValue(IBaseDatatype theValue) {
|
public Extension setValue(IBaseDatatype theValue) {
|
||||||
setValue((Type)theValue);
|
setValue((Type)theValue);
|
||||||
@ -14,4 +18,13 @@ public abstract class BaseExtension extends Type implements IBaseExtension<Exten
|
|||||||
|
|
||||||
public abstract Extension setValue(Type theValue);
|
public abstract Extension setValue(Type theValue);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of this extension cast as a {@link IPrimitiveType}. This method is just a convenience method for easy chaining.
|
||||||
|
*/
|
||||||
|
public IPrimitiveType<?> getValueAsPrimitive() {
|
||||||
|
return (IPrimitiveType<?>)getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ import org.hl7.fhir.exceptions.FHIRException;
|
|||||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
||||||
|
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.annotation.Child;
|
import ca.uhn.fhir.model.api.annotation.Child;
|
||||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||||
@ -388,6 +389,5 @@ public class Extension extends BaseExtension implements IBaseExtension<Extension
|
|||||||
return super.isEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(url, value);
|
return super.isEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(url, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ public class ErrorHandlerTest {
|
|||||||
new ErrorHandlerAdapter().unknownElement(null, null);
|
new ErrorHandlerAdapter().unknownElement(null, null);
|
||||||
new ErrorHandlerAdapter().containedResourceWithNoId(null);
|
new ErrorHandlerAdapter().containedResourceWithNoId(null);
|
||||||
new ErrorHandlerAdapter().unknownReference(null, null);
|
new ErrorHandlerAdapter().unknownReference(null, null);
|
||||||
|
new ErrorHandlerAdapter().missingRequiredElement(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -56,6 +56,38 @@ public class JsonParserDstu3Test {
|
|||||||
public void after() {
|
public void after() {
|
||||||
ourCtx.setNarrativeGenerator(null);
|
ourCtx.setNarrativeGenerator(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #414
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseJsonModifierExtensionWithoutUrl() {
|
||||||
|
//@formatter:off
|
||||||
|
String input =
|
||||||
|
"{\"resourceType\":\"Patient\"," +
|
||||||
|
"\"modifierExtension\":[ {\"valueDateTime\":\"2011-01-02T11:13:15\"} ]" +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newJsonParser();
|
||||||
|
parser.setParserErrorHandler(new LenientErrorHandler());
|
||||||
|
Patient parsed = (Patient) parser.parseResource(input);
|
||||||
|
assertEquals(1, parsed.getModifierExtension().size());
|
||||||
|
assertEquals(null, parsed.getModifierExtension().get(0).getUrl());
|
||||||
|
assertEquals("2011-01-02T11:13:15", parsed.getModifierExtension().get(0).getValueAsPrimitive().getValueAsString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser = ourCtx.newJsonParser();
|
||||||
|
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||||
|
parser.parseResource(input);
|
||||||
|
fail();
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
assertEquals("Resource is missing required element 'url' in parent element 'modifierExtension'", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParseMissingArray() throws IOException {
|
public void testParseMissingArray() throws IOException {
|
||||||
@ -82,6 +114,8 @@ public class JsonParserDstu3Test {
|
|||||||
assertThat(output, containsString("\"div\": \"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">VALUE</div>\""));
|
assertThat(output, containsString("\"div\": \"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">VALUE</div>\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeNarrativeShouldIncludeNamespaceWithProcessingInstruction() {
|
public void testEncodeNarrativeShouldIncludeNamespaceWithProcessingInstruction() {
|
||||||
|
|
||||||
@ -137,6 +171,37 @@ public class JsonParserDstu3Test {
|
|||||||
assertThat(encode, containsString("\"value\": \"APPID\""));
|
assertThat(encode, containsString("\"value\": \"APPID\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #414
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseJsonExtensionWithoutUrl() {
|
||||||
|
//@formatter:off
|
||||||
|
String input =
|
||||||
|
"{\"resourceType\":\"Patient\"," +
|
||||||
|
"\"extension\":[ {\"valueDateTime\":\"2011-01-02T11:13:15\"} ]" +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newJsonParser();
|
||||||
|
parser.setParserErrorHandler(new LenientErrorHandler());
|
||||||
|
Patient parsed = (Patient) parser.parseResource(input);
|
||||||
|
assertEquals(1, parsed.getExtension().size());
|
||||||
|
assertEquals(null, parsed.getExtension().get(0).getUrl());
|
||||||
|
assertEquals("2011-01-02T11:13:15", parsed.getExtension().get(0).getValueAsPrimitive().getValueAsString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser = ourCtx.newJsonParser();
|
||||||
|
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||||
|
parser.parseResource(input);
|
||||||
|
fail();
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
assertEquals("Resource is missing required element 'url' in parent element 'extension'", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #344
|
* See #344
|
||||||
*/
|
*/
|
||||||
|
@ -23,8 +23,6 @@ import java.io.IOException;
|
|||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZoneOffset;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
@ -78,6 +76,69 @@ public class XmlParserDstu3Test {
|
|||||||
ourCtx.setNarrativeGenerator(null);
|
ourCtx.setNarrativeGenerator(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #414
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseXmlExtensionWithoutUrl() {
|
||||||
|
//@formatter:off
|
||||||
|
String input = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
|
||||||
|
" <extension>\n" +
|
||||||
|
" <valueDateTime value=\"2011-01-02T11:13:15\"/>\n" +
|
||||||
|
" </extension>\n" +
|
||||||
|
"</Patient>";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newXmlParser();
|
||||||
|
parser.setParserErrorHandler(new LenientErrorHandler());
|
||||||
|
Patient parsed = (Patient) parser.parseResource(input);
|
||||||
|
assertEquals(1, parsed.getExtension().size());
|
||||||
|
assertEquals(null, parsed.getExtension().get(0).getUrl());
|
||||||
|
assertEquals("2011-01-02T11:13:15", parsed.getExtension().get(0).getValueAsPrimitive().getValueAsString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser = ourCtx.newXmlParser();
|
||||||
|
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||||
|
parser.parseResource(input);
|
||||||
|
fail();
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
assertEquals("Resource is missing required element 'url' in parent element 'extension'", e.getCause().getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #414
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseXmlModifierExtensionWithoutUrl() {
|
||||||
|
//@formatter:off
|
||||||
|
String input = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
|
||||||
|
" <modifierExtension>\n" +
|
||||||
|
" <valueDateTime value=\"2011-01-02T11:13:15\"/>\n" +
|
||||||
|
" </modifierExtension>\n" +
|
||||||
|
"</Patient>";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newXmlParser();
|
||||||
|
parser.setParserErrorHandler(new LenientErrorHandler());
|
||||||
|
Patient parsed = (Patient) parser.parseResource(input);
|
||||||
|
assertEquals(1, parsed.getModifierExtension().size());
|
||||||
|
assertEquals(null, parsed.getModifierExtension().get(0).getUrl());
|
||||||
|
assertEquals("2011-01-02T11:13:15", parsed.getModifierExtension().get(0).getValueAsPrimitive().getValueAsString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser = ourCtx.newXmlParser();
|
||||||
|
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||||
|
parser.parseResource(input);
|
||||||
|
fail();
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
assertEquals("Resource is missing required element 'url' in parent element 'modifierExtension'", e.getCause().getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeChainedContainedResourcer() {
|
public void testEncodeChainedContainedResourcer() {
|
||||||
Organization gp = new Organization();
|
Organization gp = new Organization();
|
||||||
@ -1868,53 +1929,7 @@ public class XmlParserDstu3Test {
|
|||||||
assertThat(output, containsString("<text><status value=\"generated\"/><div xmlns=\"http://www.w3.org/1999/xhtml\"><div class=\"hapiHeaderText\">John <b>SMITH </b>"));
|
assertThat(output, containsString("<text><status value=\"generated\"/><div xmlns=\"http://www.w3.org/1999/xhtml\"><div class=\"hapiHeaderText\">John <b>SMITH </b>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testExceptionWithoutUrl() {
|
|
||||||
//@formatter:off
|
|
||||||
String input =
|
|
||||||
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>" +
|
|
||||||
"<Patient xmlns=\"http://hl7.org/fhir\">" +
|
|
||||||
"<extension>" +
|
|
||||||
"<valueString value=\"FOO\">" +
|
|
||||||
"</extension>" +
|
|
||||||
"<address>" +
|
|
||||||
"<line value=\"FOO\"/>" +
|
|
||||||
"</address>" +
|
|
||||||
"</Patient>";
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
try {
|
|
||||||
ourCtx.newXmlParser().parseResource(Patient.class, input);
|
|
||||||
fail();
|
|
||||||
} catch (DataFormatException e) {
|
|
||||||
assertThat(e.toString(), containsString("Extension element has no 'url' attribute"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testModifierExceptionWithoutUrl() {
|
|
||||||
//@formatter:off
|
|
||||||
String input =
|
|
||||||
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>" +
|
|
||||||
"<Patient xmlns=\"http://hl7.org/fhir\">" +
|
|
||||||
"<modifierExtension>" +
|
|
||||||
"<valueString value=\"FOO\">" +
|
|
||||||
"</modifierExtension>" +
|
|
||||||
"<address>" +
|
|
||||||
"<line value=\"FOO\"/>" +
|
|
||||||
"</address>" +
|
|
||||||
"</Patient>";
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
try {
|
|
||||||
ourCtx.newXmlParser().parseResource(Patient.class, input);
|
|
||||||
fail();
|
|
||||||
} catch (DataFormatException e) {
|
|
||||||
assertThat(e.toString(), containsString("Extension element has no 'url' attribute"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMoreExtensions() throws Exception {
|
public void testMoreExtensions() throws Exception {
|
||||||
|
@ -128,6 +128,11 @@
|
|||||||
<![CDATA[<code>MedicationAdministration.medication[x]</code>]]>.
|
<![CDATA[<code>MedicationAdministration.medication[x]</code>]]>.
|
||||||
Thanks to GitHub user @Crudelus for reporting!
|
Thanks to GitHub user @Crudelus for reporting!
|
||||||
</action>
|
</action>
|
||||||
|
<action type="fix" issue="414">
|
||||||
|
Handle parsing an extension without a URL more gracefully. In HAPI FHIR 2.0 this caused
|
||||||
|
a NullPointerException to be thrown. Now it will trigger a warning, or throw a
|
||||||
|
DataFormatException if the StrictErrorHandler is configured on the parser.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="2.0" date="2016-08-30">
|
<release version="2.0" date="2016-08-30">
|
||||||
<action type="fix">
|
<action type="fix">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user