Correctly encode extensions on the root of a resource with type

reference
This commit is contained in:
James Agnew 2017-05-17 12:40:10 -04:00
parent 7bb9e5edd9
commit a92d80d860
6 changed files with 3506 additions and 3443 deletions

View File

@ -151,7 +151,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
return false;
}
private boolean addToHeldExtensions(int valueIdx, List<? extends IBaseExtension<?, ?>> ext, ArrayList<ArrayList<HeldExtension>> list, boolean theIsModifier, CompositeChildElement theChildElem) {
private boolean addToHeldExtensions(int valueIdx, List<? extends IBaseExtension<?, ?>> ext, ArrayList<ArrayList<HeldExtension>> list, boolean theIsModifier, CompositeChildElement theChildElem, CompositeChildElement theParent) {
if (ext.size() > 0) {
list.ensureCapacity(valueIdx);
while (list.size() <= valueIdx) {
@ -161,7 +161,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
list.set(valueIdx, new ArrayList<JsonParser.HeldExtension>());
}
for (IBaseExtension<?, ?> next : ext) {
list.get(valueIdx).add(new HeldExtension(next, theIsModifier, theChildElem));
list.get(valueIdx).add(new HeldExtension(next, theIsModifier, theChildElem, theParent));
}
return true;
}
@ -593,7 +593,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
if (nextChildElem.getDef().getElementName().equals("extension") || nextChildElem.getDef().getElementName().equals("modifierExtension") || nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
if (!haveWrittenExtensions) {
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, myContext.getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem);
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, myContext.getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem, theParent);
haveWrittenExtensions = true;
}
continue;
@ -673,20 +673,20 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
if (primitive) {
if (nextValue instanceof ISupportsUndeclaredExtensions) {
List<ExtensionDt> ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredExtensions();
force |= addToHeldExtensions(valueIdx, ext, extensions, false, nextChildElem);
force |= addToHeldExtensions(valueIdx, ext, extensions, false, nextChildElem, theParent);
ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredModifierExtensions();
force |= addToHeldExtensions(valueIdx, ext, modifierExtensions, true, nextChildElem);
force |= addToHeldExtensions(valueIdx, ext, modifierExtensions, true, nextChildElem, theParent);
} else {
if (nextValue instanceof IBaseHasExtensions) {
IBaseHasExtensions element = (IBaseHasExtensions) nextValue;
List<? extends IBaseExtension<?, ?>> ext = element.getExtension();
force |= addToHeldExtensions(valueIdx, ext, extensions, false, nextChildElem);
force |= addToHeldExtensions(valueIdx, ext, extensions, false, nextChildElem, theParent);
}
if (nextValue instanceof IBaseHasModifierExtensions) {
IBaseHasModifierExtensions element = (IBaseHasModifierExtensions) nextValue;
List<? extends IBaseExtension<?, ?>> ext = element.getModifierExtension();
force |= addToHeldExtensions(valueIdx, ext, extensions, true, nextChildElem);
force |= addToHeldExtensions(valueIdx, ext, extensions, true, nextChildElem, theParent);
}
}
if (nextValue.hasFormatComment()) {
@ -996,13 +996,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object
* called _name): resource extensions, and extension extensions
* @param theChildElem
* @param theParent
*/
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonLikeWriter theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IBaseResource theResource, CompositeChildElement theChildElem) throws IOException {
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonLikeWriter theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IBaseResource theResource, CompositeChildElement theChildElem, CompositeChildElement theParent) throws IOException {
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
// Undeclared extensions
extractUndeclaredExtensions(theElement, extensions, modifierExtensions, theChildElem);
extractUndeclaredExtensions(theElement, extensions, modifierExtensions, theChildElem, theParent);
// Declared extensions
if (theElementDef != null) {
@ -1036,7 +1037,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
}
private void extractUndeclaredExtensions(IBase theElement, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, CompositeChildElement theChildElem) {
private void extractUndeclaredExtensions(IBase theElement, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, CompositeChildElement theChildElem, CompositeChildElement theParent) {
if (theElement instanceof ISupportsUndeclaredExtensions) {
ISupportsUndeclaredExtensions element = (ISupportsUndeclaredExtensions) theElement;
List<ExtensionDt> ext = element.getUndeclaredExtensions();
@ -1044,7 +1045,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
if (next == null || next.isEmpty()) {
continue;
}
extensions.add(new HeldExtension(next, false, theChildElem));
extensions.add(new HeldExtension(next, false, theChildElem, theParent));
}
ext = element.getUndeclaredModifierExtensions();
@ -1052,7 +1053,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
if (next == null || next.isEmpty()) {
continue;
}
modifierExtensions.add(new HeldExtension(next, true, theChildElem));
modifierExtensions.add(new HeldExtension(next, true, theChildElem, theParent));
}
} else {
if (theElement instanceof IBaseHasExtensions) {
@ -1062,7 +1063,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
if (next == null || (ElementUtil.isEmpty(next.getValue()) && next.getExtension().isEmpty())) {
continue;
}
extensions.add(new HeldExtension(next, false, theChildElem));
extensions.add(new HeldExtension(next, false, theChildElem, theParent));
}
}
if (theElement instanceof IBaseHasModifierExtensions) {
@ -1072,7 +1073,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
if (next == null || next.isEmpty()) {
continue;
}
modifierExtensions.add(new HeldExtension(next, true, theChildElem));
modifierExtensions.add(new HeldExtension(next, true, theChildElem, theParent));
}
}
}
@ -1775,12 +1776,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
private boolean myModifier;
private IBaseExtension<?, ?> myUndeclaredExtension;
private IBase myValue;
private CompositeChildElement myParent;
public HeldExtension(IBaseExtension<?, ?> theUndeclaredExtension, boolean theModifier, CompositeChildElement theChildElem) {
public HeldExtension(IBaseExtension<?, ?> theUndeclaredExtension, boolean theModifier, CompositeChildElement theChildElem, CompositeChildElement theParent) {
assert theUndeclaredExtension != null;
myUndeclaredExtension = theUndeclaredExtension;
myModifier = theModifier;
myChildElem = theChildElem;
myParent = theParent;
}
public HeldExtension(RuntimeChildDeclaredExtensionDefinition theDef, IBase theValue, CompositeChildElement theChildElem) {
@ -1835,10 +1838,10 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
BaseRuntimeElementDefinition<?> def = myDef.getChildElementDefinitionByDatatype(myValue.getClass());
if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) {
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource, myChildElem);
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource, myChildElem, null);
} else {
String childName = myDef.getChildNameByDatatype(myValue.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, null, false);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, myParent, false);
}
theEventWriter.endObject();
@ -1893,7 +1896,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
if (childDef == null) {
throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName());
}
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true, null, false);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true, myParent, false);
}
// theEventWriter.name(myUndeclaredExtension.get);

