Fix #274 - Primitive elements with no value but an extension were sometimes not encoded correctly in XML, and sometimes not parsed correctly in JSON.

This commit is contained in:
jamesagnew 2016-02-28 22:15:43 -05:00
parent 66ec863bde
commit 496d866f48
9 changed files with 282 additions and 93 deletions

View File

@ -42,6 +42,8 @@ import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -64,6 +66,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
@ -549,7 +552,7 @@ public abstract class BaseParser implements IParser {
protected List<? extends IBase> preProcessValues(BaseRuntimeChildDefinition metaChildUncast, IBaseResource theResource, List<? extends IBase> theValues) {
if (myContext.getVersion().getVersion().isRi()) {
/*
* If we're encoding the meta tag, we do some massaging of the meta values before
* encoding. Buf if there is no meta element at all, we create one since we're possibly going to be
@ -562,36 +565,36 @@ public abstract class BaseParser implements IParser {
theValues = Collections.singletonList(newType);
}
}
if (theValues.size() == 1 && theValues.get(0) instanceof IBaseMetaType) {
IBaseMetaType metaValue = (IBaseMetaType) theValues.get(0);
try {
metaValue = (IBaseMetaType) metaValue.getClass().getMethod("copy").invoke(metaValue);
} catch (Exception e) {
throw new InternalErrorException("Failed to duplicate meta", e);
}
if (isBlank(metaValue.getVersionId())) {
if (theResource.getIdElement().hasVersionIdPart()) {
metaValue.setVersionId(theResource.getIdElement().getVersionIdPart());
}
}
filterCodingsWithNoCodeOrSystem(metaValue.getTag());
filterCodingsWithNoCodeOrSystem(metaValue.getSecurity());
if (shouldAddSubsettedTag()) {
IBaseCoding coding = metaValue.addTag();
coding.setCode(Constants.TAG_SUBSETTED_CODE);
coding.setSystem(Constants.TAG_SUBSETTED_SYSTEM);
coding.setDisplay(subsetDescription());
}
return Collections.singletonList(metaValue);
}
}
return theValues;
}
@ -688,6 +691,28 @@ public abstract class BaseParser implements IParser {
return securityLabels;
}
static boolean hasExtensions(IBase theElement) {
if (theElement instanceof ISupportsUndeclaredExtensions) {
ISupportsUndeclaredExtensions res = (ISupportsUndeclaredExtensions) theElement;
if (res.getUndeclaredExtensions().size() > 0 || res.getUndeclaredModifierExtensions().size() > 0) {
return true;
}
}
if (theElement instanceof IBaseHasExtensions) {
IBaseHasExtensions res = (IBaseHasExtensions) theElement;
if (res.hasExtension()) {
return true;
}
}
if (theElement instanceof IBaseHasModifierExtensions) {
IBaseHasModifierExtensions res = (IBaseHasModifierExtensions) theElement;
if (res.hasModifierExtension()) {
return true;
}
}
return false;
}
protected class CompositeChildElement {
private final BaseRuntimeChildDefinition myDef;
private final CompositeChildElement myParent;
@ -711,7 +736,6 @@ public abstract class BaseParser implements IParser {
}
public CompositeChildElement(RuntimeResourceDefinition theResDef) {
myResDef = theResDef;
myDef = null;

View File

@ -216,7 +216,7 @@ public class JsonParser extends BaseParser implements IParser {
try {
JsonReader reader = Json.createReader(theReader);
JsonObject object = reader.readObject();
JsonValue resourceTypeObj = object.get("resourceType");
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString) resourceTypeObj).getString();
@ -440,7 +440,8 @@ public class JsonParser extends BaseParser implements IParser {
@Override
public String toString() {
return value.getValueAsString();
}};
}
};
if (theChildName != null) {
theWriter.write(theChildName, decimalValue);
} else {
@ -549,7 +550,7 @@ public class JsonParser extends BaseParser implements IParser {
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren, boolean theContainedResource, CompositeChildElement theParent)
throws IOException {
for (CompositeChildElement nextChildElem : super.compositeChildIterator(theChildren, theContainedResource, theParent)) {
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
@ -560,7 +561,7 @@ public class JsonParser extends BaseParser implements IParser {
if (theResource instanceof IResource) {
narr = ((IResource) theResource).getText();
} else if (theResource instanceof IDomainResource) {
narr = ((IDomainResource)theResource).getText();
narr = ((IDomainResource) theResource).getText();
} else {
narr = null;
}
@ -706,7 +707,7 @@ public class JsonParser extends BaseParser implements IParser {
haveContent = true;
heldModExts = modifierExtensions.get(i);
}
ArrayList<String> nextComments;
if (comments.size() > i) {
nextComments = comments.get(i);
@ -746,7 +747,7 @@ public class JsonParser extends BaseParser implements IParser {
throws IOException, DataFormatException {
writeCommentsPreAndPost(theNextValue, theEventWriter);
extractAndWriteExtensionsAsDirectChild(theNextValue, theEventWriter, resDef, theResDef, theResource, null);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getExtensions(), theContainedResource, theParent);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getChildren(), theContainedResource, theParent);
@ -756,13 +757,13 @@ public class JsonParser extends BaseParser implements IParser {
if (theNextValue.hasFormatComment()) {
theEventWriter.writeStartArray("fhir_comments");
List<String> pre = theNextValue.getFormatCommentsPre();
if (pre.isEmpty()==false) {
if (pre.isEmpty() == false) {
for (String next : pre) {
theEventWriter.write(next);
}
}
List<String> post = theNextValue.getFormatCommentsPost();
if (post.isEmpty()==false) {
if (post.isEmpty() == false) {
for (String next : post) {
theEventWriter.write(next);
}
@ -773,21 +774,21 @@ public class JsonParser extends BaseParser implements IParser {
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theContainedResource) throws IOException {
IIdType resourceId = null;
// if (theResource instanceof IResource) {
// IResource res = (IResource) theResource;
// if (StringUtils.isNotBlank(res.getId().getIdPart())) {
// if (theContainedResource) {
// resourceId = res.getId().getIdPart();
// } else if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
// resourceId = res.getId().getIdPart();
// }
// }
// } else if (theResource instanceof IAnyResource) {
// IAnyResource res = (IAnyResource) theResource;
// if (/* theContainedResource && */StringUtils.isNotBlank(res.getIdElement().getIdPart())) {
// resourceId = res.getIdElement().getIdPart();
// }
// }
// if (theResource instanceof IResource) {
// IResource res = (IResource) theResource;
// if (StringUtils.isNotBlank(res.getId().getIdPart())) {
// if (theContainedResource) {
// resourceId = res.getId().getIdPart();
// } else if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
// resourceId = res.getId().getIdPart();
// }
// }
// } else if (theResource instanceof IAnyResource) {
// IAnyResource res = (IAnyResource) theResource;
// if (/* theContainedResource && */StringUtils.isNotBlank(res.getIdElement().getIdPart())) {
// resourceId = res.getIdElement().getIdPart();
// }
// }
if (StringUtils.isNotBlank(theResource.getIdElement().getIdPart())) {
resourceId = theResource.getIdElement();
@ -1183,8 +1184,12 @@ public class JsonParser extends BaseParser implements IParser {
private void parseChildren(JsonObject theObject, ParserState<?> theState) {
String elementId = null;
Set<String> keySet = theObject.keySet();
int allUnderscoreNames = 0;
int handledUnderscoreNames = 0;
for (String nextName : keySet) {
if ("resourceType".equals(nextName)) {
continue;
@ -1217,12 +1222,16 @@ public class JsonParser extends BaseParser implements IParser {
parseFhirComments(theObject.get(nextName), theState);
continue;
} else if (nextName.charAt(0) == '_') {
allUnderscoreNames++;
continue;
}
JsonValue nextVal = theObject.get(nextName);
String alternateName = '_' + nextName;
JsonValue alternateVal = theObject.get(alternateName);
if (alternateVal != null) {
handledUnderscoreNames++;
}
parseChildren(theState, nextName, nextVal, alternateVal, alternateName);
@ -1236,13 +1245,36 @@ public class JsonParser extends BaseParser implements IParser {
((IBaseResource) object).getIdElement().setValue(elementId);
}
}
/*
* 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 : keySet) {
if (alternateName.startsWith("_") && alternateName.length() > 1) {
JsonValue nextValue = theObject.get(alternateName);
if (nextValue.getValueType() == ValueType.OBJECT) {
String nextName = alternateName.substring(1);
if (theObject.get(nextName) == null) {
theState.enteringNewElement(null, nextName);
parseAlternates(nextValue, theState, alternateName);
theState.endingElement();
}
}
}
}
}
}
private void parseFhirComments(JsonValue theObject, ParserState<?> theState) {
if (theObject.getValueType() == ValueType.ARRAY) {
for (JsonValue nextComment : ((JsonArray)theObject)) {
for (JsonValue nextComment : ((JsonArray) theObject)) {
if (nextComment.getValueType() == ValueType.STRING) {
String commentText = ((JsonString)nextComment).getString();
String commentText = ((JsonString) nextComment).getString();
if (commentText != null) {
theState.commentPre(commentText);
}
@ -1550,7 +1582,7 @@ public class JsonParser extends BaseParser implements IParser {
writeUndeclaredExtInDstu1Format(theResDef, theResource, theEventWriter, myUndeclaredExtension);
} else {
theEventWriter.writeStartObject();
writeCommentsPreAndPost(myValue, theEventWriter);
theEventWriter.write("url", myDef.getExtensionUrl());
@ -1572,7 +1604,7 @@ public class JsonParser extends BaseParser implements IParser {
String extensionUrl = ext.getUrl();
theEventWriter.writeStartObject();
writeCommentsPreAndPost(myUndeclaredExtension, theEventWriter);
theEventWriter.write("url", extensionUrl);

View File

@ -95,7 +95,8 @@ import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
import ca.uhn.fhir.util.XmlUtil;
/**
* This class is the FHIR XML parser/encoder. Users should not interact with this class directly, but should use {@link FhirContext#newXmlParser()} to get an instance.
* This class is the FHIR XML parser/encoder. Users should not interact with this class directly, but should use
* {@link FhirContext#newXmlParser()} to get an instance.
*/
public class XmlParser extends BaseParser implements IParser {
@ -114,7 +115,8 @@ public class XmlParser extends BaseParser implements IParser {
private boolean myPrettyPrint;
/**
* Do not use this constructor, the recommended way to obtain a new instance of the XML parser is to invoke {@link FhirContext#newXmlParser()}.
* Do not use this constructor, the recommended way to obtain a new instance of the XML parser is to invoke
* {@link FhirContext#newXmlParser()}.
*
* @param theParserErrorHandler
*/
@ -198,7 +200,7 @@ public class XmlParser extends BaseParser implements IParser {
try {
List<String> heldComments = new ArrayList<String>(1);
while (streamReader.hasNext()) {
XMLEvent nextEvent = streamReader.nextEvent();
try {
@ -225,7 +227,7 @@ public class XmlParser extends BaseParser implements IParser {
String elementName = elem.getName().getLocalPart();
parserState.enteringNewElement(namespaceURI, elementName);
}
if (!heldComments.isEmpty()) {
for (String next : heldComments) {
parserState.commentPre(next);
@ -272,7 +274,7 @@ public class XmlParser extends BaseParser implements IParser {
break;
}
}
parserState.xmlEvent(nextEvent);
} catch (DataFormatException e) {
@ -524,9 +526,11 @@ public class XmlParser extends BaseParser implements IParser {
case PRIMITIVE_DATATYPE: {
IPrimitiveType<?> pd = (IPrimitiveType<?>) theElement;
String value = pd.getValueAsString();
if (value != null) {
if (value != null || super.hasExtensions(pd)) {
theEventWriter.writeStartElement(childName);
theEventWriter.writeAttribute("value", value);
if (value != null) {
theEventWriter.writeAttribute("value", value);
}
encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource);
theEventWriter.writeEndElement();
}
@ -623,8 +627,8 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeCompositeElementChildrenToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren,
boolean theContainedResource, CompositeChildElement theParent) throws XMLStreamException, DataFormatException {
private void encodeCompositeElementChildrenToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren, boolean theContainedResource, CompositeChildElement theParent)
throws XMLStreamException, DataFormatException {
for (CompositeChildElement nextChildElem : super.compositeChildIterator(theChildren, theContainedResource, theParent)) {
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
@ -635,7 +639,7 @@ public class XmlParser extends BaseParser implements IParser {
if (theResource instanceof IResource) {
narr = ((IResource) theResource).getText();
} else if (theResource instanceof IDomainResource) {
narr = ((IDomainResource)theResource).getText();
narr = ((IDomainResource) theResource).getText();
} else {
narr = null;
}
@ -652,8 +656,7 @@ public class XmlParser extends BaseParser implements IParser {
}
if (nextChild instanceof RuntimeChildContainedResources) {
encodeChildElementToStreamWriter(theResource, theEventWriter, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null, theContainedResource,
nextChildElem);
encodeChildElementToStreamWriter(theResource, theEventWriter, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null, theContainedResource, nextChildElem);
} else {
List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
@ -700,8 +703,8 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeCompositeElementToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> theElementDefinition,
boolean theIncludedResource, CompositeChildElement theParent) throws XMLStreamException, DataFormatException {
private void encodeCompositeElementToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> theElementDefinition, boolean theIncludedResource, CompositeChildElement theParent)
throws XMLStreamException, DataFormatException {
encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getExtensions(), theIncludedResource, theParent);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getChildren(), theIncludedResource, theParent);
@ -740,10 +743,10 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeResourceToStreamWriterInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeResourceToStreamWriterInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
/*
* DSTU2 requires extensions to come in a specific spot within the encoded content - This is a bit of a messy way to make that happen, but hopefully this won't matter as much once we use the HL7
* DSTU2 requires extensions to come in a specific spot within the encoded content - This is a bit of a messy way
* to make that happen, but hopefully this won't matter as much once we use the HL7
* structures
*/
@ -812,7 +815,7 @@ public class XmlParser extends BaseParser implements IParser {
writeOptionalTagWithValue(theEventWriter, "id", theResourceId.getIdPart());
writeCommentsPost(theEventWriter, theResourceId);
}
encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, resDef, theContainedResource, new CompositeChildElement(resDef));
} else {
@ -827,7 +830,7 @@ public class XmlParser extends BaseParser implements IParser {
writeOptionalTagWithValue(theEventWriter, "id", theResourceId.getIdPart());
writeCommentsPost(theEventWriter, theResourceId);
}
InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
IdDt resourceId = resource.getId();
String versionIdPart = resourceId.getVersionIdPart();
@ -934,15 +937,14 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeUndeclaredExtensions(IBaseResource theResource, XMLStreamWriter theWriter, List<? extends IBaseExtension<?, ?>> theExtensions, String tagName, boolean theIncludedResource)
throws XMLStreamException, DataFormatException {
private void encodeUndeclaredExtensions(IBaseResource theResource, XMLStreamWriter theWriter, List<? extends IBaseExtension<?, ?>> theExtensions, String tagName, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
for (IBaseExtension<?, ?> next : theExtensions) {
if (next == null || (ElementUtil.isEmpty(next.getValue()) && next.getExtension().isEmpty())) {
continue;
}
writeCommentsPre(theWriter, next);
theWriter.writeStartElement(tagName);
String url = next.getUrl();
@ -973,7 +975,7 @@ public class XmlParser extends BaseParser implements IParser {
encodeExtensionsIfPresent(theResource, theWriter, next, theIncludedResource);
theWriter.writeEndElement();
writeCommentsPost(theWriter, next);
}
@ -1109,7 +1111,8 @@ public class XmlParser extends BaseParser implements IParser {
}
/**
* This is just to work around the fact that casting java.util.List<ca.uhn.fhir.model.api.ExtensionDt> to java.util.List<? extends org.hl7.fhir.instance.model.api.IBaseExtension<?, ?>> seems to be
* This is just to work around the fact that casting java.util.List<ca.uhn.fhir.model.api.ExtensionDt> to
* java.util.List<? extends org.hl7.fhir.instance.model.api.IBaseExtension<?, ?>> seems to be
* rejected by the compiler some of the time.
*/
private <Q extends IBaseExtension<?, ?>> List<IBaseExtension<?, ?>> toBaseExtensionList(final List<Q> theList) {

View File

@ -24,8 +24,10 @@ import java.util.List;
public interface IBaseHasExtensions {
public List<? extends IBaseExtension<?, ?>> getExtension();
IBaseExtension<?, ?> addExtension();
public IBaseExtension<?, ?> addExtension();
List<? extends IBaseExtension<?, ?>> getExtension();
boolean hasExtension();
}

View File

@ -24,8 +24,10 @@ import java.util.List;
public interface IBaseHasModifierExtensions {
public List<? extends IBaseExtension<?, ?>> getModifierExtension();
public IBaseExtension<?, ?> addModifierExtension();
public IBaseExtension<?, ?> addModifierExtension();
public List<? extends IBaseExtension<?, ?>> getModifierExtension();
boolean hasModifierExtension();
}

View File

@ -0,0 +1,74 @@
package ca.uhn.fhir.parser;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.primitive.StringDt;
/**
* Created by Bill de Beaubien on 12/20/2015.
*/
public class EmptyElementWithExtensionDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(EmptyElementWithExtensionDstu2Test.class);
private static FhirContext ctx = FhirContext.forDstu2();
@Test
public void testNullFlavorCompositeJson() throws Exception {
Observation observation = new Observation();
observation.getCode().addCoding().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(observation);
ourLog.info(json);
observation = (Observation) parser.parseResource(json);
assertEquals(1, observation.getCode().getCoding().get(0).getUndeclaredExtensions().size());
}
@Test
public void testNullFlavorCompositeXml() throws Exception {
Observation observation = new Observation();
observation.getCode().addCoding().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
IParser parser = ctx.newXmlParser().setPrettyPrint(true);
String xml = parser.encodeResourceToString(observation);
ourLog.info(xml);
observation = (Observation) parser.parseResource(xml);
assertEquals(1, observation.getCode().getCoding().get(0).getUndeclaredExtensions().size());
}
@Test
public void testNullFlavorPrimitiveJson() throws Exception {
Observation observation = new Observation();
observation.getCode().getCoding().add(new CodingDt().setSystem("http://loinc.org").setCode("3141-9"));
observation.getStatusElement().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(observation);
ourLog.info(json);
observation = (Observation) parser.parseResource(json);
assertEquals(1, observation.getStatusElement().getUndeclaredExtensions().size());
}
@Test
public void testNullFlavorPrimitiveXml() throws Exception {
Observation observation = new Observation();
observation.getCode().getCoding().add(new CodingDt().setSystem("http://loinc.org").setCode("3141-9"));
observation.getStatusElement().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
IParser parser = ctx.newXmlParser().setPrettyPrint(true);
String xml = parser.encodeResourceToString(observation);
ourLog.info(xml);
observation = (Observation) parser.parseResource(xml);
assertEquals(1, observation.getStatusElement().getUndeclaredExtensions().size());
}
}

View File

@ -1,28 +0,0 @@
package ca.uhn.fhir.parser;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.primitive.StringDt;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Created by Bill de Beaubien on 12/20/2015.
*/
public class EmptyElementWithExtensionTest {
@Ignore
@Test
public void testNullFlavor() throws Exception {
Observation observation = new Observation();
observation.getCode().getCoding().add(new CodingDt().setSystem("http://loinc.org").setCode("3141-9"));
observation.getStatusElement().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
FhirContext ctx = FhirContext.forDstu2();
IParser parser = ctx.newXmlParser().setPrettyPrint(true);
String xml = parser.encodeResourceToString(observation);
observation = (Observation) parser.parseResource(xml);
assertEquals(1, observation.getStatusElement().getUndeclaredExtensions().size());
}
}

View File

@ -0,0 +1,75 @@
package ca.uhn.fhir.parser;
import static org.junit.Assert.assertEquals;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.StringType;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
/**
* Created by Bill de Beaubien on 12/20/2015.
*/
public class EmptyElementWithExtensionDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(EmptyElementWithExtensionDstu3Test.class);
private static FhirContext ctx = FhirContext.forDstu3();
@Test
public void testNullFlavorCompositeJson() throws Exception {
Observation observation = new Observation();
observation.getCode().addCoding().addExtension(new Extension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")));
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(observation);
ourLog.info(json);
observation = (Observation) parser.parseResource(json);
assertEquals(1, observation.getCode().getCoding().get(0).getExtension().size());
}
@Test
public void testNullFlavorCompositeXml() throws Exception {
Observation observation = new Observation();
observation.getCode().addCoding().addExtension(new Extension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")));
IParser parser = ctx.newXmlParser().setPrettyPrint(true);
String xml = parser.encodeResourceToString(observation);
ourLog.info(xml);
observation = (Observation) parser.parseResource(xml);
assertEquals(1, observation.getCode().getCoding().get(0).getExtension().size());
}
@Test
public void testNullFlavorPrimitiveJson() throws Exception {
Observation observation = new Observation();
observation.getCode().getCoding().add(new Coding().setSystem("http://loinc.org").setCode("3141-9"));
observation.getStatusElement().addExtension(new Extension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")));
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(observation);
ourLog.info(json);
observation = (Observation) parser.parseResource(json);
assertEquals(1, observation.getStatusElement().getExtension().size());
}
@Test
public void testNullFlavorPrimitiveXml() throws Exception {
Observation observation = new Observation();
observation.getCode().getCoding().add(new Coding().setSystem("http://loinc.org").setCode("3141-9"));
observation.getStatusElement().addExtension(new Extension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK")));
IParser parser = ctx.newXmlParser().setPrettyPrint(true);
String xml = parser.encodeResourceToString(observation);
ourLog.info(xml);
observation = (Observation) parser.parseResource(xml);
assertEquals(1, observation.getStatusElement().getExtension().size());
}
}

View File

@ -145,6 +145,11 @@
extensions on fields in the resonse Bundle (e.g. Bundle.entry.search).
Thanks to GitHub user am202 for reporting!
</action>
<action type="fix" issue="274">
Primitive elements with no value but an extension were sometimes not
encoded correctly in XML, and sometimes not parsed correctly in JSON.
Thanks to Bill de Beaubien for reporting!
</action>
</release>
<release version="1.4" date="2016-02-04">
<action type="add">