diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index 4c4388f51ee..55c800ca0f3 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -1123,9 +1123,9 @@ public class JsonParser extends BaseParser implements IParser { JsonObject nextObject = (JsonObject) theJsonVal; boolean preResource = false; if (theState.isPreResource()) { - String resType = nextObject.getString("resourceType"); + String resType = nextObject.getString("resourceType", null); if (isBlank(resType)) { - throw new DataFormatException("Missing 'resourceType' from resource"); + throw new DataFormatException("Missing required element 'resourceType' from JSON resource object, unable to parse"); } theState.enteringNewElement(null, resType); preResource = true; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java index 93e5b1a5442..1ab5d559d3c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java @@ -328,7 +328,7 @@ public abstract class BaseMethodBinding implements IClientResponseHandler } /** - * Subclasses may override this method (but should also call super.{@link #populateActionRequestDetailsForInterceptor(ActionRequestDetails, Object[])) to provide method specifics to the + * Subclasses may override this method (but should also call super.{@link #populateActionRequestDetailsForInterceptor(RequestDetails, ActionRequestDetails, Object[])} to provide method specifics to the * interceptors. * * @param theRequestDetails diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java index 2d60e7ad2a4..ed15bef1127 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java @@ -9,6 +9,8 @@ import java.util.Map; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; +import ca.uhn.fhir.rest.server.IResourceProvider; + /* * #%L * HAPI FHIR - Core Library @@ -32,6 +34,18 @@ import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; /** * Base class for RESTful client and server exceptions. RESTful client methods will only throw exceptions which are subclasses of this exception type, and RESTful server methods should also only call * subclasses of this exception type. + *

+ * HAPI provides a number of subclasses of BaseServerResponseException, and each one corresponds to a specific + * HTTP status code. For example, if a {@link IResourceProvider resource provider} method throws + * {@link ResourceNotFoundException}, this is a signal to the server that an HTTP 404 should + * be returned to the client. + *

+ *

+ * See: A complete list of available exceptions is in the package summary. + * If an exception doesn't exist for a condition you want to represent, let us know by filing an + * issue in our tracker. You may also + * use {@link UnclassifiedServerFailureException} to represent any error code you want. + *

*/ public abstract class BaseServerResponseException extends RuntimeException { diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyDiagnosticReportWithBoundCodeExtension.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyDiagnosticReportWithBoundCodeExtension.java index 7f9a526b8a4..d0643438b9c 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyDiagnosticReportWithBoundCodeExtension.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyDiagnosticReportWithBoundCodeExtension.java @@ -17,6 +17,8 @@ import ca.uhn.fhir.util.ElementUtil; @ResourceDef(name = "DiagnosticReport") public class MyDiagnosticReportWithBoundCodeExtension extends DiagnosticReport { + private static final long serialVersionUID = 1L; + public static final String SP_IMAGING_STUDY = "ImagingStudy"; /** diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyOrganization.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyOrganization.java index 6e69d520ec4..82bbf9e3e43 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyOrganization.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyOrganization.java @@ -6,4 +6,6 @@ import ca.uhn.fhir.model.dstu.resource.Organization; @ResourceDef() public class MyOrganization extends Organization { + private static final long serialVersionUID = 1L; + } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyPatient.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyPatient.java index 61939f94258..30a29425f9a 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyPatient.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/MyPatient.java @@ -15,6 +15,8 @@ import ca.uhn.fhir.model.primitive.StringDt; @ResourceDef() public class MyPatient extends Patient { + private static final long serialVersionUID = 1L; + @Child(name = "importantDates", max = Child.MAX_UNLIMITED) @Extension(url = "http://example.com/dontuse#importantDates", definedLocally = false, isModifier = true) @Description(shortDefinition = "Some dates of note for the patient") diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java index 2e099c3f7f3..527614182f4 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java @@ -3,11 +3,7 @@ package ca.uhn.fhir.parser; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; import java.io.IOException; import java.util.ArrayList; @@ -299,7 +295,6 @@ public class JsonParserDstu2Test { } - /** * Fixing #89 */ @@ -319,7 +314,6 @@ public class JsonParserDstu2Test { //@formatter:on } - /** * #158 */ @@ -328,10 +322,10 @@ public class JsonParserDstu2Test { TagList tagList = new TagList(); tagList.addTag(null, null, null); tagList.addTag(null, null, "Label"); - + Patient p = new Patient(); ResourceMetadataKeyEnum.TAG_LIST.put(p, tagList); - + String encoded = ourCtx.newJsonParser().encodeResourceToString(p); assertThat(encoded, not(containsString("tag"))); } @@ -344,10 +338,10 @@ public class JsonParserDstu2Test { TagList tagList = new TagList(); tagList.addTag("scheme", "code", null); tagList.addTag(null, null, "Label"); - + Patient p = new Patient(); ResourceMetadataKeyEnum.TAG_LIST.put(p, tagList); - + String encoded = ourCtx.newJsonParser().encodeResourceToString(p); assertThat(encoded, containsString("tag")); assertThat(encoded, containsString("scheme")); @@ -415,7 +409,7 @@ public class JsonParserDstu2Test { Patient p = new Patient(); p.setId("123"); p.addName().addFamily("ABC"); - + assertThat(ourCtx.newJsonParser().encodeResourceToString(p), stringContainsInOrder("123", "ABC")); assertThat(ourCtx.newJsonParser().setOmitResourceId(true).encodeResourceToString(p), containsString("ABC")); assertThat(ourCtx.newJsonParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123"))); @@ -444,7 +438,7 @@ public class JsonParserDstu2Test { Medication m = (Medication) parsed.getEntries().get(1).getResource(); assertEquals("http://example.com/base/Medication/example", m.getId().getValue()); - assertSame(((ResourceReferenceDt)p.getMedication()).getResource(), m); + assertSame(((ResourceReferenceDt) p.getMedication()).getResource(), m); String reencoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(parsed); ourLog.info(reencoded); @@ -474,9 +468,9 @@ public class JsonParserDstu2Test { ca.uhn.fhir.model.dstu2.resource.Bundle parsed = ourCtx.newXmlParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, content); MedicationPrescription p = (MedicationPrescription) parsed.getEntry().get(0).getResource(); - assertEquals("#med", ((ResourceReferenceDt)p.getMedication()).getReference().getValue()); + assertEquals("#med", ((ResourceReferenceDt) p.getMedication()).getReference().getValue()); - Medication m = (Medication) ((ResourceReferenceDt)p.getMedication()).getResource(); + Medication m = (Medication) ((ResourceReferenceDt) p.getMedication()).getResource(); assertNotNull(m); assertEquals("#med", m.getId().getValue()); assertEquals(1, p.getContained().getContainedResources().size()); @@ -514,8 +508,8 @@ public class JsonParserDstu2Test { Medication m = (Medication) parsed.getEntry().get(1).getResource(); assertEquals("http://example.com/base/Medication/example", m.getId().getValue()); - assertEquals("Medication/example", ((ResourceReferenceDt)p.getMedication()).getReference().getValue()); - assertSame(((ResourceReferenceDt)p.getMedication()).getResource(), m); + assertEquals("Medication/example", ((ResourceReferenceDt) p.getMedication()).getReference().getValue()); + assertSame(((ResourceReferenceDt) p.getMedication()).getResource(), m); String reencoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(parsed); ourLog.info(reencoded); @@ -554,8 +548,8 @@ public class JsonParserDstu2Test { Medication m = (Medication) parsed.getEntries().get(1).getResource(); assertEquals("http://example.com/base/Medication/example", m.getId().getValue()); - assertEquals("Medication/example", ((ResourceReferenceDt)p.getMedication()).getReference().getValue()); - assertSame(((ResourceReferenceDt)p.getMedication()).getResource(), m); + assertEquals("Medication/example", ((ResourceReferenceDt) p.getMedication()).getReference().getValue()); + assertSame(((ResourceReferenceDt) p.getMedication()).getResource(), m); String reencoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(parsed); ourLog.info(reencoded); @@ -665,12 +659,12 @@ public class JsonParserDstu2Test { " ]" + "}"; //@formatter:on - + ca.uhn.fhir.model.dstu2.resource.Bundle parsed = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, input); - + String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(parsed); ourLog.info(encoded); - + assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getId().getValue()); assertEquals("urn:uuid:", parsed.getEntry().get(0).getResource().getId().getBaseUrl()); assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getId().getIdPart()); @@ -807,6 +801,22 @@ public class JsonParserDstu2Test { assertNull(ResourceMetadataKeyEnum.PROFILES.get(patient)); } + /** + * See #207 + */ + @Test + public void testParseResourceWithInvalidType() { + String input = "{" + "\"resourceType\":\"Patient\"," + "\"contained\":[" + " {" + " \"rezType\":\"Organization\"" + " }" + " ]" + "}"; + + IParser jsonParser = ourCtx.newJsonParser().setPrettyPrint(true); + try { + jsonParser.parseResource(input); + fail(); + } catch (DataFormatException e) { + assertEquals("Missing required element 'resourceType' from JSON resource object, unable to parse", e.getMessage()); + } + } + /** * See #163 */ @@ -829,10 +839,10 @@ public class JsonParserDstu2Test { String bundleText = jsonParser.encodeResourceToString(bundle); ourLog.info(bundleText); - - ca.uhn.fhir.model.dstu2.resource.Bundle reincarnatedBundle = jsonParser.parseResource (ca.uhn.fhir.model.dstu2.resource.Bundle.class, bundleText); - Patient reincarnatedPatient = (Patient) reincarnatedBundle.getEntry().get(0).getResource(); - + + ca.uhn.fhir.model.dstu2.resource.Bundle reincarnatedBundle = jsonParser.parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, bundleText); + Patient reincarnatedPatient = (Patient) reincarnatedBundle.getEntry().get(0).getResource(); + assertEquals("Patient", patient.getId().getResourceType()); assertEquals("Patient", reincarnatedPatient.getId().getResourceType()); } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 44424909537..707bbee1838 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -88,6 +88,11 @@ of the appropriate error code for the exception being thrown. Thanks to Nagesh Bashyam for reporting! + + Fix issue in JSON parser where invalid contained resources (missing + a resourceType element) fail to parse with a confusing NullPointerException. + Thanks to GitHub user @hugosoares for reporting! +