From 0fd5a44b5e32a5761cdd0336aa1f1acf075c7922 Mon Sep 17 00:00:00 2001 From: Sebastien Riviere Date: Mon, 24 Jul 2017 13:43:55 +0200 Subject: [PATCH 1/2] Fixed #622 Extension lost on ID datatype or extension of type primitive during parsing --- .../ca/uhn/fhir/model/primitive/IdDt.java | 2 +- .../java/ca/uhn/fhir/parser/JsonParser.java | 167 ++++++++--- .../java/ca/uhn/fhir/parser/ParserState.java | 19 +- .../java/ca/uhn/fhir/parser/XmlParser.java | 47 ++-- .../parser/ElementWithExtensionDstu2Test.java | 263 ++++++++++++------ .../java/org/hl7/fhir/dstu3/model/IdType.java | 2 +- .../parser/ElementWithExtensionDstu3Test.java | 237 ++++++++++------ .../MyPatientWithCustomUrlExtension.java | 62 ++--- 8 files changed, 522 insertions(+), 277 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java index 008b7fb3e0d..4e6b63c2c9a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java @@ -385,7 +385,7 @@ public class IdDt extends UriDt implements /*IPrimitiveDatatype, */IIdTy @Override public boolean isEmpty() { - return isBlank(getValue()); + return super.isBaseEmpty() && isBlank(getValue()); } @Override 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 e41dc29ca87..63fe88d74b2 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 @@ -95,6 +95,8 @@ import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType; import ca.uhn.fhir.parser.json.JsonLikeWriter; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.util.ElementUtil; +import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.ID_DATATYPE; +import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.PRIMITIVE_DATATYPE; /** * This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use @@ -867,14 +869,28 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { } write(theEventWriter, "resourceType", resDef.getName()); - if (theResourceId != null && theResourceId.hasIdPart()) { - write(theEventWriter, "id", theResourceId.getIdPart()); - if (theResourceId.hasFormatComment()) { - beginObject(theEventWriter, "_id"); - writeCommentsPreAndPost(theResourceId, theEventWriter); - theEventWriter.endObject(); - } - } + if (theResourceId != null && theResourceId.hasIdPart()) { + write(theEventWriter, "id", theResourceId.getIdPart()); + final List extensions = new ArrayList(0); + final List modifierExtensions = new ArrayList(0); + // Undeclared extensions + extractUndeclaredExtensions(theResourceId, extensions, modifierExtensions, null, null); + boolean haveExtension = false; + if (!extensions.isEmpty()) { + haveExtension = true; + } + + if (theResourceId.hasFormatComment() || haveExtension) { + beginObject(theEventWriter, "_id"); + if (theResourceId.hasFormatComment()) { + writeCommentsPreAndPost(theResourceId, theEventWriter); + } + if (haveExtension) { + writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions); + } + theEventWriter.endObject(); + } + } if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) && theResource instanceof IResource) { IResource resource = (IResource) theResource; @@ -1428,41 +1444,78 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { } } - private void parseExtension(ParserState theState, JsonLikeArray theValues, boolean theIsModifier) { - for (int i = 0; i < theValues.size(); i++) { - JsonLikeObject nextExtObj = JsonLikeValue.asObject(theValues.get(i)); - JsonLikeValue jsonElement = nextExtObj.get("url"); - String url; - if (null == jsonElement || !(jsonElement.isScalar())) { - String parentElementName; - if (theIsModifier) { - parentElementName = "modifierExtension"; - } else { - parentElementName = "extension"; - } - getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url"); - url = null; - } else { - url = getExtensionUrl(jsonElement.getAsString()); - } - theState.enteringNewElementExtension(null, url, theIsModifier, getServerBaseUrl()); - for (String next : nextExtObj.keySet()) { - if ("url".equals(next)) { - continue; - } else if ("extension".equals(next)) { - JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next)); - parseExtension(theState, jsonVal, false); - } else if ("modifierExtension".equals(next)) { - JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next)); - parseExtension(theState, jsonVal, true); - } else { - JsonLikeValue jsonVal = nextExtObj.get(next); - parseChildren(theState, next, jsonVal, null, null, false); - } - } - theState.endingElement(); - } - } + private void parseExtension(ParserState theState, JsonLikeArray theValues, boolean theIsModifier) { + int allUnderscoreNames = 0; + int handledUnderscoreNames = 0; + + for (int i = 0; i < theValues.size(); i++) { + JsonLikeObject nextExtObj = JsonLikeValue.asObject(theValues.get(i)); + JsonLikeValue jsonElement = nextExtObj.get("url"); + String url; + if (null == jsonElement || !(jsonElement.isScalar())) { + String parentElementName; + if (theIsModifier) { + parentElementName = "modifierExtension"; + } else { + parentElementName = "extension"; + } + getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url"); + url = null; + } else { + url = getExtensionUrl(jsonElement.getAsString()); + } + theState.enteringNewElementExtension(null, url, theIsModifier, getServerBaseUrl()); + for (String next : nextExtObj.keySet()) { + if ("url".equals(next)) { + continue; + } else if ("extension".equals(next)) { + JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next)); + parseExtension(theState, jsonVal, false); + } else if ("modifierExtension".equals(next)) { + JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next)); + parseExtension(theState, jsonVal, true); + } else if (next.charAt(0) == '_') { + allUnderscoreNames++; + continue; + } else { + JsonLikeValue jsonVal = nextExtObj.get(next); + String alternateName = '_' + next; + JsonLikeValue alternateVal = nextExtObj.get(alternateName); + if (alternateVal != null) { + handledUnderscoreNames++; + } + parseChildren(theState, next, jsonVal, alternateVal, alternateName, false); + } + } + + /* + * This happens if an element has an extension but no actual value. I.e. + * if a resource has a "_status" element but no corresponding "status" + * element. This could be used to handle a null value with an extension + * for example. + */ + if (allUnderscoreNames > handledUnderscoreNames) { + for (String alternateName : nextExtObj.keySet()) { + if (alternateName.startsWith("_") && alternateName.length() > 1) { + JsonLikeValue nextValue = nextExtObj.get(alternateName); + if (nextValue != null) { + if (nextValue.isObject()) { + String nextName = alternateName.substring(1); + if (nextExtObj.get(nextName) == null) { + theState.enteringNewElement(null, nextName); + parseAlternates(nextValue, theState, alternateName, alternateName); + theState.endingElement(); + } + } else { + getErrorHandler().incorrectJsonType(null, alternateName, ValueType.OBJECT, null, nextValue.getJsonType(), null); + } + } + } + } + } + theState.endingElement(); + } + } private void parseFhirComments(JsonLikeValue theObject, ParserState theState) { if (theObject.isArray()) { @@ -1842,6 +1895,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { } else { String childName = myDef.getChildNameByDatatype(myValue.getClass()); encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, myParent, false); + managePrimitiveExtension(myValue, theResDef, theResource, theEventWriter, def, childName); } theEventWriter.endObject(); @@ -1897,13 +1951,34 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName()); } encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true, myParent, false); + managePrimitiveExtension(value, theResDef, theResource, theEventWriter, childDef, childName); } // theEventWriter.name(myUndeclaredExtension.get); theEventWriter.endObject(); } - - } - + + private void managePrimitiveExtension(final IBase theValue, final RuntimeResourceDefinition theResDef, final IBaseResource theResource, final JsonLikeWriter theEventWriter, final BaseRuntimeElementDefinition def, final String childName) throws IOException { + if (def.getChildType().equals(ID_DATATYPE) || def.getChildType().equals(PRIMITIVE_DATATYPE)) { + final List extensions = new ArrayList(0); + final List modifierExtensions = new ArrayList(0); + // Undeclared extensions + extractUndeclaredExtensions(theValue, extensions, modifierExtensions, myParent, null); + // Declared extensions + if (def != null) { + extractDeclaredExtensions(theValue, def, extensions, modifierExtensions, myParent); + } + boolean haveContent = false; + if (!extensions.isEmpty() || !modifierExtensions.isEmpty()) { + haveContent = true; + } + if (haveContent) { + beginObject(theEventWriter, '_' + childName); + writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions); + theEventWriter.endObject(); + } + } + } + } } 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 8cec4158bf7..6b833526eec 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 @@ -1473,6 +1473,7 @@ class ParserState { push(newState); return; } + case ID_DATATYPE: case PRIMITIVE_DATATYPE: { RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target; IPrimitiveType newChildInstance = primitiveTarget.newInstance(myDefinition.getInstanceConstructorArguments()); @@ -2261,13 +2262,19 @@ class ParserState { String resourceName = myContext.getResourceDefinition(nextResource).getName(); String bundleIdPart = nextResource.getId().getIdPart(); if (isNotBlank(bundleIdPart)) { - // if (isNotBlank(entryBaseUrl)) { - // nextResource.setId(new IdDt(entryBaseUrl, resourceName, bundleIdPart, version)); - // } else { - nextResource.setId(new IdDt(null, resourceName, bundleIdPart, version)); - // } + // if (isNotBlank(entryBaseUrl)) { + // nextResource.setId(new IdDt(entryBaseUrl, resourceName, bundleIdPart, version)); + // } else { + IdDt previousId = nextResource.getId(); + nextResource.setId(new IdDt(null, resourceName, bundleIdPart, version)); + // Copy extensions + if (!previousId.getAllUndeclaredExtensions().isEmpty()) { + for (final ExtensionDt ext : previousId.getAllUndeclaredExtensions()) { + nextResource.getId().addUndeclaredExtension(ext); + } + } + // } } - } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java index 81bf5c634fb..8cc9b9527d1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java @@ -507,13 +507,17 @@ public class XmlParser extends BaseParser /*implements IParser */{ switch (childDef.getChildType()) { case ID_DATATYPE: { - IIdType value = IIdType.class.cast(theElement); - String encodedValue = "id".equals(childName) ? value.getIdPart() : value.getValue(); - theEventWriter.writeStartElement(childName); - theEventWriter.writeAttribute("value", encodedValue); - encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource); - theEventWriter.writeEndElement(); - break; + IIdType value = IIdType.class.cast(theElement); + String encodedValue = "id".equals(childName) ? value.getIdPart() : value.getValue(); + if (StringUtils.isNotBlank(encodedValue) || super.hasExtensions(value)) { + theEventWriter.writeStartElement(childName); + if (StringUtils.isNotBlank(encodedValue)) { + theEventWriter.writeAttribute("value", encodedValue); + } + encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource); + theEventWriter.writeEndElement(); + } + break; } case PRIMITIVE_DATATYPE: { IPrimitiveType pd = IPrimitiveType.class.cast(theElement); @@ -756,13 +760,15 @@ public class XmlParser extends BaseParser /*implements IParser */{ theEventWriter.writeDefaultNamespace(FHIR_NS); if (theResource instanceof IAnyResource) { - // HL7.org Structures - if (theResourceId != null) { - writeCommentsPre(theEventWriter, theResourceId); - writeOptionalTagWithValue(theEventWriter, "id", theResourceId.getIdPart()); - writeCommentsPost(theEventWriter, theResourceId); - } + if (theResourceId != null) { + writeCommentsPre(theEventWriter, theResourceId); + theEventWriter.writeStartElement("id"); + theEventWriter.writeAttribute("value", theResourceId.getIdPart()); + encodeExtensionsIfPresent(theResource, theEventWriter, theResourceId, false); + theEventWriter.writeEndElement(); + writeCommentsPost(theEventWriter, theResourceId); + } encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, theContainedResource, new CompositeChildElement(resDef)); @@ -773,11 +779,16 @@ public class XmlParser extends BaseParser /*implements IParser */{ // DSTU2+ IResource resource = (IResource) theResource; - if (theResourceId != null) { - writeCommentsPre(theEventWriter, theResourceId); - writeOptionalTagWithValue(theEventWriter, "id", theResourceId.getIdPart()); - writeCommentsPost(theEventWriter, theResourceId); - } + if (theResourceId != null) { + /* writeCommentsPre(theEventWriter, theResourceId); + writeOptionalTagWithValue(theEventWriter, "id", theResourceId.getIdPart()); + writeCommentsPost(theEventWriter, theResourceId);*/ + theEventWriter.writeStartElement("id"); + theEventWriter.writeAttribute("value", theResourceId.getIdPart()); + encodeExtensionsIfPresent(theResource, theEventWriter, theResourceId, false); + theEventWriter.writeEndElement(); + writeCommentsPost(theEventWriter, theResourceId); + } InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); IdDt resourceId = resource.getId(); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/ElementWithExtensionDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/ElementWithExtensionDstu2Test.java index c596f24d5be..589ec5855c9 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/ElementWithExtensionDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/ElementWithExtensionDstu2Test.java @@ -1,121 +1,206 @@ package ca.uhn.fhir.parser; -import static org.junit.Assert.assertEquals; - -import org.junit.AfterClass; -import org.junit.Ignore; -import org.junit.Test; - import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.dstu2.composite.HumanNameDt; +import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.util.TestUtil; +import org.junit.AfterClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; /** * Created by Sébastien Rivière 12/04/2017 */ public class ElementWithExtensionDstu2Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ca.uhn.fhir.parser.ElementWithExtensionDstu2Test.class); - private static FhirContext ctx = FhirContext.forDstu2(); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ca.uhn.fhir.parser.ElementWithExtensionDstu2Test.class); + private final FhirContext ctx = FhirContext.forDstu2(); - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } - @Test - @Ignore - public void testExtensionOnPrimitiveExtensionJson() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); - final IParser parser = ctx.newJsonParser().setPrettyPrint(true); - parser.setServerBaseUrl("http://foo"); - final String json = parser.encodeResourceToString(patient); + @Test + public void testExtensionOnPrimitiveExtensionJson() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + final HumanNameDt name = patient.getNameFirstRep(); + name.addFamily(new StringDt("family")); + name.getFamilyFirstRep().addUndeclaredExtension(new ExtensionDt(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"))); - ourLog.info(json); + final StringDt stringExt = new StringDt(); + stringExt.setValue("myStringExt"); + stringExt.addUndeclaredExtension(new ExtensionDt(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"))); + final ExtensionDt ext = new ExtensionDt(); + ext.setValue(stringExt); + ext.setUrl("/myExt"); + patient.addUndeclaredExtension(ext); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); - assertEquals(1, patient.getPetName().getUndeclaredExtensions().size()); - } - @Test - public void testExtensionOnPrimitiveExtensionXml() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); - final IParser parser = ctx.newXmlParser().setPrettyPrint(true); - parser.setServerBaseUrl("http://foo"); - final String xml = parser.encodeResourceToString(patient); + patient.setPetName(new StringDt("myPet")); + patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); - ourLog.info(xml); + final IParser parser = ctx.newJsonParser().setPrettyPrint(true); + final String json = parser.encodeResourceToString(patient); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); - assertEquals(1, patient.getPetName().getUndeclaredExtensions().size()); - } + ourLog.info(json); - @Test - @Ignore - public void testExtensionOnIDDatatypeJson() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); - final IParser parser = ctx.newJsonParser().setPrettyPrint(true); - parser.setServerBaseUrl("http://foo"); - final String json = parser.encodeResourceToString(patient); + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); + assertEquals(1, patient.getName().get(0).getFamilyFirstRep().getUndeclaredExtensions().size()); + assertEquals(1, ((StringDt) patient.getUndeclaredExtensionsByUrl("/myExt").get(0).getValue()).getUndeclaredExtensions().size()); + assertEquals(1, patient.getPetName().getUndeclaredExtensions().size()); + } - ourLog.info(json); + @Test + public void testExtensionOnPrimitiveExtensionWithNullValueJson() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); + final IParser parser = ctx.newJsonParser().setPrettyPrint(true); + final String json = parser.encodeResourceToString(patient); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); - assertEquals(1, patient.getId().getUndeclaredExtensions().size()); - } + ourLog.info(json); - @Test - @Ignore - public void testExtensionOnIDDatatypeXml() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); - final IParser parser = ctx.newXmlParser().setPrettyPrint(true); - parser.setServerBaseUrl("http://foo"); - final String xml = parser.encodeResourceToString(patient); + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); + assertEquals(1, patient.getPetName().getUndeclaredExtensions().size()); + } - ourLog.info(xml); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); - assertEquals(1, patient.getId().getUndeclaredExtensions().size()); - } + @Test + public void testExtensionOnPrimitiveExtensionXml() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); - @Test - @Ignore - public void testExtensionOnIDDatatypeExtensionJson() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); - final IParser parser = ctx.newJsonParser().setPrettyPrint(true); - parser.setServerBaseUrl("http://foo"); - final String json = parser.encodeResourceToString(patient); + final HumanNameDt name = patient.getNameFirstRep(); + name.addFamily(new StringDt("family")); + name.getFamilyFirstRep().addUndeclaredExtension(new ExtensionDt(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"))); - ourLog.info(json); + final StringDt stringExt = new StringDt(); + stringExt.setValue("myStringExt"); + stringExt.addUndeclaredExtension(new ExtensionDt(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"))); + final ExtensionDt ext = new ExtensionDt(); + ext.setValue(stringExt); + ext.setUrl("/myExt"); + patient.addUndeclaredExtension(ext); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); - assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size()); - } - @Test - @Ignore - public void testExtensionOnIDDatatypeExtensionXml() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); - final IParser parser = ctx.newXmlParser().setPrettyPrint(true); - parser.setServerBaseUrl("http://foo"); - final String xml = parser.encodeResourceToString(patient); + patient.setPetName(new StringDt("myPet")); + patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); + final IParser parser = ctx.newXmlParser().setPrettyPrint(true); + final String xml = parser.encodeResourceToString(patient); - ourLog.info(xml); + ourLog.info(xml); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); - assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size()); - } + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); + assertEquals(1, patient.getName().get(0).getFamilyFirstRep().getUndeclaredExtensions().size()); + assertEquals(1, ((StringDt) patient.getUndeclaredExtensionsByUrl("/myExt").get(0).getValue()).getUndeclaredExtensions().size()); + assertEquals(1, patient.getPetName().getUndeclaredExtensions().size()); + } + + @Test + public void testExtensionOnPrimitiveExtensionWithNullValueXml() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); + final IParser parser = ctx.newXmlParser().setPrettyPrint(true); + final String xml = parser.encodeResourceToString(patient); + + ourLog.info(xml); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); + assertEquals(1, patient.getPetName().getUndeclaredExtensions().size()); + } + + @Test + public void testExtensionOnIDDatatypeJson() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.getId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); + final IParser parser = ctx.newJsonParser().setPrettyPrint(true); + final String json = parser.encodeResourceToString(patient); + + ourLog.info(json); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); + assertEquals(1, patient.getId().getUndeclaredExtensions().size()); + } + + + + @Test + public void testExtensionOnIDDatatypeXml() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.getId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); + final IParser parser = ctx.newXmlParser().setPrettyPrint(true); + final String xml = parser.encodeResourceToString(patient); + + ourLog.info(xml); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); + assertEquals(1, patient.getId().getUndeclaredExtensions().size()); + } + + @Test + public void testExtensionOnIDDatatypeExtensionJson() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.setCustomId(new IdDt("3")); + patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); + final IParser parser = ctx.newJsonParser().setPrettyPrint(true); + final String json = parser.encodeResourceToString(patient); + + ourLog.info(json); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); + assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size()); + } + + @Test + public void testExtensionOnIDDatatypeExtensionNullValueJson() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); + final IParser parser = ctx.newJsonParser().setPrettyPrint(true); + final String json = parser.encodeResourceToString(patient); + + ourLog.info(json); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); + assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size()); + } + + @Test + public void testExtensionOnIDDatatypeExtensionXml() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.setCustomId(new IdDt("4")); + patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); + final IParser parser = ctx.newXmlParser().setPrettyPrint(true); + final String xml = parser.encodeResourceToString(patient); + + ourLog.info(xml); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); + assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size()); + } + + @Test + public void testExtensionOnIDDatatypeExtensionNullValueXml() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")); + final IParser parser = ctx.newXmlParser().setPrettyPrint(true); + final String xml = parser.encodeResourceToString(patient); + + ourLog.info(xml); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); + assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size()); + } } diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java index 02535643be3..677aef6579d 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java @@ -437,7 +437,7 @@ public final class IdType extends UriType implements IPrimitiveType, IId @Override public boolean isEmpty() { - return isBlank(getValue()); + return super.isEmpty() && isBlank(getValue()); } @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ElementWithExtensionDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ElementWithExtensionDstu3Test.java index 686db4ced24..52d727cc66b 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ElementWithExtensionDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ElementWithExtensionDstu3Test.java @@ -1,116 +1,183 @@ package ca.uhn.fhir.parser; -import static org.junit.Assert.assertEquals; - -import org.hl7.fhir.dstu3.model.StringType; -import org.junit.AfterClass; -import org.junit.Ignore; -import org.junit.Test; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.util.TestUtil; +import org.hl7.fhir.dstu3.model.Extension; +import org.hl7.fhir.dstu3.model.HumanName; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.StringType; +import org.junit.AfterClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; /** * Created by Sébastien Rivière 12/04/2017 */ public class ElementWithExtensionDstu3Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ca.uhn.fhir.parser.ElementWithExtensionDstu3Test.class); - private static final FhirContext ctx = FhirContext.forDstu3(); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ca.uhn.fhir.parser.ElementWithExtensionDstu3Test.class); + private final FhirContext ctx = FhirContext.forDstu3(); - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } - @Test - @Ignore - public void testNullFlavorPrimitiveExtensionJson() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - final IParser parser = ctx.newJsonParser().setPrettyPrint(true); - final String json = parser.encodeResourceToString(patient); + @Test + public void testNullFlavorPrimitiveExtensionJson() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + final HumanName name = patient.getNameFirstRep(); + name.setFamily("family"); + name.getFamilyElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - ourLog.info(json); + patient.setPetName(new StringType("myPet")); + patient.getExtensionsByUrl("/petname"); + patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); - assertEquals(1, patient.getPetName().getExtension().size()); - } + final StringType stringExt = new StringType(); + stringExt.setValue("myStringExt"); + stringExt.addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); + final Extension ext = new Extension(); + ext.setValue(stringExt); + ext.setUrl("/myExt"); + patient.addExtension(ext); - @Test - @Ignore - public void testNullFlavorPrimitiveExtensionXml() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - final IParser parser = ctx.newXmlParser().setPrettyPrint(true); - final String xml = parser.encodeResourceToString(patient); + final IParser parser = ctx.newJsonParser().setPrettyPrint(true); + final String json = parser.encodeResourceToString(patient); - ourLog.info(xml); + ourLog.info(json); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); - assertEquals(1, patient.getPetName().getExtension().size()); - } + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); + assertEquals(1, patient.getName().get(0).getFamilyElement().getExtension().size()); + assertEquals(1, patient.getExtensionsByUrl("/myExt").get(0).getValue().getExtension().size()); + assertEquals(1, patient.getPetName().getExtension().size()); + } - @Test - @Ignore - public void testNullFlavorIDDatatypeJson() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getIdElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - final IParser parser = ctx.newJsonParser().setPrettyPrint(true); - final String json = parser.encodeResourceToString(patient); + @Test + public void testNullFlavorPrimitiveExtensionNullValueJson() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.getExtensionsByUrl("/petname"); + patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - ourLog.info(json); + final IParser parser = ctx.newJsonParser().setPrettyPrint(true); + final String json = parser.encodeResourceToString(patient); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); - assertEquals(1, patient.getIdElement().getExtension().size()); - } + ourLog.info(json); - @Test - @Ignore - public void testNullFlavorIDDatatypeXml() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getIdElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - final IParser parser = ctx.newXmlParser().setPrettyPrint(true); - final String xml = parser.encodeResourceToString(patient); + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); + assertEquals(1, patient.getPetName().getExtension().size()); + } - ourLog.info(xml); + @Test + public void testNullFlavorPrimitiveExtensionXml() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + final HumanName name = patient.getNameFirstRep(); + name.setFamily("family"); + name.getFamilyElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); - assertEquals(1, patient.getIdElement().getExtension().size()); - } + patient.setPetName(new StringType("myPet")); + patient.getExtensionsByUrl("/petname"); + patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - @Test - @Ignore - public void testNullFlavorExtensionIDDatatypeJson() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getCustomId().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - final IParser parser = ctx.newJsonParser().setPrettyPrint(true); - final String json = parser.encodeResourceToString(patient); + final StringType stringExt = new StringType(); + stringExt.setValue("myStringExt"); + stringExt.addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); + final Extension ext = new Extension(); + ext.setValue(stringExt); + ext.setUrl("/myExt"); + patient.addExtension(ext); - ourLog.info(json); + final IParser parser = ctx.newXmlParser().setPrettyPrint(true); + final String xml = parser.encodeResourceToString(patient); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); - assertEquals(1, patient.getCustomId().getExtension().size()); - } + ourLog.info(xml); - @Test - @Ignore - public void testNullFlavorExtensionIDDatatypeXml() throws Exception { - MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); - patient.setId("1"); - patient.getCustomId().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); - final IParser parser = ctx.newXmlParser().setPrettyPrint(true); - final String xml = parser.encodeResourceToString(patient); + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); + assertEquals(1, patient.getName().get(0).getFamilyElement().getExtension().size()); + assertEquals(1, patient.getExtensionsByUrl("/myExt").get(0).getValue().getExtension().size()); + assertEquals(1, patient.getPetName().getExtension().size()); + } - ourLog.info(xml); - patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); - assertEquals(1, patient.getCustomId().getExtension().size()); - } + @Test + public void testNullFlavorPrimitiveExtensionNullValueXml() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + + patient.getExtensionsByUrl("/petname"); + patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); + + final IParser parser = ctx.newXmlParser().setPrettyPrint(true); + final String xml = parser.encodeResourceToString(patient); + + ourLog.info(xml); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); + assertEquals(1, patient.getPetName().getExtension().size()); + } + + + @Test + public void testNullFlavorIDDatatypeJson() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.getIdElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); + final IParser parser = ctx.newJsonParser().setPrettyPrint(true); + final String json = parser.encodeResourceToString(patient); + + ourLog.info(json); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); + assertEquals(1, patient.getIdElement().getExtension().size()); + } + + @Test + public void testNullFlavorIDDatatypeXml() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.getIdElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); + final IParser parser = ctx.newXmlParser().setPrettyPrint(true); + final String xml = parser.encodeResourceToString(patient); + + ourLog.info(xml); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); + assertEquals(1, patient.getIdElement().getExtension().size()); + } + + @Test + public void testNullFlavorExtensionIDDatatypeJson() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + patient.setCustomId(new IdType(("4"))); + patient.getCustomId().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); + final IParser parser = ctx.newJsonParser().setPrettyPrint(true); + final String json = parser.encodeResourceToString(patient); + + ourLog.info(json); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json); + assertEquals(1, patient.getCustomId().getExtension().size()); + } + + @Test + public void testNullFlavorExtensionIDDatatypeXml() throws Exception { + MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension(); + patient.setId("1"); + final IParser parser = ctx.newXmlParser().setPrettyPrint(true); + patient.setCustomId(new IdType(("4"))); + patient.getCustomId().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")); + + final String xml = parser.encodeResourceToString(patient); + + ourLog.info(xml); + + patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml); + assertEquals(1, patient.getCustomId().getExtension().size()); + } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/MyPatientWithCustomUrlExtension.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/MyPatientWithCustomUrlExtension.java index cca4b7ede6d..f7800e6ef65 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/MyPatientWithCustomUrlExtension.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/MyPatientWithCustomUrlExtension.java @@ -11,42 +11,42 @@ import org.hl7.fhir.dstu3.model.StringType; @ResourceDef() public class MyPatientWithCustomUrlExtension extends Patient { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - @Child(name = "petName") - @Extension(url = "/petname", definedLocally = false, isModifier = false) - @Description(shortDefinition = "The name of the patient's favourite pet") - private StringType myPetName; + @Child(name = "petName") + @Extension(url = "/petname", definedLocally = false, isModifier = false) + @Description(shortDefinition = "The name of the patient's favourite pet") + private StringType myPetName; - @Child(name = "customid") - @Extension(url = "/customid", definedLocally = false, isModifier = false) - @Description(shortDefinition = "The customid of the patient's ") - private IdType myCustomId; + @Child(name = "customid") + @Extension(url = "/customid", definedLocally = false, isModifier = false) + @Description(shortDefinition = "The customid of the patient's ") + private IdType myCustomId; - public StringType getPetName() { - if (myPetName == null) { - myPetName = new StringType(); + public StringType getPetName() { + if (myPetName == null) { + myPetName = new StringType(); + } + return myPetName; } - return myPetName; - } - public void setPetName(final StringType thePetName) { - myPetName = thePetName; - } - - @Override - public boolean isEmpty() { - return super.isEmpty() && myPetName.isEmpty(); - } - - public IdType getCustomId() { - if (myCustomId == null) { - myCustomId = new IdType(); + public void setPetName(final StringType thePetName) { + myPetName = thePetName; } - return myCustomId; - } - public void setCustomId(final IdType myCustomId) { - this.myCustomId = myCustomId; - } + @Override + public boolean isEmpty() { + return super.isEmpty() && getCustomId().isEmpty() && getPetName().isEmpty(); + } + + public IdType getCustomId() { + if (myCustomId == null) { + myCustomId = new IdType(); + } + return myCustomId; + } + + public void setCustomId(final IdType myCustomId) { + this.myCustomId = myCustomId; + } } From 51fa762e1f6879988b86929ef720e60887815e66 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 13 Aug 2017 10:03:27 -0400 Subject: [PATCH 2/2] Credit for #695 --- src/changes/changes.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 1f0cd5dd1be..043906be78b 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -211,6 +211,10 @@ Previously my.org was treated as the resource type and Foo was treated as the ID. Thanks to GitHub user @CarthageKing for the pull request! + + Extensions on ID datatypes were not parsed or serialized correctly. Thanks to + Stephen Rivière for the pull request! +