View File

@ -384,7 +384,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
* to be reflected in the resource shared with interceptors
*/
if (!thePerformIndexing) {
incremenetId(theResource, entity, theResource.getIdElement());
incrementId(theResource, entity, theResource.getIdElement());
}
// Notify JPA interceptors
@ -415,7 +415,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return outcome;
}
private void incremenetId(T theResource, ResourceTable theSavedEntity, IIdType theResourceId) {
private void incrementId(T theResource, ResourceTable theSavedEntity, IIdType theResourceId) {
IIdType idType = theResourceId;
String newVersion;
long newVersionLong;
@ -1131,7 +1131,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
if (resourceId.hasVersionIdPart() == false) {
resourceId = resourceId.withVersion(Long.toString(savedEntity.getVersion()));
}
incremenetId(theResource, savedEntity, resourceId);
incrementId(theResource, savedEntity, resourceId);
}
// Notify interceptors

View File

@ -175,6 +175,25 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
myDaoConfig.setAllowMultipleDelete(true);
}
@Test
public void testSaveAndRetrieveResourceWithExtension() {
Patient nextPatient = new Patient();
nextPatient.setId("Patient/B");
nextPatient
.addExtension()
.setUrl("http://foo")
.setValue(new Reference("Practitioner/A"));
ourClient.update().resource(nextPatient).execute();
Patient p = ourClient.read().resource(Patient.class).withId("B").execute();
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(encoded);
assertThat(encoded, containsString("http://foo"));
}
private void checkParamMissing(String paramName) throws IOException, ClientProtocolException {
HttpGet get = new HttpGet(ourServerBase + "/Observation?" + paramName + ":missing=false");
CloseableHttpResponse resp = ourHttpClient.execute(get);
@ -1549,12 +1568,11 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
assertEquals(null, responseBundle.getLink("next"));
assertThat(ids, hasItem("List/A161444"));
assertThat(ids, hasItem("List/A161468"));
assertThat(ids, hasItem("List/A161500"));
ourLog.info("Expected {} - {}", allIds.size(), allIds);
ourLog.info("Actual {} - {}", ids.size(), ids);
assertEquals(allIds, ids);
@ -1726,7 +1744,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
preDates.add(new Date());
Thread.sleep(100);
patient.setId(id);
patient.getName().get(0).getFamilyElement().setValue(methodName + "_i"+i);
patient.getName().get(0).getFamilyElement().setValue(methodName + "_i" + i);
ids.add(myPatientDao.update(patient, mySrd).getId().toUnqualified().getValue());
}
@ -3149,7 +3167,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
// should be subject._id
HttpGet httpPost = new HttpGet(ourServerBase + "/Observation?subject.id=FOO");
HttpGet httpPost = new HttpGet(ourServerBase + "/Observation?subject.id=FOO");
CloseableHttpResponse resp = ourHttpClient.execute(httpPost);
try {
@ -3162,7 +3180,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
ourLog.info("Outgoing post: {}", httpPost);
}
/**
* See #411
*

View File

@ -98,6 +98,7 @@ public class JsonParserDstu3Test {
assertEquals("Found incorrect type for element subject - Expected OBJECT and found SCALAR (STRING)", e.getMessage());
}
}
/**
* See #563
@ -116,47 +117,6 @@ public class JsonParserDstu3Test {
}
}
@Test
public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnFhirContext() {
try {
String tmp = "{\"resourceType\":\"Bundle\",\"entry\":[{\"fullUrl\":\"http://lalaland.org/patient/pat1\",\"resource\":{\"resourceType\":\"Patient\",\"id\":\"patxuzos\"}}]}";
ourCtx.getParserOptions().setOverrideResourceIdWithBundleEntryFullUrl(false);
Bundle bundle = (Bundle) ourCtx.newJsonParser().parseResource(tmp);
assertEquals(1, bundle.getEntry().size());
{
Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
IIdType o1Id = o1.getIdElement();
assertFalse(o1Id.hasBaseUrl());
assertEquals("Patient", o1Id.getResourceType());
assertEquals("patxuzos", o1Id.getIdPart());
assertFalse(o1Id.hasVersionIdPart());
}
} finally {
// ensure we cleanup ourCtx so other tests continue to work
ourCtx = FhirContext.forDstu3();
}
}
@Test
public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnParser() {
try {
String tmp = "{\"resourceType\":\"Bundle\",\"entry\":[{\"fullUrl\":\"http://lalaland.org/patient/pat1\",\"resource\":{\"resourceType\":\"Patient\",\"id\":\"patxuzos\"}}]}";
Bundle bundle = (Bundle) ourCtx.newJsonParser().setOverrideResourceIdWithBundleEntryFullUrl(false).parseResource(tmp);
assertEquals(1, bundle.getEntry().size());
{
Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
IIdType o1Id = o1.getIdElement();
assertFalse(o1Id.hasBaseUrl());
assertEquals("Patient", o1Id.getResourceType());
assertEquals("patxuzos", o1Id.getIdPart());
assertFalse(o1Id.hasVersionIdPart());
}
} finally {
// ensure we cleanup ourCtx so other tests continue to work
ourCtx = FhirContext.forDstu3();
}
}
/**
* See #544
*/
@ -184,81 +144,73 @@ public class JsonParserDstu3Test {
assertNotNull(subject);
assertEquals("FAMILY", subject.getNameFirstRep().getFamily());
}
/**
* Test for the url generated based on the server config
*/
@Test
public void testIncorrectJsonTypesIdAndArray() {
// ID should be a String and communication should be an Array
String input = "{\"resourceType\": \"Patient\",\n" +
" \"id\": 123,\n" +
" \"communication\": {\n" +
" \"language\": {\n" +
" \"text\": \"Hindi\"\n" +
" },\n" +
" \"preferred\": true\n" +
" }\n" +
"}";
public void testCustomUrlExtension() {
final String expected = "{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://www.example.com/petname\",\"valueString\":\"myName\"}]}";
IParser p = ourCtx.newJsonParser();
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
p.setParserErrorHandler(errorHandler);
Patient patient = (Patient) p.parseResource(input);
ArgumentCaptor<String> elementName = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<ValueType> found = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ValueType> expected = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ScalarType> expectedScalarType = ArgumentCaptor.forClass(ScalarType.class);
ArgumentCaptor<ScalarType> foundScalarType = ArgumentCaptor.forClass(ScalarType.class);
verify(errorHandler, times(2)).incorrectJsonType(any(IParseLocation.class), elementName.capture(), expected.capture(), expectedScalarType.capture(), found.capture(), foundScalarType.capture());
assertEquals(ValueType.SCALAR, found.getAllValues().get(0));
assertEquals(ValueType.SCALAR, expected.getAllValues().get(0));
assertEquals(ScalarType.NUMBER, foundScalarType.getAllValues().get(0));
assertEquals(ScalarType.STRING, expectedScalarType.getAllValues().get(0));
assertEquals(ValueType.OBJECT, found.getAllValues().get(1));
assertEquals(ValueType.ARRAY, expected.getAllValues().get(1));
assertEquals(null, foundScalarType.getAllValues().get(1));
assertEquals(null, expectedScalarType.getAllValues().get(1));
assertEquals("123", patient.getIdElement().getIdPart());
assertEquals("Hindi", patient.getCommunicationFirstRep().getLanguage().getText());
final MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setPetName(new StringType("myName"));
final IParser jsonParser = ourCtx.newJsonParser();
jsonParser.setServerBaseUrl("http://www.example.com");
final String parsedPatient = jsonParser.encodeResourceToString(patient);
System.out.println(parsedPatient);
assertEquals(expected, parsedPatient);
// Parse with string
MyPatientWithCustomUrlExtension newPatient = jsonParser.parseResource(MyPatientWithCustomUrlExtension.class, parsedPatient);
assertEquals("myName", newPatient.getPetName().getValue());
// Parse with stream
newPatient = jsonParser.parseResource(MyPatientWithCustomUrlExtension.class, new StringReader(parsedPatient));
assertEquals("myName", newPatient.getPetName().getValue());
//Check no NPE if base server not configure
newPatient = ourCtx.newJsonParser().parseResource(MyPatientWithCustomUrlExtension.class, new StringReader(parsedPatient));
assertNull("myName", newPatient.getPetName().getValue());
assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
}
@Test
public void testIncorrectJsonTypesNone() {
// ID should be a String and communication should be an Array
String input = "{\"resourceType\": \"Patient\",\n" +
" \"id\": \"123\",\n" +
" \"communication\": [{\n" +
" \"language\": {\n" +
" \"text\": \"Hindi\"\n" +
" },\n" +
" \"preferred\": true\n" +
" }]\n" +
"}";
public void testCustomUrlExtensioninBundle() {
final String expected = "{\"resourceType\":\"Bundle\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://www.example.com/petname\",\"valueString\":\"myName\"}]}}]}";
final MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setPetName(new StringType("myName"));
final Bundle bundle = new Bundle();
final BundleEntryComponent entry = new BundleEntryComponent();
entry.setResource(patient);
bundle.addEntry(entry);
final IParser jsonParser = ourCtx.newJsonParser();
jsonParser.setServerBaseUrl("http://www.example.com");
final String parsedBundle = jsonParser.encodeResourceToString(bundle);
System.out.println(parsedBundle);
assertEquals(expected, parsedBundle);
// Parse with string
Bundle newBundle = jsonParser.parseResource(Bundle.class, parsedBundle);
assertNotNull(newBundle);
assertEquals(1, newBundle.getEntry().size());
Patient newPatient = (Patient) newBundle.getEntry().get(0).getResource();
assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
// Parse with stream
newBundle = jsonParser.parseResource(Bundle.class, new StringReader(parsedBundle));
assertNotNull(newBundle);
assertEquals(1, newBundle.getEntry().size());
newPatient = (Patient) newBundle.getEntry().get(0).getResource();
assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
IParser p = ourCtx.newJsonParser();
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
p.setParserErrorHandler(errorHandler);
Patient patient = (Patient) p.parseResource(input);
ArgumentCaptor<String> elementName = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<ValueType> found = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ValueType> expected = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ScalarType> expectedScalarType = ArgumentCaptor.forClass(ScalarType.class);
ArgumentCaptor<ScalarType> foundScalarType = ArgumentCaptor.forClass(ScalarType.class);
verify(errorHandler, times(0)).incorrectJsonType(any(IParseLocation.class), elementName.capture(), expected.capture(), expectedScalarType.capture(), found.capture(), foundScalarType.capture());
assertEquals("123", patient.getIdElement().getIdPart());
assertEquals("Hindi", patient.getCommunicationFirstRep().getLanguage().getText());
}
/**
* See #276
*/
@ -285,81 +237,7 @@ public class JsonParserDstu3Test {
assertEquals(3, countMatches(encoded, "resourceType"));
}
/**
* #480
*/
@Test
public void testEncodeEmptyValue() {
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.setId("123");
qr.getAuthoredElement().setValueAsString("");
qr.getItemFirstRep().setLinkIdElement(new StringType());
qr.getItemFirstRep().addItem().setLinkIdElement(new StringType(""));
qr.getItemFirstRep().addItem().setLinkIdElement(new StringType("LINKID"));
String encoded = ourCtx.newJsonParser().encodeResourceToString(qr);
ourLog.info(encoded);
assertThat(encoded, stringContainsInOrder("123"));
assertThat(encoded, not(stringContainsInOrder("\"\"")));
assertThat(encoded, not(stringContainsInOrder("null")));
}
/**
* #480
*/
@Test
public void testParseEmptyValue() {
String input = "{\"resourceType\":\"QuestionnaireResponse\",\"id\":\"123\",\"authored\":\"\",\"group\":{\"linkId\":\"\"}}";
IParser parser = ourCtx.newJsonParser();
parser.setParserErrorHandler(new LenientErrorHandler().setErrorOnInvalidValue(false));
QuestionnaireResponse qr = parser.parseResource(QuestionnaireResponse.class, input);
assertEquals("QuestionnaireResponse/123", qr.getIdElement().getValue());
assertEquals(null, qr.getAuthored());
assertEquals(null, qr.getAuthoredElement().getValue());
assertEquals(null, qr.getAuthoredElement().getValueAsString());
assertEquals(null, qr.getItemFirstRep().getLinkId());
assertEquals(null, qr.getItemFirstRep().getLinkIdElement().getValue());
}
/**
* See #477
*/
@Test
public void testUnexpectedElementsWithUnderscoreAtStartOfName() throws Exception {
String input = IOUtils.toString(JsonParserDstu3Test.class.getResourceAsStream("/bug477.json"), StandardCharsets.UTF_8);
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
// Do it once without the custom error handler just for the logging
IParser p = ourCtx.newJsonParser();
p.parseResource(Patient.class, input);
p = ourCtx.newJsonParser();
p.setParserErrorHandler(errorHandler);
Patient parsed = p.parseResource(Patient.class, input);
assertEquals("1", parsed.getIdElement().getIdPart());
ArgumentCaptor<String> elementName = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<ValueType> expected = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ValueType> actual = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ScalarType> expectedScalar = ArgumentCaptor.forClass(ScalarType.class);
ArgumentCaptor<ScalarType> actualScalar = ArgumentCaptor.forClass(ScalarType.class);
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), elementName.capture(), expected.capture(), expectedScalar.capture(), actual.capture(), actualScalar.capture());
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("_id"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR), actualScalar.capture());
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("__v"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR), actualScalar.capture());
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("_status"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR), actualScalar.capture());
assertEquals("_id", elementName.getAllValues().get(0));
assertEquals(ValueType.OBJECT, expected.getAllValues().get(0));
assertEquals(ValueType.SCALAR, actual.getAllValues().get(0));
assertEquals(null, expectedScalar.getAllValues().get(0));
assertEquals(null, actualScalar.getAllValues().get(0));
}
@Test
public void testEncodeAndParseExtensions() throws Exception {
@ -445,7 +323,7 @@ public class JsonParserDstu3Test {
assertEquals("CHILD", ((StringType) given2ext2.getValue()).getValue());
}
@Test
public void testEncodeAndParseMetaProfileAndTags() {
Patient p = new Patient();
@ -524,6 +402,7 @@ public class JsonParserDstu3Test {
assertEquals("sec_label2", tagList.get(1).getDisplay());
}
/**
* See #336
*/
@ -740,6 +619,26 @@ public class JsonParserDstu3Test {
assertThat(encoded, not(containsString("Label")));
}
/**
* #480
*/
@Test
public void testEncodeEmptyValue() {
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.setId("123");
qr.getAuthoredElement().setValueAsString("");
qr.getItemFirstRep().setLinkIdElement(new StringType());
qr.getItemFirstRep().addItem().setLinkIdElement(new StringType(""));
qr.getItemFirstRep().addItem().setLinkIdElement(new StringType("LINKID"));
String encoded = ourCtx.newJsonParser().encodeResourceToString(qr);
ourLog.info(encoded);
assertThat(encoded, stringContainsInOrder("123"));
assertThat(encoded, not(stringContainsInOrder("\"\"")));
assertThat(encoded, not(stringContainsInOrder("null")));
}
@Test
public void testEncodeExtendedInfrastructureComponent() {
IParser parser = ourCtx.newJsonParser();
@ -794,6 +693,25 @@ public class JsonParserDstu3Test {
}
@Test
public void testEncodeExtensionOnRoot() {
Patient p = new Patient();
p.setId("Patient/B");
p
.addExtension()
.setUrl("http://foo")
.setValue(new Reference("Practitioner/A"));
IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
parser.setDontEncodeElements(new HashSet<String>(Arrays.asList("*.id", "*.meta")));
String encoded = parser.encodeResourceToString(p);
ourLog.info(encoded);
assertThat(encoded, containsString("http://foo"));
assertThat(encoded, containsString("Practitioner/A"));
}
@Test
public void testEncodeExtensionUndeclaredNonModifier() {
Observation obs = new Observation();
@ -1387,6 +1305,98 @@ public class JsonParserDstu3Test {
assertEquals("{\"resourceType\":\"Observation\",\"valueQuantity\":{\"value\":0.0000000000000001}}", str);
}
@Test
public void testIncorrectJsonTypesIdAndArray() {
// ID should be a String and communication should be an Array
String input = "{\"resourceType\": \"Patient\",\n" +
" \"id\": 123,\n" +
" \"communication\": {\n" +
" \"language\": {\n" +
" \"text\": \"Hindi\"\n" +
" },\n" +
" \"preferred\": true\n" +
" }\n" +
"}";
IParser p = ourCtx.newJsonParser();
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
p.setParserErrorHandler(errorHandler);
Patient patient = (Patient) p.parseResource(input);
ArgumentCaptor<String> elementName = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<ValueType> found = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ValueType> expected = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ScalarType> expectedScalarType = ArgumentCaptor.forClass(ScalarType.class);
ArgumentCaptor<ScalarType> foundScalarType = ArgumentCaptor.forClass(ScalarType.class);
verify(errorHandler, times(2)).incorrectJsonType(any(IParseLocation.class), elementName.capture(), expected.capture(), expectedScalarType.capture(), found.capture(), foundScalarType.capture());
assertEquals(ValueType.SCALAR, found.getAllValues().get(0));
assertEquals(ValueType.SCALAR, expected.getAllValues().get(0));
assertEquals(ScalarType.NUMBER, foundScalarType.getAllValues().get(0));
assertEquals(ScalarType.STRING, expectedScalarType.getAllValues().get(0));
assertEquals(ValueType.OBJECT, found.getAllValues().get(1));
assertEquals(ValueType.ARRAY, expected.getAllValues().get(1));
assertEquals(null, foundScalarType.getAllValues().get(1));
assertEquals(null, expectedScalarType.getAllValues().get(1));
assertEquals("123", patient.getIdElement().getIdPart());
assertEquals("Hindi", patient.getCommunicationFirstRep().getLanguage().getText());
}
@Test
public void testIncorrectJsonTypesNone() {
// ID should be a String and communication should be an Array
String input = "{\"resourceType\": \"Patient\",\n" +
" \"id\": \"123\",\n" +
" \"communication\": [{\n" +
" \"language\": {\n" +
" \"text\": \"Hindi\"\n" +
" },\n" +
" \"preferred\": true\n" +
" }]\n" +
"}";
IParser p = ourCtx.newJsonParser();
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
p.setParserErrorHandler(errorHandler);
Patient patient = (Patient) p.parseResource(input);
ArgumentCaptor<String> elementName = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<ValueType> found = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ValueType> expected = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ScalarType> expectedScalarType = ArgumentCaptor.forClass(ScalarType.class);
ArgumentCaptor<ScalarType> foundScalarType = ArgumentCaptor.forClass(ScalarType.class);
verify(errorHandler, times(0)).incorrectJsonType(any(IParseLocation.class), elementName.capture(), expected.capture(), expectedScalarType.capture(), found.capture(), foundScalarType.capture());
assertEquals("123", patient.getIdElement().getIdPart());
assertEquals("Hindi", patient.getCommunicationFirstRep().getLanguage().getText());
}
@Test
public void testInvalidDateTimeValueInvalid() throws Exception {
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
String res = "{ \"resourceType\": \"Observation\", \"valueDateTime\": \"foo\" }";
IParser parser = ourCtx.newJsonParser();
parser.setParserErrorHandler(errorHandler);
Observation parsed = parser.parseResource(Observation.class, res);
assertEquals(null, parsed.getValueDateTimeType().getValue());
assertEquals("foo", parsed.getValueDateTimeType().getValueAsString());
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
verify(errorHandler, times(1)).invalidValue(isNull(IParseLocation.class), eq("foo"), msgCaptor.capture());
assertEquals("Invalid date/time format: \"foo\"", msgCaptor.getValue());
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
assertEquals("{\"resourceType\":\"Observation\",\"valueDateTime\":\"foo\"}", encoded);
}
/**
* #516
*/
@ -1439,26 +1449,6 @@ public class JsonParserDstu3Test {
assertEquals("{\"resourceType\":\"Patient\",\"gender\":\"foo\"}", encoded);
}
@Test
public void testInvalidDateTimeValueInvalid() throws Exception {
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
String res = "{ \"resourceType\": \"Observation\", \"valueDateTime\": \"foo\" }";
IParser parser = ourCtx.newJsonParser();
parser.setParserErrorHandler(errorHandler);
Observation parsed = parser.parseResource(Observation.class, res);
assertEquals(null, parsed.getValueDateTimeType().getValue());
assertEquals("foo", parsed.getValueDateTimeType().getValueAsString());
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
verify(errorHandler, times(1)).invalidValue(isNull(IParseLocation.class), eq("foo"), msgCaptor.capture());
assertEquals("Invalid date/time format: \"foo\"", msgCaptor.getValue());
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
assertEquals("{\"resourceType\":\"Observation\",\"valueDateTime\":\"foo\"}", encoded);
}
/**
* #65
*/
@ -1533,6 +1523,47 @@ public class JsonParserDstu3Test {
assertThat(ourCtx.newJsonParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123")));
}
@Test
public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnFhirContext() {
try {
String tmp = "{\"resourceType\":\"Bundle\",\"entry\":[{\"fullUrl\":\"http://lalaland.org/patient/pat1\",\"resource\":{\"resourceType\":\"Patient\",\"id\":\"patxuzos\"}}]}";
ourCtx.getParserOptions().setOverrideResourceIdWithBundleEntryFullUrl(false);
Bundle bundle = (Bundle) ourCtx.newJsonParser().parseResource(tmp);
assertEquals(1, bundle.getEntry().size());
{
Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
IIdType o1Id = o1.getIdElement();
assertFalse(o1Id.hasBaseUrl());
assertEquals("Patient", o1Id.getResourceType());
assertEquals("patxuzos", o1Id.getIdPart());
assertFalse(o1Id.hasVersionIdPart());
}
} finally {
// ensure we cleanup ourCtx so other tests continue to work
ourCtx = FhirContext.forDstu3();
}
}
@Test
public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnParser() {
try {
String tmp = "{\"resourceType\":\"Bundle\",\"entry\":[{\"fullUrl\":\"http://lalaland.org/patient/pat1\",\"resource\":{\"resourceType\":\"Patient\",\"id\":\"patxuzos\"}}]}";
Bundle bundle = (Bundle) ourCtx.newJsonParser().setOverrideResourceIdWithBundleEntryFullUrl(false).parseResource(tmp);
assertEquals(1, bundle.getEntry().size());
{
Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
IIdType o1Id = o1.getIdElement();
assertFalse(o1Id.hasBaseUrl());
assertEquals("Patient", o1Id.getResourceType());
assertEquals("patxuzos", o1Id.getIdPart());
assertFalse(o1Id.hasVersionIdPart());
}
} finally {
// ensure we cleanup ourCtx so other tests continue to work
ourCtx = FhirContext.forDstu3();
}
}
@Test
@Ignore
public void testParseAndEncodeBundle() throws Exception {
@ -1872,6 +1903,25 @@ public class JsonParserDstu3Test {
assertEquals("patient family", p.getName().get(0).getFamilyElement().getValue());
}
/**
* #480
*/
@Test
public void testParseEmptyValue() {
String input = "{\"resourceType\":\"QuestionnaireResponse\",\"id\":\"123\",\"authored\":\"\",\"group\":{\"linkId\":\"\"}}";
IParser parser = ourCtx.newJsonParser();
parser.setParserErrorHandler(new LenientErrorHandler().setErrorOnInvalidValue(false));
QuestionnaireResponse qr = parser.parseResource(QuestionnaireResponse.class, input);
assertEquals("QuestionnaireResponse/123", qr.getIdElement().getValue());
assertEquals(null, qr.getAuthored());
assertEquals(null, qr.getAuthoredElement().getValue());
assertEquals(null, qr.getAuthoredElement().getValueAsString());
assertEquals(null, qr.getItemFirstRep().getLinkId());
assertEquals(null, qr.getItemFirstRep().getLinkIdElement().getValue());
}
/**
* See #335
*/
@ -2224,6 +2274,42 @@ public class JsonParserDstu3Test {
Assert.assertThat(message, containsString("contained"));
}
/**
* See #477
*/
@Test
public void testUnexpectedElementsWithUnderscoreAtStartOfName() throws Exception {
String input = IOUtils.toString(JsonParserDstu3Test.class.getResourceAsStream("/bug477.json"), StandardCharsets.UTF_8);
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
// Do it once without the custom error handler just for the logging
IParser p = ourCtx.newJsonParser();
p.parseResource(Patient.class, input);
p = ourCtx.newJsonParser();
p.setParserErrorHandler(errorHandler);
Patient parsed = p.parseResource(Patient.class, input);
assertEquals("1", parsed.getIdElement().getIdPart());
ArgumentCaptor<String> elementName = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<ValueType> expected = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ValueType> actual = ArgumentCaptor.forClass(ValueType.class);
ArgumentCaptor<ScalarType> expectedScalar = ArgumentCaptor.forClass(ScalarType.class);
ArgumentCaptor<ScalarType> actualScalar = ArgumentCaptor.forClass(ScalarType.class);
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), elementName.capture(), expected.capture(), expectedScalar.capture(), actual.capture(), actualScalar.capture());
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("_id"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR), actualScalar.capture());
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("__v"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR), actualScalar.capture());
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("_status"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR), actualScalar.capture());
assertEquals("_id", elementName.getAllValues().get(0));
assertEquals(ValueType.OBJECT, expected.getAllValues().get(0));
assertEquals(ValueType.SCALAR, actual.getAllValues().get(0));
assertEquals(null, expectedScalar.getAllValues().get(0));
assertEquals(null, actualScalar.getAllValues().get(0));
}
@Test
public void testValidateCustomStructure() throws Exception {
@ -2247,72 +2333,6 @@ public class JsonParserDstu3Test {
assertTrue(result.isSuccessful());
}
/**
* Test for the url generated based on the server config
*/
@Test
public void testCustomUrlExtension() {
final String expected = "{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://www.example.com/petname\",\"valueString\":\"myName\"}]}";
final MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setPetName(new StringType("myName"));
final IParser jsonParser = ourCtx.newJsonParser();
jsonParser.setServerBaseUrl("http://www.example.com");
final String parsedPatient = jsonParser.encodeResourceToString(patient);
System.out.println(parsedPatient);
assertEquals(expected, parsedPatient);
// Parse with string
MyPatientWithCustomUrlExtension newPatient = jsonParser.parseResource(MyPatientWithCustomUrlExtension.class, parsedPatient);
assertEquals("myName", newPatient.getPetName().getValue());
// Parse with stream
newPatient = jsonParser.parseResource(MyPatientWithCustomUrlExtension.class, new StringReader(parsedPatient));
assertEquals("myName", newPatient.getPetName().getValue());
//Check no NPE if base server not configure
newPatient = ourCtx.newJsonParser().parseResource(MyPatientWithCustomUrlExtension.class, new StringReader(parsedPatient));
assertNull("myName", newPatient.getPetName().getValue());
assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
}
@Test
public void testCustomUrlExtensioninBundle() {
final String expected = "{\"resourceType\":\"Bundle\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://www.example.com/petname\",\"valueString\":\"myName\"}]}}]}";
final MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setPetName(new StringType("myName"));
final Bundle bundle = new Bundle();
final BundleEntryComponent entry = new BundleEntryComponent();
entry.setResource(patient);
bundle.addEntry(entry);
final IParser jsonParser = ourCtx.newJsonParser();
jsonParser.setServerBaseUrl("http://www.example.com");
final String parsedBundle = jsonParser.encodeResourceToString(bundle);
System.out.println(parsedBundle);
assertEquals(expected, parsedBundle);
// Parse with string
Bundle newBundle = jsonParser.parseResource(Bundle.class, parsedBundle);
assertNotNull(newBundle);
assertEquals(1, newBundle.getEntry().size());
Patient newPatient = (Patient) newBundle.getEntry().get(0).getResource();
assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
// Parse with stream
newBundle = jsonParser.parseResource(Bundle.class, new StringReader(parsedBundle));
assertNotNull(newBundle);
assertEquals(1, newBundle.getEntry().size());
newPatient = (Patient) newBundle.getEntry().get(0).getResource();
assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -97,6 +97,10 @@
JPA server did not correctly support searching on a custom search parameter whose
path pointed to an extension, where the client used a chained value.
</action>
<action type="fix">
Fix issue where the JSON parser sometimes did not encode DSTU3 extensions on the root of a
resource which have a value of type reference.
</action>
</release>
<release version="2.4" date="2017-04-19">
<action type="add">