Fix #403 - Allow references to keep versions when encoding

This commit is contained in:
jamesagnew 2016-07-23 18:48:00 -04:00
parent c532d1a25b
commit 350e82b6cf
14 changed files with 681 additions and 67 deletions

View File

@ -0,0 +1,42 @@
package example;
import java.io.IOException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
public class Parser {
public static void main(String[] args) throws DataFormatException, IOException {
{
//START SNIPPET: disableStripVersions
FhirContext ctx = FhirContext.forDstu2();
IParser parser = ctx.newJsonParser();
// Disable the automatic stripping of versions from references on the parser
parser.setStripVersionsFromReferences(false);
//END SNIPPET: disableStripVersions
//START SNIPPET: disableStripVersionsCtx
ctx.getParserOptions().setStripVersionsFromReferences(false);
//END SNIPPET: disableStripVersionsCtx
}
{
//START SNIPPET: disableStripVersionsField
FhirContext ctx = FhirContext.forDstu2();
IParser parser = ctx.newJsonParser();
// Preserve versions only on these two fields (for the given parser)
parser.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference", "Patient.managingOrganization");
// You can also apply this setting to the context so that it will
// flow to all parsers
ctx.getParserOptions().setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference", "Patient.managingOrganization");
//END SNIPPET: disableStripVersionsField
}
}
}

View File

