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:
parent
66ec863bde
commit
496d866f48
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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">
|
||||
|
|
Loading…
Reference in New Issue