@ -103,6 +103,28 @@ public class FhirContext {
private final IFhirVersion myVersion;
private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap();
private boolean myInitializing;
private ParserOptions myParserOptions = new ParserOptions();
/**
* Returns the parser options object which will be used to supply default
* options to newly created parsers
*
* @return The parser options - Will not return <code>null</code>
*/
public ParserOptions getParserOptions() {
return myParserOptions;
}
/**
* Sets the parser options object which will be used to supply default
* options to newly created parsers
*
* @param theParserOptions The parser options object - Must not be <code>null</code>
*/
public void setParserOptions(ParserOptions theParserOptions) {
Validate.notNull(theParserOptions, "theParserOptions must not be null");
myParserOptions = theParserOptions;
}
/**
* @deprecated It is recommended that you use one of the static initializer methods instead

View File

@ -0,0 +1,127 @@
package ca.uhn.fhir.context;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import ca.uhn.fhir.parser.IParser;
/**
* This object supplies default configuration to all {@link IParser parser} instances
* created by a given {@link FhirContext}. It is accessed using {@link FhirContext#getParserOptions()}
* and {@link FhirContext#setParserOptions(ParserOptions)}.
* <p>
* It is fine to share a ParserOptions instances across multiple context instances.
* </p>
*/
public class ParserOptions {
private boolean myStripVersionsFromReferences = true;
private Set<String> myDontStripVersionsFromReferencesAtPaths = Collections.emptySet();
/**
* If supplied value(s), any resource references at the specified paths will have their
* resource versions encoded instead of being automatically stripped during the encoding
* process. This setting has no effect on the parsing process.
* <p>
* This method provides a finer-grained level of control than {@link #setStripVersionsFromReferences(boolean)}
* and any paths specified by this method will be encoded even if {@link #setStripVersionsFromReferences(boolean)}
* has been set to <code>true</code> (which is the default)
* </p>
*
* @param thePaths
* A collection of paths for which the resource versions will not be removed automatically
* when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that
* only resource name and field names with dots separating is allowed here (no repetition
* indicators, FluentPath expressions, etc.)
* @see #setStripVersionsFromReferences(boolean)
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
*/
public ParserOptions setDontStripVersionsFromReferencesAtPaths(String... thePaths) {
if (thePaths == null) {
setDontStripVersionsFromReferencesAtPaths((List<String>) null);
} else {
setDontStripVersionsFromReferencesAtPaths(Arrays.asList(thePaths));
}
return this;
}
/**
* If set to <code>true<code> (which is the default), resource references containing a version
* will have the version removed when the resource is encoded. This is generally good behaviour because
* in most situations, references from one resource to another should be to the resource by ID, not
* by ID and version. In some cases though, it may be desirable to preserve the version in resource
* links. In that case, this value should be set to <code>false</code>.
*
* @return Returns the parser instance's configuration setting for stripping versions from resource references when
* encoding. Default is <code>true</code>.
*/
public boolean isStripVersionsFromReferences() {
return myStripVersionsFromReferences ;
}
/**
* If set to <code>true<code> (which is the default), resource references containing a version
* will have the version removed when the resource is encoded. This is generally good behaviour because
* in most situations, references from one resource to another should be to the resource by ID, not
* by ID and version. In some cases though, it may be desirable to preserve the version in resource
* links. In that case, this value should be set to <code>false</code>.
* <p>
* This method provides the ability to globally disable reference encoding. If finer-grained
* control is needed, use {@link #setDontStripVersionsFromReferencesAtPaths(String...)}
* </p>
* @param theStripVersionsFromReferences
* Set this to <code>false<code> to prevent the parser from removing
* resource versions from references.
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
* @see #setDontStripVersionsFromReferencesAtPaths(String...)
*/
public ParserOptions setStripVersionsFromReferences(boolean theStripVersionsFromReferences) {
myStripVersionsFromReferences = theStripVersionsFromReferences;
return this;
}
/**
* If supplied value(s), any resource references at the specified paths will have their
* resource versions encoded instead of being automatically stripped during the encoding
* process. This setting has no effect on the parsing process.
* <p>
* This method provides a finer-grained level of control than {@link #setStripVersionsFromReferences(boolean)}
* and any paths specified by this method will be encoded even if {@link #setStripVersionsFromReferences(boolean)}
* has been set to <code>true</code> (which is the default)
* </p>
*
* @param thePaths
* A collection of paths for which the resource versions will not be removed automatically
* when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that
* only resource name and field names with dots separating is allowed here (no repetition
* indicators, FluentPath expressions, etc.)
* @see #setStripVersionsFromReferences(boolean)
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
*/
@SuppressWarnings("unchecked")
public ParserOptions setDontStripVersionsFromReferencesAtPaths(Collection<String> thePaths) {
if (thePaths == null) {
myDontStripVersionsFromReferencesAtPaths = Collections.emptySet();
} else if (thePaths instanceof HashSet) {
myDontStripVersionsFromReferencesAtPaths = (Set<String>) ((HashSet<String>)thePaths).clone();
} else {
myDontStripVersionsFromReferencesAtPaths = new HashSet<String>(thePaths);
}
return this;
}
/**
* Returns the value supplied to {@link IParser#setDontStripVersionsFromReferencesAtPaths(String...)}
*
* @see #setDontStripVersionsFromReferencesAtPaths(String...)
* @see #setStripVersionsFromReferences(boolean)
*/
public Set<String> getDontStripVersionsFromReferencesAtPaths() {
return myDontStripVersionsFromReferencesAtPaths;
}
}

View File

@ -29,6 +29,8 @@ import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@ -93,9 +95,11 @@ public abstract class BaseParser implements IParser {
private boolean myOmitResourceId;
private List<Class<? extends IBaseResource>> myPreferTypes;
private String myServerBaseUrl;
private boolean myStripVersionsFromReferences = true;
private Boolean myStripVersionsFromReferences;
private boolean mySummaryMode;
private boolean mySuppressNarratives;
private Set<String> myDontStripVersionsFromReferencesAtPaths;
/**
* Constructor
*
@ -265,7 +269,7 @@ public abstract class BaseParser implements IParser {
myContainedResources = contained;
}
private String determineReferenceText(IBaseReference theRef) {
private String determineReferenceText(IBaseReference theRef, CompositeChildElement theCompositeChildElement) {
IIdType ref = theRef.getReferenceElement();
if (isBlank(ref.getIdPart())) {
String reference = ref.getValue();
@ -284,7 +288,7 @@ public abstract class BaseParser implements IParser {
if (!refId.hasResourceType()) {
refId = refId.withResourceType(myContext.getResourceDefinition(theRef.getResource()).getName());
}
if (isStripVersionsFromReferences()) {
if (isStripVersionsFromReferences(theCompositeChildElement)) {
reference = refId.toVersionless().getValue();
} else {
reference = refId.getValue();
@ -299,13 +303,13 @@ public abstract class BaseParser implements IParser {
ref = ref.withResourceType(myContext.getResourceDefinition(theRef.getResource()).getName());
}
if (isNotBlank(myServerBaseUrl) && StringUtils.equals(myServerBaseUrl, ref.getBaseUrl())) {
if (isStripVersionsFromReferences()) {
if (isStripVersionsFromReferences(theCompositeChildElement)) {
return ref.toUnqualifiedVersionless().getValue();
} else {
return ref.toUnqualified().getValue();
}
} else {
if (isStripVersionsFromReferences()) {
if (isStripVersionsFromReferences(theCompositeChildElement)) {
return ref.toVersionless().getValue();
} else {
return ref.getValue();
@ -314,6 +318,31 @@ public abstract class BaseParser implements IParser {
}
}
private boolean isStripVersionsFromReferences(CompositeChildElement theCompositeChildElement) {
Boolean stripVersionsFromReferences = myStripVersionsFromReferences;
if (stripVersionsFromReferences != null) {
return stripVersionsFromReferences;
}
if (myContext.getParserOptions().isStripVersionsFromReferences() == false) {
return false;
}
Set<String> dontStripVersionsFromReferencesAtPaths = myDontStripVersionsFromReferencesAtPaths;
if (dontStripVersionsFromReferencesAtPaths != null) {
if (dontStripVersionsFromReferencesAtPaths.isEmpty() == false && theCompositeChildElement.anyPathMatches(dontStripVersionsFromReferencesAtPaths)) {
return false;
}
}
dontStripVersionsFromReferencesAtPaths = myContext.getParserOptions().getDontStripVersionsFromReferencesAtPaths();
if (dontStripVersionsFromReferencesAtPaths.isEmpty() == false && theCompositeChildElement.anyPathMatches(dontStripVersionsFromReferencesAtPaths)) {
return false;
}
return true;
}
protected abstract void doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException, DataFormatException;
protected abstract void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException, DataFormatException;
@ -401,10 +430,10 @@ public abstract class BaseParser implements IParser {
String childName = theChild.getChildNameByDatatype(type);
BaseRuntimeElementDefinition<?> childDef = theChild.getChildElementDefinitionByDatatype(type);
if (childDef == null) {
// if (theValue instanceof IBaseExtension) {
// return null;
// }
// if (theValue instanceof IBaseExtension) {
// return null;
// }
/*
* For RI structures Enumeration class, this replaces the child def
* with the "code" one. This is messy, and presumably there is a better
@ -431,12 +460,12 @@ public abstract class BaseParser implements IParser {
nextSuperType = nextSuperType.getSuperclass();
}
}
if (childDef == null) {
throwExceptionForUnknownChildType(theChild, type);
}
}
return new ChildNameAndDef(childName, childDef);
}
@ -558,7 +587,7 @@ public abstract class BaseParser implements IParser {
}
@Override
public boolean isStripVersionsFromReferences() {
public Boolean getStripVersionsFromReferences() {
return myStripVersionsFromReferences;
}
@ -667,7 +696,7 @@ public abstract class BaseParser implements IParser {
return parseTagList(new StringReader(theString));
}
protected List<? extends IBase> preProcessValues(BaseRuntimeChildDefinition theMetaChildUncast, IBaseResource theResource, List<? extends IBase> theValues) {
protected List<? extends IBase> preProcessValues(BaseRuntimeChildDefinition theMetaChildUncast, IBaseResource theResource, List<? extends IBase> theValues, CompositeChildElement theCompositeChildElement) {
if (myContext.getVersion().getVersion().isRi()) {
/*
@ -736,7 +765,7 @@ public abstract class BaseParser implements IParser {
*/
if (next instanceof IBaseReference) {
IBaseReference nextRef = (IBaseReference) next;
String refText = determineReferenceText(nextRef);
String refText = determineReferenceText(nextRef, theCompositeChildElement);
if (!StringUtils.equals(refText, nextRef.getReferenceElement().getValue())) {
if (retVal == theValues) {
@ -824,11 +853,39 @@ public abstract class BaseParser implements IParser {
}
@Override
public IParser setStripVersionsFromReferences(boolean theStripVersionsFromReferences) {
public IParser setStripVersionsFromReferences(Boolean theStripVersionsFromReferences) {
myStripVersionsFromReferences = theStripVersionsFromReferences;
return this;
}
@Override
public IParser setDontStripVersionsFromReferencesAtPaths(String... thePaths) {
if (thePaths == null) {
setDontStripVersionsFromReferencesAtPaths((List<String>) null);
} else {
setDontStripVersionsFromReferencesAtPaths(Arrays.asList(thePaths));
}
return this;
}
@SuppressWarnings("unchecked")
@Override
public IParser setDontStripVersionsFromReferencesAtPaths(Collection<String> thePaths) {
if (thePaths == null) {
myDontStripVersionsFromReferencesAtPaths = Collections.emptySet();
} else if (thePaths instanceof HashSet) {
myDontStripVersionsFromReferencesAtPaths = (Set<String>) ((HashSet<String>) thePaths).clone();
} else {
myDontStripVersionsFromReferencesAtPaths = new HashSet<String>(thePaths);
}
return this;
}
@Override
public Set<String> getDontStripVersionsFromReferencesAtPaths() {
return myDontStripVersionsFromReferencesAtPaths;
}
@Override
public IParser setSummaryMode(boolean theSummaryMode) {
mySummaryMode = theSummaryMode;
@ -970,6 +1027,34 @@ public abstract class BaseParser implements IParser {
}
public boolean anyPathMatches(Set<String> thePaths) {
StringBuilder b = new StringBuilder();
addParent(this, b);
String path = b.toString();
return thePaths.contains(path);
}
private void addParent(CompositeChildElement theParent, StringBuilder theB) {
if (theParent != null) {
if (theParent.myResDef != null) {
theB.append(theParent.myResDef.getName());
return;
}
if (theParent.myParent != null) {
addParent(theParent.myParent, theB);
}
if (theParent.myDef != null) {
if (theB.length() > 0) {
theB.append('.');
}
theB.append(theParent.myDef.getElementName());
}
}
}
public CompositeChildElement(RuntimeResourceDefinition theResDef) {
myResDef = theResDef;
myDef = null;
@ -992,14 +1077,14 @@ public abstract class BaseParser implements IParser {
}
private boolean checkIfParentShouldBeEncodedAndBuildPath(StringBuilder thePathBuilder, boolean theStarPass) {
return checkIfPathMatches(thePathBuilder, theStarPass, myEncodeElementsAppliesToResourceTypes, myEncodeElements, true);
return checkIfPathMatchesForEncoding(thePathBuilder, theStarPass, myEncodeElementsAppliesToResourceTypes, myEncodeElements, true);
}
private boolean checkIfParentShouldNotBeEncodedAndBuildPath(StringBuilder thePathBuilder, boolean theStarPass) {
return checkIfPathMatches(thePathBuilder, theStarPass, null, myDontEncodeElements, false);
return checkIfPathMatchesForEncoding(thePathBuilder, theStarPass, null, myDontEncodeElements, false);
}
private boolean checkIfPathMatches(StringBuilder thePathBuilder, boolean theStarPass, Set<String> theResourceTypes, Set<String> theElements, boolean theCheckingForWhitelist) {
private boolean checkIfPathMatchesForEncoding(StringBuilder thePathBuilder, boolean theStarPass, Set<String> theResourceTypes, Set<String> theElements, boolean theCheckingForWhitelist) {
if (myResDef != null) {
if (theResourceTypes != null) {
if (!theResourceTypes.contains(myResDef.getName())) {
@ -1069,12 +1154,12 @@ public abstract class BaseParser implements IParser {
retVal = !checkIfParentShouldNotBeEncodedAndBuildPath(new StringBuilder(), true);
}
}
// if (retVal == false && myEncodeElements.contains("*.(mandatory)")) {
// if (myDef.getMin() > 0) {
// retVal = true;
// }
// }
// if (retVal == false && myEncodeElements.contains("*.(mandatory)")) {
// if (myDef.getMin() > 0) {
// retVal = true;
// }
// }
return retVal;
}
}

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.parser;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@ -32,6 +33,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.ParserOptions;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
@ -64,7 +66,7 @@ public interface IParser {
* @return An encoded tag list
*/
String encodeTagListToString(TagList theTagList);
/**
* Encodes a tag list, as defined in the <a href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR
* Specification</a>.
@ -75,7 +77,7 @@ public interface IParser {
* The writer to encode to
*/
void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException;
/**
* See {@link #setEncodeElements(Set)}
*/
@ -121,9 +123,11 @@ public interface IParser {
* links. In that case, this value should be set to <code>false</code>.
*
* @return Returns the parser instance's configuration setting for stripping versions from resource references when
* encoding. Default is <code>true</code>.
* encoding. This method will retun <code>null</code> if no value is set, in which case
* the value from the {@link ParserOptions} will be used (default is <code>true</code>)
* @see ParserOptions
*/
boolean isStripVersionsFromReferences();
Boolean getStripVersionsFromReferences();
/**
* Is the parser in "summary mode"? See {@link #setSummaryMode(boolean)} for information
@ -301,21 +305,22 @@ public interface IParser {
IParser setParserErrorHandler(IParserErrorHandler theErrorHandler);
/**
* If set, when parsing resources the parser will try to use the given types when possible, in
* If set, when parsing resources the parser will try to use the given types when possible, in
* the order that they are provided (from highest to lowest priority). For example, if a custom
* type which declares to implement the Patient resource is passed in here, and the
* type which declares to implement the Patient resource is passed in here, and the
* parser is parsing a Bundle containing a Patient resource, the parser will use the given
* custom type.
* <p>
* This feature is related to, but not the same as the
* This feature is related to, but not the same as the
* {@link FhirContext#setDefaultTypeForProfile(String, Class)} feature.
* <code>setDefaultTypeForProfile</code> is used to specify a type to be used
* when a resource explicitly declares support for a given profile. This
* feature specifies a type to be used irrespective of the profile declaration
* in the metadata statement.
* </p>
* in the metadata statement.
* </p>
*
* @param thePreferTypes The preferred types, or <code>null</code>
* @param thePreferTypes
* The preferred types, or <code>null</code>
*/
void setPreferTypes(List<Class<? extends IBaseResource>> thePreferTypes);
@ -345,13 +350,17 @@ public interface IParser {
* in most situations, references from one resource to another should be to the resource by ID, not
* by ID and version. In some cases though, it may be desirable to preserve the version in resource
* links. In that case, this value should be set to <code>false</code>.
*
* <p>
* This method provides the ability to globally disable reference encoding. If finer-grained
* control is needed, use {@link #setDontStripVersionsFromReferencesAtPaths(String...)}
* </p>
* @param theStripVersionsFromReferences
* Set this to <code>false<code> to prevent the parser from removing
* resource versions from references.
* Set this to <code>false<code> to prevent the parser from removing resource versions from references (or <code>null</code> to apply the default setting from the {@link ParserOptions}
* @see #setDontStripVersionsFromReferencesAtPaths(String...)
* @see ParserOptions
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
*/
IParser setStripVersionsFromReferences(boolean theStripVersionsFromReferences);
IParser setStripVersionsFromReferences(Boolean theStripVersionsFromReferences);
/**
* If set to <code>true</code> (default is <code>false</code>) only elements marked by the FHIR specification as
@ -366,4 +375,60 @@ public interface IParser {
* values.
*/
IParser setSuppressNarratives(boolean theSuppressNarratives);
/**
* If supplied value(s), any resource references at the specified paths will have their
* resource versions encoded instead of being automatically stripped during the encoding
* process. This setting has no effect on the parsing process.
* <p>
* This method provides a finer-grained level of control than {@link #setStripVersionsFromReferences(Boolean)}
* and any paths specified by this method will be encoded even if {@link #setStripVersionsFromReferences(Boolean)}
* has been set to <code>true</code> (which is the default)
* </p>
*
* @param thePaths
* A collection of paths for which the resource versions will not be removed automatically
* when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that
* only resource name and field names with dots separating is allowed here (no repetition
* indicators, FluentPath expressions, etc.). Set to <code>null</code> to use the value
* set in the {@link ParserOptions}
* @see #setStripVersionsFromReferences(Boolean)
* @see ParserOptions
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
*/
IParser setDontStripVersionsFromReferencesAtPaths(String... thePaths);
/**
* If supplied value(s), any resource references at the specified paths will have their
* resource versions encoded instead of being automatically stripped during the encoding
* process. This setting has no effect on the parsing process.
* <p>
* This method provides a finer-grained level of control than {@link #setStripVersionsFromReferences(Boolean)}
* and any paths specified by this method will be encoded even if {@link #setStripVersionsFromReferences(Boolean)}
* has been set to <code>true</code> (which is the default)
* </p>
*
* @param thePaths
* A collection of paths for which the resource versions will not be removed automatically
* when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that
* only resource name and field names with dots separating is allowed here (no repetition
* indicators, FluentPath expressions, etc.). Set to <code>null</code> to use the value
* set in the {@link ParserOptions}
* @see #setStripVersionsFromReferences(Boolean)
* @see ParserOptions
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
*/
IParser setDontStripVersionsFromReferencesAtPaths(Collection<String> thePaths);
/**
* Returns the value supplied to {@link IParser#setDontStripVersionsFromReferencesAtPaths(String...)}
* or <code>null</code> if no value has been set for this parser (in which case the default from
* the {@link ParserOptions} will be used}
*
* @see #setDontStripVersionsFromReferencesAtPaths(String...)
* @see #setStripVersionsFromReferences(Boolean)
* @see ParserOptions
*/
Set<String> getDontStripVersionsFromReferencesAtPaths();
}

View File

@ -152,7 +152,7 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private boolean addToHeldExtensions(int valueIdx, List<? extends IBaseExtension<?, ?>> ext, ArrayList<ArrayList<HeldExtension>> list, boolean theIsModifier) {
private boolean addToHeldExtensions(int valueIdx, List<? extends IBaseExtension<?, ?>> ext, ArrayList<ArrayList<HeldExtension>> list, boolean theIsModifier, CompositeChildElement theChildElem) {
if (ext.size() > 0) {
list.ensureCapacity(valueIdx);
while (list.size() <= valueIdx) {
@ -162,7 +162,7 @@ public class JsonParser extends BaseParser implements IParser {
list.set(valueIdx, new ArrayList<JsonParser.HeldExtension>());
}
for (IBaseExtension<?, ?> next : ext) {
list.get(valueIdx).add(new HeldExtension(next, theIsModifier));
list.get(valueIdx).add(new HeldExtension(next, theIsModifier, theChildElem));
}
return true;
} else {
@ -593,7 +593,7 @@ public class JsonParser extends BaseParser implements IParser {
if (nextChildElem.getDef().getElementName().equals("extension") || nextChildElem.getDef().getElementName().equals("modifierExtension") || nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
if (!haveWrittenExtensions) {
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, myContext.getElementDefinition(theElement.getClass()), theResDef, theResource);
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, myContext.getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem);
haveWrittenExtensions = true;
}
continue;
@ -629,7 +629,7 @@ public class JsonParser extends BaseParser implements IParser {
}
List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
values = super.preProcessValues(nextChild, theResource, values);
values = super.preProcessValues(nextChild, theResource, values, nextChildElem);
if (values == null || values.isEmpty()) {
continue;
@ -673,20 +673,20 @@ public class JsonParser extends BaseParser implements IParser {
if (primitive) {
if (nextValue instanceof ISupportsUndeclaredExtensions) {
List<ExtensionDt> ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredExtensions();
force |= addToHeldExtensions(valueIdx, ext, extensions, false);
force |= addToHeldExtensions(valueIdx, ext, extensions, false, nextChildElem);
ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredModifierExtensions();
force |= addToHeldExtensions(valueIdx, ext, modifierExtensions, true);
force |= addToHeldExtensions(valueIdx, ext, modifierExtensions, true, nextChildElem);
} else {
if (nextValue instanceof IBaseHasExtensions) {
IBaseHasExtensions element = (IBaseHasExtensions) nextValue;
List<? extends IBaseExtension<?, ?>> ext = element.getExtension();
force |= addToHeldExtensions(valueIdx, ext, extensions, false);
force |= addToHeldExtensions(valueIdx, ext, extensions, false, nextChildElem);
}
if (nextValue instanceof IBaseHasModifierExtensions) {
IBaseHasModifierExtensions element = (IBaseHasModifierExtensions) nextValue;
List<? extends IBaseExtension<?, ?>> ext = element.getModifierExtension();
force |= addToHeldExtensions(valueIdx, ext, extensions, true);
force |= addToHeldExtensions(valueIdx, ext, extensions, true, nextChildElem);
}
}
if (nextValue.hasFormatComment()) {
@ -984,31 +984,32 @@ public class JsonParser extends BaseParser implements IParser {
/**
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object
* called _name): resource extensions, and extension extensions
* @param theChildElem
*/
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonWriter theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IBaseResource theResource) throws IOException {
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonWriter theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IBaseResource theResource, CompositeChildElement theChildElem) throws IOException {
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
// Undeclared extensions
extractUndeclaredExtensions(theElement, extensions, modifierExtensions);
extractUndeclaredExtensions(theElement, extensions, modifierExtensions, theChildElem);
// Declared extensions
if (theElementDef != null) {
extractDeclaredExtensions(theElement, theElementDef, extensions, modifierExtensions);
extractDeclaredExtensions(theElement, theElementDef, extensions, modifierExtensions, theChildElem);
}
// Write the extensions
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
}
private void extractDeclaredExtensions(IBase theResource, BaseRuntimeElementDefinition<?> resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) {
private void extractDeclaredExtensions(IBase theResource, BaseRuntimeElementDefinition<?> resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, CompositeChildElement theChildElem) {
for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsNonModifier()) {
for (IBase nextValue : nextDef.getAccessor().getValues(theResource)) {
if (nextValue != null) {
if (nextValue == null || nextValue.isEmpty()) {
continue;
}
extensions.add(new HeldExtension(nextDef, nextValue));
extensions.add(new HeldExtension(nextDef, nextValue, theChildElem));
}
}
}
@ -1018,13 +1019,13 @@ public class JsonParser extends BaseParser implements IParser {
if (nextValue == null || nextValue.isEmpty()) {
continue;
}
modifierExtensions.add(new HeldExtension(nextDef, nextValue));
modifierExtensions.add(new HeldExtension(nextDef, nextValue, theChildElem));
}
}
}
}
private void extractUndeclaredExtensions(IBase theElement, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) {
private void extractUndeclaredExtensions(IBase theElement, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, CompositeChildElement theChildElem) {
if (theElement instanceof ISupportsUndeclaredExtensions) {
ISupportsUndeclaredExtensions element = (ISupportsUndeclaredExtensions) theElement;
List<ExtensionDt> ext = element.getUndeclaredExtensions();
@ -1032,7 +1033,7 @@ public class JsonParser extends BaseParser implements IParser {
if (next == null || next.isEmpty()) {
continue;
}
extensions.add(new HeldExtension(next, false));
extensions.add(new HeldExtension(next, false, theChildElem));
}
ext = element.getUndeclaredModifierExtensions();
@ -1040,7 +1041,7 @@ public class JsonParser extends BaseParser implements IParser {
if (next == null || next.isEmpty()) {
continue;
}
modifierExtensions.add(new HeldExtension(next, true));
modifierExtensions.add(new HeldExtension(next, true, theChildElem));
}
} else {
if (theElement instanceof IBaseHasExtensions) {
@ -1050,7 +1051,7 @@ public class JsonParser extends BaseParser implements IParser {
if (next == null || (ElementUtil.isEmpty(next.getValue()) && next.getExtension().isEmpty())) {
continue;
}
extensions.add(new HeldExtension(next, false));
extensions.add(new HeldExtension(next, false, theChildElem));
}
}
if (theElement instanceof IBaseHasModifierExtensions) {
@ -1060,7 +1061,7 @@ public class JsonParser extends BaseParser implements IParser {
if (next == null || next.isEmpty()) {
continue;
}
modifierExtensions.add(new HeldExtension(next, true));
modifierExtensions.add(new HeldExtension(next, true, theChildElem));
}
}
}
@ -1591,18 +1592,21 @@ public class JsonParser extends BaseParser implements IParser {
private boolean myModifier;
private IBaseExtension<?, ?> myUndeclaredExtension;
private IBase myValue;
private CompositeChildElement myChildElem;
public HeldExtension(IBaseExtension<?, ?> theUndeclaredExtension, boolean theModifier) {
public HeldExtension(IBaseExtension<?, ?> theUndeclaredExtension, boolean theModifier, CompositeChildElement theChildElem) {
assert theUndeclaredExtension != null;
myUndeclaredExtension = theUndeclaredExtension;
myModifier = theModifier;
myChildElem = theChildElem;
}
public HeldExtension(RuntimeChildDeclaredExtensionDefinition theDef, IBase theValue) {
public HeldExtension(RuntimeChildDeclaredExtensionDefinition theDef, IBase theValue, CompositeChildElement theChildElem) {
assert theDef != null;
assert theValue != null;
myDef = theDef;
myValue = theValue;
myChildElem = theChildElem;
}
@Override
@ -1626,7 +1630,7 @@ public class JsonParser extends BaseParser implements IParser {
BaseRuntimeElementDefinition<?> def = myDef.getChildElementDefinitionByDatatype(myValue.getClass());
if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) {
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource);
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource, myChildElem);
} else {
String childName = myDef.getChildNameByDatatype(myValue.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, null, false);
@ -1673,7 +1677,7 @@ public class JsonParser extends BaseParser implements IParser {
* Pre-process value - This is called in case the value is a reference
* since we might modify the text
*/
value = JsonParser.super.preProcessValues(myDef, theResource, Collections.singletonList(value)).get(0);
value = JsonParser.super.preProcessValues(myDef, theResource, Collections.singletonList(value), myChildElem).get(0);
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
String childName = extDef.getChildNameByDatatype(value.getClass());

View File

@ -632,7 +632,7 @@ public class XmlParser extends BaseParser implements IParser {
} else {
List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
values = super.preProcessValues(nextChild, theResource, values);
values = super.preProcessValues(nextChild, theResource, values, nextChildElem);
if (values == null || values.isEmpty()) {
continue;

View File

@ -26,6 +26,8 @@ import java.util.Arrays;
import java.util.Locale;
import java.util.TimeZone;
import ca.uhn.fhir.context.FhirContext;
public class TestUtil {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
@ -63,6 +65,11 @@ public class TestUtil {
throw new Error(e);
}
}
if (Modifier.isFinal(next.getModifiers())) {
if (next.getType().equals(FhirContext.class)) {
throw new Error("Test has final field of type FhirContext: " + next);
}
}
}
}

View File

@ -39,6 +39,7 @@ import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.ParserOptions;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc;
@ -102,6 +103,10 @@ public class BaseConfig implements SchedulingConfigurer {
public FhirContext fhirContextDstu3() {
if (ourFhirContextDstu3 == null) {
ourFhirContextDstu3 = FhirContext.forDstu3();
// Don't strip versions in some places
ParserOptions parserOptions = ourFhirContextDstu3.getParserOptions();
parserOptions.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference");
}
return ourFhirContextDstu3;
}

View File

@ -41,6 +41,7 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.dstu3.model.AuditEvent;
import org.hl7.fhir.dstu3.model.BaseResource;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
@ -438,6 +439,34 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart());
}
@Test
public void testPreserveVersionsOnAuditEvent() {
Organization org = new Organization();
org.setName("ORG");
IIdType orgId = ourClient.create().resource(org).execute().getId();
assertEquals("1", orgId.getVersionIdPart());
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.getManagingOrganization().setReference(orgId.toUnqualified().getValue());
IIdType patientId = ourClient.create().resource(patient).execute().getId();
assertEquals("1", patientId.getVersionIdPart());
AuditEvent ae = new org.hl7.fhir.dstu3.model.AuditEvent();
ae.addEntity().getReference().setReference(patientId.toUnqualified().getValue());
IIdType aeId = ourClient.create().resource(ae).execute().getId();
assertEquals("1", aeId.getVersionIdPart());
patient = ourClient.read().resource(Patient.class).withId(patientId).execute();
assertTrue(patient.getManagingOrganization().getReferenceElement().hasIdPart());
assertFalse(patient.getManagingOrganization().getReferenceElement().hasVersionIdPart());
ae = ourClient.read().resource(AuditEvent.class).withId(aeId).execute();
assertTrue(ae.getEntityFirstRep().getReference().getReferenceElement().hasIdPart());
assertTrue(ae.getEntityFirstRep().getReference().getReferenceElement().hasVersionIdPart());
}
// private void delete(String theResourceType, String theParamName, String theParamValue) {
// Bundle resources;
// do {

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
@ -9,6 +10,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
@ -25,6 +27,7 @@ import org.hamcrest.core.StringContains;
import org.hl7.fhir.dstu3.model.Address.AddressUse;
import org.hl7.fhir.dstu3.model.Address.AddressUseEnumFactory;
import org.hl7.fhir.dstu3.model.Attachment;
import org.hl7.fhir.dstu3.model.AuditEvent;
import org.hl7.fhir.dstu3.model.Binary;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
@ -35,15 +38,14 @@ import org.hl7.fhir.dstu3.model.Condition;
import org.hl7.fhir.dstu3.model.Condition.ConditionVerificationStatus;
import org.hl7.fhir.dstu3.model.Conformance;
import org.hl7.fhir.dstu3.model.Conformance.UnknownContentCode;
import org.hl7.fhir.dstu3.model.Coverage;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.DecimalType;
import org.hl7.fhir.dstu3.model.DiagnosticReport;
import org.hl7.fhir.dstu3.model.EnumFactory;
import org.hl7.fhir.dstu3.model.Enumeration;
import org.hl7.fhir.dstu3.model.ExplanationOfBenefit;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu3.model.ExplanationOfBenefit;
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.HumanName;
import org.hl7.fhir.dstu3.model.IdType;
@ -62,7 +64,6 @@ import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.dstu3.model.SampledData;
import org.hl7.fhir.dstu3.model.SimpleQuantity;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.Type;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.After;
@ -83,7 +84,7 @@ import net.sf.json.JSONSerializer;
import net.sf.json.JsonConfig;
public class JsonParserDstu3Test {
private static final FhirContext ourCtx = FhirContext.forDstu3();
private static FhirContext ourCtx = FhirContext.forDstu3();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserDstu3Test.class);
@After
@ -795,6 +796,151 @@ public class JsonParserDstu3Test {
}
@Test
public void testEncodeHistoryStripVersionsFromReferences() {
ourCtx = FhirContext.forDstu3();
assertNull(ourCtx.newJsonParser().getStripVersionsFromReferences());
Patient p = new Patient();
p.setManagingOrganization(new Reference("http://foo.com/Organization/2/_history/1"));
IParser parser = ourCtx.newJsonParser();
String enc = parser.setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
parser.setStripVersionsFromReferences(false);
enc = parser.setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
ourCtx = FhirContext.forDstu3();
}
@Test
public void testEncodeHistoryStripVersionsFromReferencesFromContext() {
ourCtx = FhirContext.forDstu3();
assertTrue(ourCtx.getParserOptions().isStripVersionsFromReferences());
Patient p = new Patient();
p.setManagingOrganization(new Reference("http://foo.com/Organization/2/_history/1"));
IParser parser = ourCtx.newJsonParser();
String enc = parser.setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
ourCtx.getParserOptions().setStripVersionsFromReferences(false);
enc = parser.setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
parser.setStripVersionsFromReferences(true);
enc = parser.setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
ourCtx = FhirContext.forDstu3();
}
@Test
public void testEncodeHistoryEncodeVersionsAtPath1() {
ourCtx = FhirContext.forDstu3();
assertNull(ourCtx.newJsonParser().getStripVersionsFromReferences());
Patient p = new Patient();
p.setManagingOrganization(new Reference("http://foo.com/Organization/2/_history/1"));
IParser parser = ourCtx.newJsonParser();
parser.setDontStripVersionsFromReferencesAtPaths("Patient.managingOrganization");
String enc = parser.setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
}
@Test
public void testEncodeHistoryEncodeVersionsAtPath2() {
ourCtx = FhirContext.forDstu3();
assertNull(ourCtx.newJsonParser().getStripVersionsFromReferences());
assertTrue(ourCtx.getParserOptions().isStripVersionsFromReferences());
Patient p = new Patient();
p.setManagingOrganization(new Reference("http://foo.com/Organization/2/_history/1"));
IParser parser = ourCtx.newJsonParser();
parser.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference");
String enc = parser.setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
}
@Test
public void testEncodeHistoryEncodeVersionsAtPathUsingOptions() {
ourCtx = FhirContext.forDstu3();
assertNull(ourCtx.newJsonParser().getStripVersionsFromReferences());
assertTrue(ourCtx.getParserOptions().isStripVersionsFromReferences());
assertThat(ourCtx.getParserOptions().getDontStripVersionsFromReferencesAtPaths(), empty());
Patient p = new Patient();
p.setManagingOrganization(new Reference("http://foo.com/Organization/2/_history/1"));
IParser parser = ourCtx.newJsonParser();
ourCtx.getParserOptions().setDontStripVersionsFromReferencesAtPaths("Patient.managingOrganization");
String enc = parser.setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
ourCtx.getParserOptions().setDontStripVersionsFromReferencesAtPaths(Arrays.asList("Patient.managingOrganization"));
enc = parser.setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
ourCtx.getParserOptions().setDontStripVersionsFromReferencesAtPaths(new HashSet<String>(Arrays.asList("Patient.managingOrganization")));
enc = parser.setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
}
@Test
public void testEncodeHistoryEncodeVersionsAtPath3() {
ourCtx = FhirContext.forDstu3();
assertNull(ourCtx.newJsonParser().getStripVersionsFromReferences());
AuditEvent auditEvent = new AuditEvent();
auditEvent.addEntity().setReference(new Reference("http://foo.com/Organization/2/_history/1"));
IParser parser = ourCtx.newJsonParser();
parser.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference");
String enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
parser.setDontStripVersionsFromReferencesAtPaths(new ArrayList<String>());
enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
parser.setDontStripVersionsFromReferencesAtPaths((String[])null);
enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
parser.setDontStripVersionsFromReferencesAtPaths((List<String>)null);
enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
ourLog.info(enc);
assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
}
@Test
public void testEncodeExtendedInfrastructureComponent() {
IParser parser = ourCtx.newJsonParser();

View File

@ -39,6 +39,7 @@ import org.hl7.fhir.dstu3.model.Address.AddressUseEnumFactory;
import org.hl7.fhir.dstu3.model.AllergyIntolerance;
import org.hl7.fhir.dstu3.model.Annotation;
import org.hl7.fhir.dstu3.model.Appointment;
import org.hl7.fhir.dstu3.model.AuditEvent;
import org.hl7.fhir.dstu3.model.Binary;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
@ -113,13 +114,47 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.TestUtil;
public class XmlParserDstu3Test {
private static final FhirContext ourCtx = FhirContext.forDstu3();
private static FhirContext ourCtx = FhirContext.forDstu3();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserDstu3Test.class);
@After
public void after() {
ourCtx.setNarrativeGenerator(null);
}
@Test
public void testEncodeHistoryEncodeVersionsAtPath3() {
ourCtx = FhirContext.forDstu3();
assertNull(ourCtx.newXmlParser().getStripVersionsFromReferences());
AuditEvent auditEvent = new AuditEvent();
auditEvent.addEntity().setReference(new Reference("http://foo.com/Organization/2/_history/1"));
IParser parser = ourCtx.newXmlParser();
parser.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference");
String enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
ourLog.info(enc);
assertThat(enc, containsString("<reference value=\"http://foo.com/Organization/2/_history/1\"/>"));
parser.setDontStripVersionsFromReferencesAtPaths(new ArrayList<String>());
enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
ourLog.info(enc);
assertThat(enc, containsString("<reference value=\"http://foo.com/Organization/2\"/>"));
parser.setDontStripVersionsFromReferencesAtPaths((String[])null);
enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
ourLog.info(enc);
assertThat(enc, containsString("<reference value=\"http://foo.com/Organization/2\"/>"));
parser.setDontStripVersionsFromReferencesAtPaths((List<String>)null);
enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
ourLog.info(enc);
assertThat(enc, containsString("<reference value=\"http://foo.com/Organization/2\"/>"));
}
@Test
public void testBundleWithBinary() {

View File

@ -78,6 +78,12 @@
DSTU2 HL7org structures with the JAX-RS module. Thanks to Carlo Mion
for the pull request!
</action>
<action type="add" issue="403">
It is not possible to configure both the parser and the context to
preserve versions in resource references (default behaviour is to
strip versions from references). Thanks to GitHub user @cknaap
for the suggestion!
</action>
</release>
<release version="1.6" date="2016-07-07">
<action type="fix">

View File

@ -209,7 +209,48 @@ System.out.println(encoded);]]></source>
</section>
<section name="Versionned References">
<p>
By default, HAPI will strip resource versions from references between resources.
For example, if you set a reference to <code>Patient.managingOrganization</code>
to the value <code>Patient/123/_history/2</code>, HAPI will encode this
reference as <code>Patient/123</code>.
</p>
<p>
This is because in most circumstances, references between resources should be
versionless (e.g. the reference just points to the latest version, whatever
version that might be).
</p>
<p>
There are valid circumstances however for wanting versioned references. If you
need HAPI to emit versionned references, you have a few options:
</p>
<p>
You can force the parser to never strip versions:
</p>
<macro name="snippet">
<param name="id" value="disableStripVersions" />
<param name="file" value="examples/src/main/java/example/Parser.java" />
</macro>
<p>
You can also disable this behaviour entirely on the context (so that it
will apply to all parsers):
</p>
<macro name="snippet">
<param name="id" value="disableStripVersionsCtx" />
<param name="file" value="examples/src/main/java/example/Parser.java" />
</macro>
<p>
You can also configure HAPI to not strip versions only on certain fields. This
is desirable if you want versionless references in most places but need them
in some places:
</p>
<macro name="snippet">
<param name="id" value="disableStripVersionsField" />
<param name="file" value="examples/src/main/java/example/Parser.java" />
</macro>
</section>
</body>
</document>