mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-03-09 14:33:32 +00:00
Configurable summary mode (#5944)
* Fix #5871 - Configurable summary mode * Add docs * Spotless * Add javadoc
This commit is contained in:
parent
ac3a5e2ad2
commit
ab0b62706a
@ -20,8 +20,10 @@
|
|||||||
package ca.uhn.fhir.context;
|
package ca.uhn.fhir.context;
|
||||||
|
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.util.CollectionUtil;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -42,6 +44,8 @@ public class ParserOptions {
|
|||||||
private Set<String> myDontStripVersionsFromReferencesAtPaths = Collections.emptySet();
|
private Set<String> myDontStripVersionsFromReferencesAtPaths = Collections.emptySet();
|
||||||
private boolean myOverrideResourceIdWithBundleEntryFullUrl = true;
|
private boolean myOverrideResourceIdWithBundleEntryFullUrl = true;
|
||||||
private boolean myAutoContainReferenceTargetsWithNoId = true;
|
private boolean myAutoContainReferenceTargetsWithNoId = true;
|
||||||
|
private Set<String> myEncodeElementsForSummaryMode = null;
|
||||||
|
private Set<String> myDontEncodeElementsForSummaryMode = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to {@literal true} (which is the default), contained resources may be specified by
|
* If set to {@literal true} (which is the default), contained resources may be specified by
|
||||||
@ -143,7 +147,7 @@ public class ParserOptions {
|
|||||||
if (thePaths == null) {
|
if (thePaths == null) {
|
||||||
setDontStripVersionsFromReferencesAtPaths((List<String>) null);
|
setDontStripVersionsFromReferencesAtPaths((List<String>) null);
|
||||||
} else {
|
} else {
|
||||||
setDontStripVersionsFromReferencesAtPaths(Arrays.asList(thePaths));
|
setDontStripVersionsFromReferencesAtPaths(CollectionUtil.newSet(thePaths));
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -205,4 +209,119 @@ public class ParserOptions {
|
|||||||
myOverrideResourceIdWithBundleEntryFullUrl = theOverrideResourceIdWithBundleEntryFullUrl;
|
myOverrideResourceIdWithBundleEntryFullUrl = theOverrideResourceIdWithBundleEntryFullUrl;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This option specifies one or more elements that should be included when the parser is encoding
|
||||||
|
* a resource in {@link IParser#setSummaryMode(boolean) summary mode}, even if the element is not
|
||||||
|
* a part of the base FHIR specification's list of summary elements. Examples of valid values
|
||||||
|
* include:
|
||||||
|
* <ul>
|
||||||
|
* <li><b>Patient.maritalStatus</b> - Encode the entire maritalStatus CodeableConcept, even though Patient.maritalStatus is not a summary element</li>
|
||||||
|
* <li><b>Patient.maritalStatus.text</b> - Encode only the text component of the patient's maritalStatus</li>
|
||||||
|
* <li><b>*.text</b> - Encode the text element on any resource (only the very first position may contain a
|
||||||
|
* wildcard)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @see IParser#setSummaryMode(boolean)
|
||||||
|
* @see IParser#setEncodeElements(Set) Can be used to specify these values for an individual parser instance.
|
||||||
|
* @since 7.4.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"UnusedReturnValue"})
|
||||||
|
@Nonnull
|
||||||
|
public ParserOptions setEncodeElementsForSummaryMode(@Nonnull String... theEncodeElements) {
|
||||||
|
return setEncodeElementsForSummaryMode(CollectionUtil.newSet(theEncodeElements));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This option specifies one or more elements that should be included when the parser is encoding
|
||||||
|
* a resource in {@link IParser#setSummaryMode(boolean) summary mode}, even if the element is not
|
||||||
|
* a part of the base FHIR specification's list of summary elements. Examples of valid values
|
||||||
|
* include:
|
||||||
|
* <ul>
|
||||||
|
* <li><b>Patient.maritalStatus</b> - Encode the entire maritalStatus CodeableConcept, even though Patient.maritalStatus is not a summary element</li>
|
||||||
|
* <li><b>Patient.maritalStatus.text</b> - Encode only the text component of the patient's maritalStatus</li>
|
||||||
|
* <li><b>*.text</b> - Encode the text element on any resource (only the very first position may contain a
|
||||||
|
* wildcard)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @see IParser#setSummaryMode(boolean)
|
||||||
|
* @see IParser#setEncodeElements(Set) Can be used to specify these values for an individual parser instance.
|
||||||
|
* @since 7.4.0
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public ParserOptions setEncodeElementsForSummaryMode(@Nullable Collection<String> theEncodeElements) {
|
||||||
|
Set<String> encodeElements = null;
|
||||||
|
if (theEncodeElements != null && !theEncodeElements.isEmpty()) {
|
||||||
|
encodeElements = new HashSet<>(theEncodeElements);
|
||||||
|
}
|
||||||
|
myEncodeElementsForSummaryMode = encodeElements;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the values provided to {@link #setEncodeElementsForSummaryMode(Collection)}
|
||||||
|
* or <code>null</code>
|
||||||
|
*
|
||||||
|
* @since 7.4.0
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Set<String> getEncodeElementsForSummaryMode() {
|
||||||
|
return myEncodeElementsForSummaryMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This option specifies one or more elements that should be excluded when the parser is encoding
|
||||||
|
* a resource in {@link IParser#setSummaryMode(boolean) summary mode}, even if the element is
|
||||||
|
* a part of the base FHIR specification's list of summary elements. Examples of valid values
|
||||||
|
* include:
|
||||||
|
* <ul>
|
||||||
|
* <li><b>Patient.name</b> - Do not include the patient's name</li>
|
||||||
|
* <li><b>Patient.name.family</b> - Do not include the patient's family name</li>
|
||||||
|
* <li><b>*.name</b> - Do not include the name element on any resource type</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @see IParser#setSummaryMode(boolean)
|
||||||
|
* @see IParser#setDontEncodeElements(Collection) Can be used to specify these values for an individual parser instance.
|
||||||
|
* @since 7.4.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"UnusedReturnValue"})
|
||||||
|
@Nonnull
|
||||||
|
public ParserOptions setDontEncodeElementsForSummaryMode(@Nonnull String... theEncodeElements) {
|
||||||
|
return setDontEncodeElementsForSummaryMode(CollectionUtil.newSet(theEncodeElements));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This option specifies one or more elements that should be excluded when the parser is encoding
|
||||||
|
* a resource in {@link IParser#setSummaryMode(boolean) summary mode}, even if the element is
|
||||||
|
* a part of the base FHIR specification's list of summary elements. Examples of valid values
|
||||||
|
* include:
|
||||||
|
* <ul>
|
||||||
|
* <li><b>Patient.name</b> - Do not include the patient's name</li>
|
||||||
|
* <li><b>Patient.name.family</b> - Do not include the patient's family name</li>
|
||||||
|
* <li><b>*.name</b> - Do not include the name element on any resource type</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @see IParser#setSummaryMode(boolean)
|
||||||
|
* @see IParser#setDontEncodeElements(Collection) Can be used to specify these values for an individual parser instance.
|
||||||
|
* @since 7.4.0
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public ParserOptions setDontEncodeElementsForSummaryMode(@Nullable Collection<String> theDontEncodeElements) {
|
||||||
|
Set<String> dontEncodeElements = null;
|
||||||
|
if (theDontEncodeElements != null && !theDontEncodeElements.isEmpty()) {
|
||||||
|
dontEncodeElements = new HashSet<>(theDontEncodeElements);
|
||||||
|
}
|
||||||
|
myDontEncodeElementsForSummaryMode = dontEncodeElements;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the values provided to {@link #setDontEncodeElementsForSummaryMode(Collection)}
|
||||||
|
* or <code>null</code>
|
||||||
|
* @since 7.4.0
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Set<String> getDontEncodeElementsForSummaryMode() {
|
||||||
|
return myDontEncodeElementsForSummaryMode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
|
|||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
import ca.uhn.fhir.context.ParserOptions;
|
||||||
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
|
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeChildContainedResources;
|
import ca.uhn.fhir.context.RuntimeChildContainedResources;
|
||||||
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
|
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
|
||||||
@ -42,6 +43,7 @@ import ca.uhn.fhir.parser.path.EncodeContextPath;
|
|||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.util.BundleUtil;
|
import ca.uhn.fhir.util.BundleUtil;
|
||||||
|
import ca.uhn.fhir.util.CollectionUtil;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import ca.uhn.fhir.util.MetaUtil;
|
import ca.uhn.fhir.util.MetaUtil;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
@ -106,9 +108,8 @@ public abstract class BaseParser implements IParser {
|
|||||||
private FhirTerser.ContainedResources myContainedResources;
|
private FhirTerser.ContainedResources myContainedResources;
|
||||||
private boolean myEncodeElementsAppliesToChildResourcesOnly;
|
private boolean myEncodeElementsAppliesToChildResourcesOnly;
|
||||||
private final FhirContext myContext;
|
private final FhirContext myContext;
|
||||||
private List<EncodeContextPath> myDontEncodeElements;
|
private Collection<String> myDontEncodeElements;
|
||||||
private List<EncodeContextPath> myEncodeElements;
|
private Collection<String> myEncodeElements;
|
||||||
private Set<String> myEncodeElementsAppliesToResourceTypes;
|
|
||||||
private IIdType myEncodeForceResourceId;
|
private IIdType myEncodeForceResourceId;
|
||||||
private IParserErrorHandler myErrorHandler;
|
private IParserErrorHandler myErrorHandler;
|
||||||
private boolean myOmitResourceId;
|
private boolean myOmitResourceId;
|
||||||
@ -131,52 +132,15 @@ public abstract class BaseParser implements IParser {
|
|||||||
return myContext;
|
return myContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<EncodeContextPath> getDontEncodeElements() {
|
|
||||||
return myDontEncodeElements;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IParser setDontEncodeElements(Collection<String> theDontEncodeElements) {
|
public IParser setDontEncodeElements(Collection<String> theDontEncodeElements) {
|
||||||
if (theDontEncodeElements == null || theDontEncodeElements.isEmpty()) {
|
myDontEncodeElements = theDontEncodeElements;
|
||||||
myDontEncodeElements = null;
|
|
||||||
} else {
|
|
||||||
myDontEncodeElements =
|
|
||||||
theDontEncodeElements.stream().map(EncodeContextPath::new).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<EncodeContextPath> getEncodeElements() {
|
|
||||||
return myEncodeElements;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IParser setEncodeElements(Set<String> theEncodeElements) {
|
public IParser setEncodeElements(Set<String> theEncodeElements) {
|
||||||
|
myEncodeElements = theEncodeElements;
|
||||||
if (theEncodeElements == null || theEncodeElements.isEmpty()) {
|
|
||||||
myEncodeElements = null;
|
|
||||||
myEncodeElementsAppliesToResourceTypes = null;
|
|
||||||
} else {
|
|
||||||
myEncodeElements =
|
|
||||||
theEncodeElements.stream().map(EncodeContextPath::new).collect(Collectors.toList());
|
|
||||||
|
|
||||||
myEncodeElementsAppliesToResourceTypes = new HashSet<>();
|
|
||||||
for (String next : myEncodeElements.stream()
|
|
||||||
.map(t -> t.getPath().get(0).getName())
|
|
||||||
.collect(Collectors.toList())) {
|
|
||||||
if (next.startsWith("*")) {
|
|
||||||
myEncodeElementsAppliesToResourceTypes = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int dotIdx = next.indexOf('.');
|
|
||||||
if (dotIdx == -1) {
|
|
||||||
myEncodeElementsAppliesToResourceTypes.add(next);
|
|
||||||
} else {
|
|
||||||
myEncodeElementsAppliesToResourceTypes.add(next.substring(0, dotIdx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +262,7 @@ public abstract class BaseParser implements IParser {
|
|||||||
@Override
|
@Override
|
||||||
public final void encodeResourceToWriter(IBaseResource theResource, Writer theWriter)
|
public final void encodeResourceToWriter(IBaseResource theResource, Writer theWriter)
|
||||||
throws IOException, DataFormatException {
|
throws IOException, DataFormatException {
|
||||||
EncodeContext encodeContext = new EncodeContext();
|
EncodeContext encodeContext = new EncodeContext(this, myContext.getParserOptions());
|
||||||
encodeResourceToWriter(theResource, theWriter, encodeContext);
|
encodeResourceToWriter(theResource, theWriter, encodeContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +285,7 @@ public abstract class BaseParser implements IParser {
|
|||||||
} else if (theElement instanceof IPrimitiveType) {
|
} else if (theElement instanceof IPrimitiveType) {
|
||||||
theWriter.write(((IPrimitiveType<?>) theElement).getValueAsString());
|
theWriter.write(((IPrimitiveType<?>) theElement).getValueAsString());
|
||||||
} else {
|
} else {
|
||||||
EncodeContext encodeContext = new EncodeContext();
|
EncodeContext encodeContext = new EncodeContext(this, myContext.getParserOptions());
|
||||||
encodeToWriter(theElement, theWriter, encodeContext);
|
encodeToWriter(theElement, theWriter, encodeContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -628,7 +592,7 @@ public abstract class BaseParser implements IParser {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> dontStripVersionsFromReferencesAtPaths = myDontStripVersionsFromReferencesAtPaths;
|
Set<String> dontStripVersionsFromReferencesAtPaths = getDontStripVersionsFromReferencesAtPaths();
|
||||||
if (dontStripVersionsFromReferencesAtPaths != null
|
if (dontStripVersionsFromReferencesAtPaths != null
|
||||||
&& !dontStripVersionsFromReferencesAtPaths.isEmpty()
|
&& !dontStripVersionsFromReferencesAtPaths.isEmpty()
|
||||||
&& theCompositeChildElement.anyPathMatches(dontStripVersionsFromReferencesAtPaths)) {
|
&& theCompositeChildElement.anyPathMatches(dontStripVersionsFromReferencesAtPaths)) {
|
||||||
@ -923,7 +887,7 @@ public abstract class BaseParser implements IParser {
|
|||||||
if (isSuppressNarratives()) {
|
if (isSuppressNarratives()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (myEncodeElements != null) {
|
if (theEncodeContext.myEncodeElementPaths != null) {
|
||||||
if (isEncodeElementsAppliesToChildResourcesOnly()
|
if (isEncodeElementsAppliesToChildResourcesOnly()
|
||||||
&& theEncodeContext.getResourcePath().size() < 2) {
|
&& theEncodeContext.getResourcePath().size() < 2) {
|
||||||
return false;
|
return false;
|
||||||
@ -933,8 +897,8 @@ public abstract class BaseParser implements IParser {
|
|||||||
.getResourcePath()
|
.getResourcePath()
|
||||||
.get(theEncodeContext.getResourcePath().size() - 1)
|
.get(theEncodeContext.getResourcePath().size() - 1)
|
||||||
.getName();
|
.getName();
|
||||||
return myEncodeElementsAppliesToResourceTypes == null
|
return theEncodeContext.myEncodeElementsAppliesToResourceTypes == null
|
||||||
|| myEncodeElementsAppliesToResourceTypes.contains(currentResourceName);
|
|| theEncodeContext.myEncodeElementsAppliesToResourceTypes.contains(currentResourceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -945,14 +909,15 @@ public abstract class BaseParser implements IParser {
|
|||||||
if (isOmitResourceId() && theEncodeContext.getPath().size() == 1) {
|
if (isOmitResourceId() && theEncodeContext.getPath().size() == 1) {
|
||||||
retVal = false;
|
retVal = false;
|
||||||
} else {
|
} else {
|
||||||
if (myDontEncodeElements != null) {
|
if (theEncodeContext.myDontEncodeElementPaths != null) {
|
||||||
String resourceName = myContext.getResourceType(theResource);
|
String resourceName = myContext.getResourceType(theResource);
|
||||||
if (myDontEncodeElements.stream().anyMatch(t -> t.equalsPath(resourceName + ".id"))) {
|
if (theEncodeContext.myDontEncodeElementPaths.stream()
|
||||||
|
.anyMatch(t -> t.equalsPath(resourceName + ".id"))) {
|
||||||
retVal = false;
|
retVal = false;
|
||||||
} else if (myDontEncodeElements.stream().anyMatch(t -> t.equalsPath("*.id"))) {
|
} else if (theEncodeContext.myDontEncodeElementPaths.stream().anyMatch(t -> t.equalsPath("*.id"))) {
|
||||||
retVal = false;
|
retVal = false;
|
||||||
} else if (theEncodeContext.getResourcePath().size() == 1
|
} else if (theEncodeContext.getResourcePath().size() == 1
|
||||||
&& myDontEncodeElements.stream().anyMatch(t -> t.equalsPath("id"))) {
|
&& theEncodeContext.myDontEncodeElementPaths.stream().anyMatch(t -> t.equalsPath("id"))) {
|
||||||
retVal = false;
|
retVal = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -963,19 +928,22 @@ public abstract class BaseParser implements IParser {
|
|||||||
/**
|
/**
|
||||||
* Used for DSTU2 only
|
* Used for DSTU2 only
|
||||||
*/
|
*/
|
||||||
protected boolean shouldEncodeResourceMeta(IResource theResource) {
|
protected boolean shouldEncodeResourceMeta(IResource theResource, EncodeContext theEncodeContext) {
|
||||||
return shouldEncodePath(theResource, "meta");
|
return shouldEncodePath(theResource, "meta", theEncodeContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for DSTU2 only
|
* Used for DSTU2 only
|
||||||
*/
|
*/
|
||||||
protected boolean shouldEncodePath(IResource theResource, String thePath) {
|
protected boolean shouldEncodePath(IResource theResource, String thePath, EncodeContext theEncodeContext) {
|
||||||
if (myDontEncodeElements != null) {
|
if (theEncodeContext.myDontEncodeElementPaths != null) {
|
||||||
String resourceName = myContext.getResourceType(theResource);
|
String resourceName = myContext.getResourceType(theResource);
|
||||||
if (myDontEncodeElements.stream().anyMatch(t -> t.equalsPath(resourceName + "." + thePath))) {
|
if (theEncodeContext.myDontEncodeElementPaths.stream()
|
||||||
|
.anyMatch(t -> t.equalsPath(resourceName + "." + thePath))) {
|
||||||
return false;
|
return false;
|
||||||
} else return myDontEncodeElements.stream().noneMatch(t -> t.equalsPath("*." + thePath));
|
} else {
|
||||||
|
return theEncodeContext.myDontEncodeElementPaths.stream().noneMatch(t -> t.equalsPath("*." + thePath));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -994,16 +962,16 @@ public abstract class BaseParser implements IParser {
|
|||||||
b.append(" but this is not a valid type for this element");
|
b.append(" but this is not a valid type for this element");
|
||||||
if (nextChild instanceof RuntimeChildChoiceDefinition) {
|
if (nextChild instanceof RuntimeChildChoiceDefinition) {
|
||||||
RuntimeChildChoiceDefinition choice = (RuntimeChildChoiceDefinition) nextChild;
|
RuntimeChildChoiceDefinition choice = (RuntimeChildChoiceDefinition) nextChild;
|
||||||
b.append(" - Expected one of: " + choice.getValidChildTypes());
|
b.append(" - Expected one of: ").append(choice.getValidChildTypes());
|
||||||
}
|
}
|
||||||
throw new DataFormatException(Msg.code(1831) + b);
|
throw new DataFormatException(Msg.code(1831) + b);
|
||||||
}
|
}
|
||||||
throw new DataFormatException(Msg.code(1832) + nextChild + " has no child of type " + theType);
|
throw new DataFormatException(Msg.code(1832) + nextChild + " has no child of type " + theType);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean shouldEncodeResource(String theName) {
|
protected boolean shouldEncodeResource(String theName, EncodeContext theEncodeContext) {
|
||||||
if (myDontEncodeElements != null) {
|
if (theEncodeContext.myDontEncodeElementPaths != null) {
|
||||||
for (EncodeContextPath next : myDontEncodeElements) {
|
for (EncodeContextPath next : theEncodeContext.myDontEncodeElementPaths) {
|
||||||
if (next.equalsPath(theName)) {
|
if (next.equalsPath(theName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1012,11 +980,6 @@ public abstract class BaseParser implements IParser {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isFhirVersionLessThanOrEqualTo(FhirVersionEnum theFhirVersionEnum) {
|
|
||||||
final FhirVersionEnum apiFhirVersion = myContext.getVersion().getVersion();
|
|
||||||
return theFhirVersionEnum == apiFhirVersion || apiFhirVersion.isOlderThan(theFhirVersionEnum);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void containResourcesInReferences(IBaseResource theResource) {
|
protected void containResourcesInReferences(IBaseResource theResource) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1043,7 +1006,7 @@ public abstract class BaseParser implements IParser {
|
|||||||
myContainedResources = getContext().newTerser().containResources(theResource);
|
myContainedResources = getContext().newTerser().containResources(theResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChildNameAndDef {
|
static class ChildNameAndDef {
|
||||||
|
|
||||||
private final BaseRuntimeElementDefinition<?> myChildDef;
|
private final BaseRuntimeElementDefinition<?> myChildDef;
|
||||||
private final String myChildName;
|
private final String myChildName;
|
||||||
@ -1066,10 +1029,40 @@ public abstract class BaseParser implements IParser {
|
|||||||
* EncodeContext is a shared state object that is passed around the
|
* EncodeContext is a shared state object that is passed around the
|
||||||
* encode process
|
* encode process
|
||||||
*/
|
*/
|
||||||
public class EncodeContext extends EncodeContextPath {
|
class EncodeContext extends EncodeContextPath {
|
||||||
private final Map<Key, List<BaseParser.CompositeChildElement>> myCompositeChildrenCache = new HashMap<>();
|
private final Map<Key, List<BaseParser.CompositeChildElement>> myCompositeChildrenCache = new HashMap<>();
|
||||||
|
private final List<EncodeContextPath> myEncodeElementPaths;
|
||||||
|
private final Set<String> myEncodeElementsAppliesToResourceTypes;
|
||||||
|
private final List<EncodeContextPath> myDontEncodeElementPaths;
|
||||||
|
|
||||||
public Map<Key, List<BaseParser.CompositeChildElement>> getCompositeChildrenCache() {
|
public EncodeContext(BaseParser theParser, ParserOptions theParserOptions) {
|
||||||
|
Collection<String> encodeElements = theParser.myEncodeElements;
|
||||||
|
Collection<String> dontEncodeElements = theParser.myDontEncodeElements;
|
||||||
|
if (isSummaryMode()) {
|
||||||
|
encodeElements = CollectionUtil.nullSafeUnion(
|
||||||
|
encodeElements, theParserOptions.getEncodeElementsForSummaryMode());
|
||||||
|
dontEncodeElements = CollectionUtil.nullSafeUnion(
|
||||||
|
dontEncodeElements, theParserOptions.getDontEncodeElementsForSummaryMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encodeElements == null || encodeElements.isEmpty()) {
|
||||||
|
myEncodeElementPaths = null;
|
||||||
|
} else {
|
||||||
|
myEncodeElementPaths =
|
||||||
|
encodeElements.stream().map(EncodeContextPath::new).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
if (dontEncodeElements == null || dontEncodeElements.isEmpty()) {
|
||||||
|
myDontEncodeElementPaths = null;
|
||||||
|
} else {
|
||||||
|
myDontEncodeElementPaths =
|
||||||
|
dontEncodeElements.stream().map(EncodeContextPath::new).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
myEncodeElementsAppliesToResourceTypes =
|
||||||
|
ParserUtil.determineApplicableResourceTypesForTerserPaths(myEncodeElementPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Key, List<BaseParser.CompositeChildElement>> getCompositeChildrenCache() {
|
||||||
return myCompositeChildrenCache;
|
return myCompositeChildrenCache;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1161,14 +1154,14 @@ public abstract class BaseParser implements IParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfParentShouldBeEncodedAndBuildPath() {
|
private boolean checkIfParentShouldBeEncodedAndBuildPath() {
|
||||||
List<EncodeContextPath> encodeElements = myEncodeElements;
|
List<EncodeContextPath> encodeElements = myEncodeContext.myEncodeElementPaths;
|
||||||
|
|
||||||
String currentResourceName = myEncodeContext
|
String currentResourceName = myEncodeContext
|
||||||
.getResourcePath()
|
.getResourcePath()
|
||||||
.get(myEncodeContext.getResourcePath().size() - 1)
|
.get(myEncodeContext.getResourcePath().size() - 1)
|
||||||
.getName();
|
.getName();
|
||||||
if (myEncodeElementsAppliesToResourceTypes != null
|
if (myEncodeContext.myEncodeElementsAppliesToResourceTypes != null
|
||||||
&& !myEncodeElementsAppliesToResourceTypes.contains(currentResourceName)) {
|
&& !myEncodeContext.myEncodeElementsAppliesToResourceTypes.contains(currentResourceName)) {
|
||||||
encodeElements = null;
|
encodeElements = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1194,7 +1187,7 @@ public abstract class BaseParser implements IParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfParentShouldNotBeEncodedAndBuildPath() {
|
private boolean checkIfParentShouldNotBeEncodedAndBuildPath() {
|
||||||
return checkIfPathMatchesForEncoding(myDontEncodeElements, false);
|
return checkIfPathMatchesForEncoding(myEncodeContext.myDontEncodeElementPaths, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfPathMatchesForEncoding(
|
private boolean checkIfPathMatchesForEncoding(
|
||||||
@ -1256,16 +1249,7 @@ public abstract class BaseParser implements IParser {
|
|||||||
|
|
||||||
public boolean shouldBeEncoded(boolean theContainedResource) {
|
public boolean shouldBeEncoded(boolean theContainedResource) {
|
||||||
boolean retVal = true;
|
boolean retVal = true;
|
||||||
if (myEncodeElements != null) {
|
if (isSummaryMode() && (getDef() == null || !getDef().isSummary())) {
|
||||||
retVal = checkIfParentShouldBeEncodedAndBuildPath();
|
|
||||||
}
|
|
||||||
if (retVal && myDontEncodeElements != null) {
|
|
||||||
retVal = !checkIfParentShouldNotBeEncodedAndBuildPath();
|
|
||||||
}
|
|
||||||
if (theContainedResource) {
|
|
||||||
retVal = !notEncodeForContainedResource.contains(myDef.getElementName());
|
|
||||||
}
|
|
||||||
if (retVal && isSummaryMode() && (getDef() == null || !getDef().isSummary())) {
|
|
||||||
String resourceName = myEncodeContext.getLeafResourceName();
|
String resourceName = myEncodeContext.getLeafResourceName();
|
||||||
// Technically the spec says we shouldn't include extensions in CapabilityStatement
|
// Technically the spec says we shouldn't include extensions in CapabilityStatement
|
||||||
// but we will do so because there are people who depend on this behaviour, at least
|
// but we will do so because there are people who depend on this behaviour, at least
|
||||||
@ -1280,6 +1264,15 @@ public abstract class BaseParser implements IParser {
|
|||||||
retVal = false;
|
retVal = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (myEncodeContext.myEncodeElementPaths != null) {
|
||||||
|
retVal = checkIfParentShouldBeEncodedAndBuildPath();
|
||||||
|
}
|
||||||
|
if (retVal && myEncodeContext.myDontEncodeElementPaths != null) {
|
||||||
|
retVal = !checkIfParentShouldNotBeEncodedAndBuildPath();
|
||||||
|
}
|
||||||
|
if (theContainedResource) {
|
||||||
|
retVal = !notEncodeForContainedResource.contains(myDef.getElementName());
|
||||||
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,9 @@ import ca.uhn.fhir.context.FhirContext;
|
|||||||
import ca.uhn.fhir.context.ParserOptions;
|
import ca.uhn.fhir.context.ParserOptions;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.util.CollectionUtil;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
@ -106,6 +109,7 @@ public interface IParser {
|
|||||||
/**
|
/**
|
||||||
* When encoding, force this resource ID to be encoded as the resource ID
|
* When encoding, force this resource ID to be encoded as the resource ID
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("UnusedReturnValue")
|
||||||
IParser setEncodeForceResourceId(IIdType theForceResourceId);
|
IParser setEncodeForceResourceId(IIdType theForceResourceId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,7 +159,7 @@ public interface IParser {
|
|||||||
* ID will not have an ID.
|
* ID will not have an ID.
|
||||||
* <p>
|
* <p>
|
||||||
* If the resource being encoded is a Bundle or Parameters resource, this setting only applies to the
|
* If the resource being encoded is a Bundle or Parameters resource, this setting only applies to the
|
||||||
* outer resource being encoded, not any resources contained wihthin.
|
* outer resource being encoded, not any resources contained within.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param theOmitResourceId Should resource IDs be omitted
|
* @param theOmitResourceId Should resource IDs be omitted
|
||||||
@ -172,7 +176,7 @@ public interface IParser {
|
|||||||
* links. In that case, this value should be set to <code>false</code>.
|
* 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
|
* @return Returns the parser instance's configuration setting for stripping versions from resource references when
|
||||||
* encoding. This method will retun <code>null</code> if no value is set, in which case
|
* encoding. This method will return <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>)
|
* the value from the {@link ParserOptions} will be used (default is <code>true</code>)
|
||||||
* @see ParserOptions
|
* @see ParserOptions
|
||||||
*/
|
*/
|
||||||
@ -199,13 +203,29 @@ public interface IParser {
|
|||||||
/**
|
/**
|
||||||
* Is the parser in "summary mode"? See {@link #setSummaryMode(boolean)} for information
|
* Is the parser in "summary mode"? See {@link #setSummaryMode(boolean)} for information
|
||||||
*
|
*
|
||||||
* @see {@link #setSummaryMode(boolean)} for information
|
* @see #setSummaryMode(boolean) for information
|
||||||
*/
|
*/
|
||||||
boolean isSummaryMode();
|
boolean isSummaryMode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to <code>true</code> (default is <code>false</code>) only elements marked by the FHIR specification as
|
* If set to <code>true</code> (default is <code>false</code>) only elements marked by the FHIR specification as
|
||||||
* being "summary elements" will be included.
|
* being "summary elements" will be included.
|
||||||
|
* <p>
|
||||||
|
* It is possible to modify the default summary mode element inclusions
|
||||||
|
* for this parser instance by invoking {@link #setEncodeElements(Set)}
|
||||||
|
* or {@link #setDontEncodeElements(Collection)}. It is also possible to
|
||||||
|
* modify the default summary mode element inclusions for all parsers
|
||||||
|
* generated for a given {@link FhirContext} by accessing
|
||||||
|
* {@link FhirContext#getParserOptions()} followed by
|
||||||
|
* {@link ParserOptions#setEncodeElementsForSummaryMode(Collection)} and/or
|
||||||
|
* {@link ParserOptions#setDontEncodeElementsForSummaryMode(Collection)}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* For compatibility reasons with other frameworks, when encoding a
|
||||||
|
* <code>CapabilityStatement</code> resource in summary mode, extensions
|
||||||
|
* are always encoded, even though the FHIR Specification does not consider
|
||||||
|
* them to be summary elements.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
|
* @return Returns a reference to <code>this</code> parser so that method calls can be chained together
|
||||||
*/
|
*/
|
||||||
@ -287,16 +307,48 @@ public interface IParser {
|
|||||||
* wildcard)</li>
|
* wildcard)</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
|
* Note: If {@link #setSummaryMode(boolean)} is set to <code>true</code>, then any
|
||||||
|
* elements specified using this method will be excluded even if they are
|
||||||
|
* summary elements.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
* DSTU2 note: Note that values including meta, such as <code>Patient.meta</code>
|
* DSTU2 note: Note that values including meta, such as <code>Patient.meta</code>
|
||||||
* will work for DSTU2 parsers, but values with subelements on meta such
|
* will work for DSTU2 parsers, but values with sub-elements on meta such
|
||||||
* as <code>Patient.meta.lastUpdated</code> will only work in
|
* as <code>Patient.meta.lastUpdated</code> will only work in
|
||||||
* DSTU3+ mode.
|
* DSTU3+ mode.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param theDontEncodeElements The elements to encode
|
* @param theDontEncodeElements The elements to not encode, or <code>null</code>
|
||||||
* @see #setEncodeElements(Set)
|
* @see #setEncodeElements(Set)
|
||||||
|
* @see ParserOptions#setDontEncodeElementsForSummaryMode(Collection)
|
||||||
*/
|
*/
|
||||||
IParser setDontEncodeElements(Collection<String> theDontEncodeElements);
|
IParser setDontEncodeElements(@Nullable Collection<String> theDontEncodeElements);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If provided, specifies the elements which should NOT be encoded. Valid values for this
|
||||||
|
* field would include:
|
||||||
|
* <ul>
|
||||||
|
* <li><b>Patient</b> - Don't encode patient and all its children</li>
|
||||||
|
* <li><b>Patient.name</b> - Don't encode the patient's name</li>
|
||||||
|
* <li><b>Patient.name.family</b> - Don't encode the patient's family name</li>
|
||||||
|
* <li><b>*.text</b> - Don't encode the text element on any resource (only the very first position may contain a
|
||||||
|
* wildcard)</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* DSTU2 note: Note that values including meta, such as <code>Patient.meta</code>
|
||||||
|
* will work for DSTU2 parsers, but values with sub-elements on meta such
|
||||||
|
* as <code>Patient.meta.lastUpdated</code> will only work in
|
||||||
|
* DSTU3+ mode.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param theDontEncodeElements The elements to not encode. Can be an empty list, but must not be <code>null</code>.
|
||||||
|
* @see #setDontEncodeElements(Collection)
|
||||||
|
* @see ParserOptions#setDontEncodeElementsForSummaryMode(Collection)
|
||||||
|
* @since 7.4.0
|
||||||
|
*/
|
||||||
|
default IParser setDontEncodeElements(@Nonnull String... theDontEncodeElements) {
|
||||||
|
return setDontEncodeElements(CollectionUtil.newSet(theDontEncodeElements));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If provided, specifies the elements which should be encoded, to the exclusion of all others. Valid values for this
|
* If provided, specifies the elements which should be encoded, to the exclusion of all others. Valid values for this
|
||||||
@ -309,11 +361,44 @@ public interface IParser {
|
|||||||
* wildcard)</li>
|
* wildcard)</li>
|
||||||
* <li><b>*.(mandatory)</b> - This is a special case which causes any mandatory fields (min > 0) to be encoded</li>
|
* <li><b>*.(mandatory)</b> - This is a special case which causes any mandatory fields (min > 0) to be encoded</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* Note: If {@link #setSummaryMode(boolean)} is set to <code>true</code>, then any
|
||||||
|
* elements specified using this method will be included even if they are not
|
||||||
|
* summary elements.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param theEncodeElements The elements to encode
|
* @param theEncodeElements The elements to encode, or <code>null</code>
|
||||||
* @see #setDontEncodeElements(Collection)
|
* @see #setDontEncodeElements(Collection)
|
||||||
|
* @see #setEncodeElements(String...)
|
||||||
|
* @see ParserOptions#setEncodeElementsForSummaryMode(Collection)
|
||||||
*/
|
*/
|
||||||
IParser setEncodeElements(Set<String> theEncodeElements);
|
IParser setEncodeElements(@Nullable Set<String> theEncodeElements);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If provided, specifies the elements which should be encoded, to the exclusion of all others. Valid values for this
|
||||||
|
* field would include:
|
||||||
|
* <ul>
|
||||||
|
* <li><b>Patient</b> - Encode patient and all its children</li>
|
||||||
|
* <li><b>Patient.name</b> - Encode only the patient's name</li>
|
||||||
|
* <li><b>Patient.name.family</b> - Encode only the patient's family name</li>
|
||||||
|
* <li><b>*.text</b> - Encode the text element on any resource (only the very first position may contain a
|
||||||
|
* wildcard)</li>
|
||||||
|
* <li><b>*.(mandatory)</b> - This is a special case which causes any mandatory fields (min > 0) to be encoded</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* Note: If {@link #setSummaryMode(boolean)} is set to <code>true</code>, then any
|
||||||
|
* elements specified using this method will be included even if they are not
|
||||||
|
* summary elements.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param theEncodeElements The elements to encode. Can be an empty list, but must not be <code>null</code>.
|
||||||
|
* @since 7.4.0
|
||||||
|
* @see #setEncodeElements(Set)
|
||||||
|
* @see ParserOptions#setEncodeElementsForSummaryMode(String...)
|
||||||
|
*/
|
||||||
|
default IParser setEncodeElements(@Nonnull String... theEncodeElements) {
|
||||||
|
return setEncodeElements(CollectionUtil.newSet(theEncodeElements));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to <code>true</code> (default is false), the values supplied
|
* If set to <code>true</code> (default is false), the values supplied
|
||||||
@ -351,7 +436,7 @@ public interface IParser {
|
|||||||
* Sets the server's base URL used by this parser. If a value is set, resource references will be turned into
|
* Sets the server's base URL used by this parser. If a value is set, resource references will be turned into
|
||||||
* relative references if they are provided as absolute URLs but have a base matching the given base.
|
* relative references if they are provided as absolute URLs but have a base matching the given base.
|
||||||
*
|
*
|
||||||
* @param theUrl The base URL, e.g. "http://example.com/base"
|
* @param theUrl The base URL, e.g. "<a href="http://example.com/base">http://example.com/base</a>"
|
||||||
* @return Returns an instance of <code>this</code> parser so that method calls can be chained together
|
* @return Returns an instance of <code>this</code> parser so that method calls can be chained together
|
||||||
*/
|
*/
|
||||||
IParser setServerBaseUrl(String theUrl);
|
IParser setServerBaseUrl(String theUrl);
|
||||||
@ -378,7 +463,7 @@ public interface IParser {
|
|||||||
/**
|
/**
|
||||||
* Returns the value supplied to {@link IParser#setDontStripVersionsFromReferencesAtPaths(String...)}
|
* 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
|
* 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}
|
* the {@link ParserOptions} will be used).
|
||||||
*
|
*
|
||||||
* @see #setDontStripVersionsFromReferencesAtPaths(String...)
|
* @see #setDontStripVersionsFromReferencesAtPaths(String...)
|
||||||
* @see #setStripVersionsFromReferences(Boolean)
|
* @see #setStripVersionsFromReferences(Boolean)
|
||||||
|
@ -284,6 +284,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
switch (theChildDef.getChildType()) {
|
switch (theChildDef.getChildType()) {
|
||||||
|
case EXTENSION_DECLARED:
|
||||||
|
break;
|
||||||
case ID_DATATYPE: {
|
case ID_DATATYPE: {
|
||||||
IIdType value = (IIdType) theNextValue;
|
IIdType value = (IIdType) theNextValue;
|
||||||
String encodedValue = "id".equals(theChildName) ? value.getIdPart() : value.getValue();
|
String encodedValue = "id".equals(theChildName) ? value.getIdPart() : value.getValue();
|
||||||
@ -797,7 +799,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||||||
|
|
||||||
private boolean isSupportsFhirComment() {
|
private boolean isSupportsFhirComment() {
|
||||||
if (myIsSupportsFhirComment == null) {
|
if (myIsSupportsFhirComment == null) {
|
||||||
myIsSupportsFhirComment = isFhirVersionLessThanOrEqualTo(FhirVersionEnum.DSTU2_1);
|
myIsSupportsFhirComment = !getContext().getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2_1);
|
||||||
}
|
}
|
||||||
return myIsSupportsFhirComment;
|
return myIsSupportsFhirComment;
|
||||||
}
|
}
|
||||||
@ -836,7 +838,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||||||
+ theResource.getStructureFhirVersionEnum());
|
+ theResource.getStructureFhirVersionEnum());
|
||||||
}
|
}
|
||||||
|
|
||||||
EncodeContext encodeContext = new EncodeContext();
|
EncodeContext encodeContext = new EncodeContext(this, getContext().getParserOptions());
|
||||||
String resourceName = getContext().getResourceType(theResource);
|
String resourceName = getContext().getResourceType(theResource);
|
||||||
encodeContext.pushPath(resourceName, true);
|
encodeContext.pushPath(resourceName, true);
|
||||||
doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter, encodeContext);
|
doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter, encodeContext);
|
||||||
@ -887,7 +889,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||||||
EncodeContext theEncodeContext)
|
EncodeContext theEncodeContext)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
if (!super.shouldEncodeResource(theResDef.getName())) {
|
if (!super.shouldEncodeResource(theResDef.getName(), theEncodeContext)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -973,15 +975,15 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||||||
}
|
}
|
||||||
List<Map.Entry<ResourceMetadataKeyEnum<?>, Object>> extensionMetadataKeys = getExtensionMetadataKeys(resource);
|
List<Map.Entry<ResourceMetadataKeyEnum<?>, Object>> extensionMetadataKeys = getExtensionMetadataKeys(resource);
|
||||||
|
|
||||||
if (super.shouldEncodeResourceMeta(resource)
|
if (super.shouldEncodeResourceMeta(resource, theEncodeContext)
|
||||||
&& (ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false)
|
&& (ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false)
|
||||||
|| !extensionMetadataKeys.isEmpty()) {
|
|| !extensionMetadataKeys.isEmpty()) {
|
||||||
beginObject(theEventWriter, "meta");
|
beginObject(theEventWriter, "meta");
|
||||||
|
|
||||||
if (shouldEncodePath(resource, "meta.versionId")) {
|
if (shouldEncodePath(resource, "meta.versionId", theEncodeContext)) {
|
||||||
writeOptionalTagWithTextNode(theEventWriter, "versionId", versionIdPart);
|
writeOptionalTagWithTextNode(theEventWriter, "versionId", versionIdPart);
|
||||||
}
|
}
|
||||||
if (shouldEncodePath(resource, "meta.lastUpdated")) {
|
if (shouldEncodePath(resource, "meta.lastUpdated", theEncodeContext)) {
|
||||||
writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", updated);
|
writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.parser.path.EncodeContextPath;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ParserUtil {
|
||||||
|
|
||||||
|
/** Non instantiable */
|
||||||
|
private ParserUtil() {}
|
||||||
|
|
||||||
|
public static @Nullable Set<String> determineApplicableResourceTypesForTerserPaths(
|
||||||
|
@Nullable List<EncodeContextPath> encodeElements) {
|
||||||
|
Set<String> encodeElementsAppliesToResourceTypes = null;
|
||||||
|
if (encodeElements != null) {
|
||||||
|
encodeElementsAppliesToResourceTypes = new HashSet<>();
|
||||||
|
for (String next : encodeElements.stream()
|
||||||
|
.map(t -> t.getPath().get(0).getName())
|
||||||
|
.collect(Collectors.toList())) {
|
||||||
|
if (next.startsWith("*")) {
|
||||||
|
encodeElementsAppliesToResourceTypes = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int dotIdx = next.indexOf('.');
|
||||||
|
if (dotIdx == -1) {
|
||||||
|
encodeElementsAppliesToResourceTypes.add(next);
|
||||||
|
} else {
|
||||||
|
encodeElementsAppliesToResourceTypes.add(next.substring(0, dotIdx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return encodeElementsAppliesToResourceTypes;
|
||||||
|
}
|
||||||
|
}
|
@ -345,12 +345,12 @@ public class RDFParser extends BaseParser {
|
|||||||
final BaseRuntimeElementDefinition<?> childDef,
|
final BaseRuntimeElementDefinition<?> childDef,
|
||||||
final boolean includedResource,
|
final boolean includedResource,
|
||||||
final CompositeChildElement parent,
|
final CompositeChildElement parent,
|
||||||
final EncodeContext encodeContext,
|
final EncodeContext theEncodeContext,
|
||||||
final Integer cardinalityIndex) {
|
final Integer cardinalityIndex) {
|
||||||
|
|
||||||
String childGenericName = childDefinition.getElementName();
|
String childGenericName = childDefinition.getElementName();
|
||||||
|
|
||||||
encodeContext.pushPath(childGenericName, false);
|
theEncodeContext.pushPath(childGenericName, false);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (element == null || element.isEmpty()) {
|
if (element == null || element.isEmpty()) {
|
||||||
@ -412,8 +412,8 @@ public class RDFParser extends BaseParser {
|
|||||||
rdfModel,
|
rdfModel,
|
||||||
extensionResource,
|
extensionResource,
|
||||||
false,
|
false,
|
||||||
new CompositeChildElement(resDef, encodeContext),
|
new CompositeChildElement(resDef, theEncodeContext),
|
||||||
encodeContext);
|
theEncodeContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,7 +429,7 @@ public class RDFParser extends BaseParser {
|
|||||||
String idPredicate = null;
|
String idPredicate = null;
|
||||||
if (element instanceof IBaseResource) {
|
if (element instanceof IBaseResource) {
|
||||||
idPredicate = FHIR_NS + RESOURCE_ID;
|
idPredicate = FHIR_NS + RESOURCE_ID;
|
||||||
IIdType resourceId = processResourceID((IBaseResource) element, encodeContext);
|
IIdType resourceId = processResourceID((IBaseResource) element, theEncodeContext);
|
||||||
if (resourceId != null) {
|
if (resourceId != null) {
|
||||||
idString = resourceId.getIdPart();
|
idString = resourceId.getIdPart();
|
||||||
}
|
}
|
||||||
@ -444,7 +444,7 @@ public class RDFParser extends BaseParser {
|
|||||||
rdfModel.createProperty(idPredicate), createFhirValueBlankNode(rdfModel, idString));
|
rdfModel.createProperty(idPredicate), createFhirValueBlankNode(rdfModel, idString));
|
||||||
}
|
}
|
||||||
rdfModel = encodeCompositeElementToStreamWriter(
|
rdfModel = encodeCompositeElementToStreamWriter(
|
||||||
resource, element, rdfModel, rdfResource, includedResource, parent, encodeContext);
|
resource, element, rdfModel, rdfResource, includedResource, parent, theEncodeContext);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CONTAINED_RESOURCE_LIST:
|
case CONTAINED_RESOURCE_LIST:
|
||||||
@ -465,7 +465,7 @@ public class RDFParser extends BaseParser {
|
|||||||
rdfModel,
|
rdfModel,
|
||||||
true,
|
true,
|
||||||
super.fixContainedResourceId(resourceId.getValue()),
|
super.fixContainedResourceId(resourceId.getValue()),
|
||||||
encodeContext,
|
theEncodeContext,
|
||||||
false,
|
false,
|
||||||
containedResource);
|
containedResource);
|
||||||
}
|
}
|
||||||
@ -474,13 +474,14 @@ public class RDFParser extends BaseParser {
|
|||||||
case RESOURCE: {
|
case RESOURCE: {
|
||||||
IBaseResource baseResource = (IBaseResource) element;
|
IBaseResource baseResource = (IBaseResource) element;
|
||||||
String resourceName = getContext().getResourceType(baseResource);
|
String resourceName = getContext().getResourceType(baseResource);
|
||||||
if (!super.shouldEncodeResource(resourceName)) {
|
if (!super.shouldEncodeResource(resourceName, theEncodeContext)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
encodeContext.pushPath(resourceName, true);
|
theEncodeContext.pushPath(resourceName, true);
|
||||||
IIdType resourceId = processResourceID(resource, encodeContext);
|
IIdType resourceId = processResourceID(resource, theEncodeContext);
|
||||||
encodeResourceToRDFStreamWriter(resource, rdfModel, false, resourceId, encodeContext, false, null);
|
encodeResourceToRDFStreamWriter(
|
||||||
encodeContext.popPath();
|
resource, rdfModel, false, resourceId, theEncodeContext, false, null);
|
||||||
|
theEncodeContext.popPath();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PRIMITIVE_XHTML:
|
case PRIMITIVE_XHTML:
|
||||||
@ -502,7 +503,7 @@ public class RDFParser extends BaseParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
encodeContext.popPath();
|
theEncodeContext.popPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
return rdfModel;
|
return rdfModel;
|
||||||
|
@ -372,7 +372,7 @@ public class XmlParser extends BaseParser {
|
|||||||
case RESOURCE: {
|
case RESOURCE: {
|
||||||
IBaseResource resource = (IBaseResource) theElement;
|
IBaseResource resource = (IBaseResource) theElement;
|
||||||
String resourceName = getContext().getResourceType(resource);
|
String resourceName = getContext().getResourceType(resource);
|
||||||
if (!super.shouldEncodeResource(resourceName)) {
|
if (!super.shouldEncodeResource(resourceName, theEncodeContext)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
theEventWriter.writeStartElement(theChildName);
|
theEventWriter.writeStartElement(theChildName);
|
||||||
@ -736,14 +736,14 @@ public class XmlParser extends BaseParser {
|
|||||||
|
|
||||||
TagList tags = getMetaTagsForEncoding((resource), theEncodeContext);
|
TagList tags = getMetaTagsForEncoding((resource), theEncodeContext);
|
||||||
|
|
||||||
if (super.shouldEncodeResourceMeta(resource)
|
if (super.shouldEncodeResourceMeta(resource, theEncodeContext)
|
||||||
&& ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) {
|
&& ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) {
|
||||||
theEventWriter.writeStartElement("meta");
|
theEventWriter.writeStartElement("meta");
|
||||||
if (shouldEncodePath(resource, "meta.versionId")) {
|
if (shouldEncodePath(resource, "meta.versionId", theEncodeContext)) {
|
||||||
writeOptionalTagWithValue(theEventWriter, "versionId", versionIdPart);
|
writeOptionalTagWithValue(theEventWriter, "versionId", versionIdPart);
|
||||||
}
|
}
|
||||||
if (updated != null) {
|
if (updated != null) {
|
||||||
if (shouldEncodePath(resource, "meta.lastUpdated")) {
|
if (shouldEncodePath(resource, "meta.lastUpdated", theEncodeContext)) {
|
||||||
writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString());
|
writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,17 +19,76 @@
|
|||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.util;
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static java.util.Collections.unmodifiableCollection;
|
||||||
|
|
||||||
public class CollectionUtil {
|
public class CollectionUtil {
|
||||||
|
|
||||||
public static <T> Set<T> newSet(T... theValues) {
|
/**
|
||||||
HashSet<T> retVal = new HashSet<T>();
|
* Non instantiable
|
||||||
|
*/
|
||||||
|
private CollectionUtil() {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
for (T t : theValues) {
|
/**
|
||||||
retVal.add(t);
|
* Returns an immutable union of both collections. If either or both arguments are
|
||||||
|
* <code>null</code> they will be treated as an empty collection, meaning
|
||||||
|
* that even if both arguments are <code>null</code>, an empty immutable
|
||||||
|
* collection will be returned.
|
||||||
|
* <p>
|
||||||
|
* DO NOT use this method if the underlying collections can be changed
|
||||||
|
* after calling this method, as the behaviour is indeterminate.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param theCollection0 The first set in the union, or <code>null</code>.
|
||||||
|
* @param theCollection1 The second set in the union, or <code>null</code>.
|
||||||
|
* @return Returns a union of both collections. Will not return <code>null</code> ever.
|
||||||
|
* @since 7.4.0
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static <T> Collection<T> nullSafeUnion(
|
||||||
|
@Nullable Collection<T> theCollection0, @Nullable Collection<T> theCollection1) {
|
||||||
|
Collection<T> collection0 = theCollection0;
|
||||||
|
if (collection0 != null && collection0.isEmpty()) {
|
||||||
|
collection0 = null;
|
||||||
}
|
}
|
||||||
return retVal;
|
Collection<T> collection1 = theCollection1;
|
||||||
|
if (collection1 != null && collection1.isEmpty()) {
|
||||||
|
collection1 = null;
|
||||||
|
}
|
||||||
|
if (collection0 == null && collection1 == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
if (collection0 == null) {
|
||||||
|
return unmodifiableCollection(collection1);
|
||||||
|
}
|
||||||
|
if (collection1 == null) {
|
||||||
|
return unmodifiableCollection(collection0);
|
||||||
|
}
|
||||||
|
return CollectionUtils.union(collection0, collection1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is equivalent to <code>Set.of(...)</code> but is kept here
|
||||||
|
* and used instead of that method because Set.of is not present on Android
|
||||||
|
* SDKs (at least up to 29).
|
||||||
|
* <p>
|
||||||
|
* Sets returned by this method are unmodifiable.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> Set<T> newSet(T... theValues) {
|
||||||
|
HashSet<T> retVal = new HashSet<>();
|
||||||
|
Collections.addAll(retVal, theValues);
|
||||||
|
return Collections.unmodifiableSet(retVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,8 +90,8 @@ import java.util.Set;
|
|||||||
public class SearchParameter extends BaseQueryParameter {
|
public class SearchParameter extends BaseQueryParameter {
|
||||||
|
|
||||||
private static final String EMPTY_STRING = "";
|
private static final String EMPTY_STRING = "";
|
||||||
private static HashMap<RestSearchParameterTypeEnum, Set<String>> ourParamQualifiers;
|
private static final HashMap<RestSearchParameterTypeEnum, Set<String>> ourParamQualifiers;
|
||||||
private static HashMap<Class<?>, RestSearchParameterTypeEnum> ourParamTypes;
|
private static final HashMap<Class<?>, RestSearchParameterTypeEnum> ourParamTypes;
|
||||||
static final String QUALIFIER_ANY_TYPE = ":*";
|
static final String QUALIFIER_ANY_TYPE = ":*";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -20,15 +20,18 @@
|
|||||||
package ca.uhn.hapi.fhir.docs;
|
package ca.uhn.hapi.fhir.docs;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.ParserOptions;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class Parser {
|
public class Parser {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public static void main(String[] args) throws DataFormatException, IOException {
|
public static void main(String[] args) throws DataFormatException, IOException {
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -117,6 +120,36 @@ public class Parser {
|
|||||||
System.out.println(serialized);
|
System.out.println(serialized);
|
||||||
// END SNIPPET: encodingConfig
|
// END SNIPPET: encodingConfig
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// Create a FHIR context
|
||||||
|
FhirContext ctx = FhirContext.forR4();
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addName().setFamily("Simpson").addGiven("James");
|
||||||
|
|
||||||
|
// START SNIPPET: encodingSummary
|
||||||
|
// Create a parser
|
||||||
|
IParser parser = ctx.newJsonParser();
|
||||||
|
|
||||||
|
// Instruct the parser to only include summary elements
|
||||||
|
parser.setSummaryMode(true);
|
||||||
|
|
||||||
|
// If you need to, you can instruct the parser to override
|
||||||
|
// the default summary elements by adding and/or removing
|
||||||
|
// elements from the list of elements it will include. This
|
||||||
|
// is typically not needed, but it's shown here in case you
|
||||||
|
// need to do this:
|
||||||
|
// Include a non-summary element in the summary view.
|
||||||
|
parser.setEncodeElements("Patient.maritalStatus");
|
||||||
|
// Exclude a summary element even though it would normally
|
||||||
|
// be included.
|
||||||
|
parser.setDontEncodeElements("Patient.name");
|
||||||
|
|
||||||
|
// Serialize it
|
||||||
|
String serialized = parser.encodeResourceToString(patient);
|
||||||
|
System.out.println(serialized);
|
||||||
|
// END SNIPPET: encodingSummary
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// START SNIPPET: disableStripVersions
|
// START SNIPPET: disableStripVersions
|
||||||
FhirContext ctx = FhirContext.forR4();
|
FhirContext ctx = FhirContext.forR4();
|
||||||
@ -148,5 +181,37 @@ public class Parser {
|
|||||||
// END SNIPPET: disableStripVersionsField
|
// END SNIPPET: disableStripVersionsField
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
IBaseResource patient = new Patient();
|
||||||
|
|
||||||
|
// START SNIPPET: globalParserConfig
|
||||||
|
FhirContext ctx = FhirContext.forR4();
|
||||||
|
|
||||||
|
// Request the ParserOptions, which store global config
|
||||||
|
// settings applied to all parsers coming from the given
|
||||||
|
// context.
|
||||||
|
ParserOptions parserOptions = ctx.getParserOptions();
|
||||||
|
|
||||||
|
// Never strip resource reference versions for the following
|
||||||
|
// paths
|
||||||
|
parserOptions.setDontStripVersionsFromReferencesAtPaths(
|
||||||
|
"AuditEvent.entity.reference", "Patient.managingOrganization");
|
||||||
|
|
||||||
|
// Never strip any resource reference versions (setting this
|
||||||
|
// to false would make the setting above redundant since this
|
||||||
|
// setting applies to all paths)
|
||||||
|
parserOptions.setStripVersionsFromReferences(false);
|
||||||
|
|
||||||
|
// Even in summary mode, always include extensions on the
|
||||||
|
// root of Patient resources.
|
||||||
|
parserOptions.setEncodeElementsForSummaryMode("Patient.extension");
|
||||||
|
|
||||||
|
// Create a parser and encode, with the global config applied.
|
||||||
|
IParser parser = ctx.newJsonParser();
|
||||||
|
String encoded = parser.encodeResourceToString(patient);
|
||||||
|
// END SNIPPET: globalParserConfig
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 5871
|
||||||
|
title: "When encoding resources in summary mode, it is now possible to override the
|
||||||
|
built-in list of summary elements, by adding additional elements and/or by
|
||||||
|
removing elements from the default list. This can be done for an individual parser
|
||||||
|
instance, or globally using the ParserOptions object available from the FhirContext."
|
@ -26,16 +26,39 @@ The following example shows a JSON Parser being used to serialize a FHIR resourc
|
|||||||
|
|
||||||
By default, the parser will output in condensed form, with no newlines or indenting. This is good for machine-to-machine communication since it reduces the amount of data to be transferred but it is harder to read. To enable pretty printed output:
|
By default, the parser will output in condensed form, with no newlines or indenting. This is good for machine-to-machine communication since it reduces the amount of data to be transferred but it is harder to read. To enable pretty printed output:
|
||||||
|
|
||||||
|
When using the [HAPI FHIR Server](../server_plain/), pretty printing can be requested by adding the parameter <code>_pretty=true</code> to the request.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|encodingPretty}}
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|encodingPretty}}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Encoding Configuration
|
## Encoding Configuration
|
||||||
|
|
||||||
There are plenty of other options too that can be used to control the output by the parser. A few examples are shown below. See the [IParser](/apidocs/hapi-fhir-base/ca/uhn/fhir/parser/IParser.html) JavaDoc for more information.
|
There are plenty of other options too, that can be used to control the output by the parser. A few examples are shown below. See the [IParser](/apidocs/hapi-fhir-base/ca/uhn/fhir/parser/IParser.html) JavaDoc for more information.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|encodingConfig}}
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|encodingConfig}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Summary Mode
|
||||||
|
|
||||||
|
For each resource type, the FHIR specification defines a collection of elements which are considered "summary elements". These are marked on the individual resource views using a Sigma (Σ) symbol next to the element names. See the [Patient Resource Definition](https://hl7.org/fhir/patient.html) for an example, looking for
|
||||||
|
this symbol on the page.
|
||||||
|
|
||||||
|
If the parser is configured as shown below, only the summary mode elements will be included in the encoded resource.
|
||||||
|
|
||||||
|
When using the [HAPI FHIR Server](../server_plain/), summary mode can be requested by adding the parameter <code>_summary=true</code> to the request.
|
||||||
|
|
||||||
|
```java
|
||||||
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|encodingSummary}}
|
||||||
|
```
|
||||||
|
|
||||||
|
<a name="parser-options"/>
|
||||||
|
|
||||||
|
# Global Parser Configuration
|
||||||
|
|
||||||
|
It is possible to configure a number of parser settings globally for a given FhirContext, meaning that they will apply to all parsers that are created by that context. This is especially useful for [HAPI FHIR Clients](../client/) and [HAPI FHIR Servers](../server_plain/), where parsers are created by the client/server internally using the given FhirContext.
|
||||||
|
|
||||||
|
```java
|
||||||
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/Parser.java|globalParserConfig}}
|
||||||
|
```
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.util.CollectionUtil.nullSafeUnion;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
|
||||||
|
class CollectionUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNullSafeUnion() {
|
||||||
|
assertThat(nullSafeUnion(null, null), empty());
|
||||||
|
assertThat(nullSafeUnion(Set.of(), Set.of()), empty());
|
||||||
|
assertThat(nullSafeUnion(Set.of("A"), null), containsInAnyOrder("A"));
|
||||||
|
assertThat(nullSafeUnion(Set.of("A"), Set.of()), containsInAnyOrder("A"));
|
||||||
|
assertThat(nullSafeUnion(null, Set.of("B")), containsInAnyOrder("B"));
|
||||||
|
assertThat(nullSafeUnion(Set.of(), Set.of("B")), containsInAnyOrder("B"));
|
||||||
|
assertThat(nullSafeUnion(Set.of("A"), Set.of("B")), containsInAnyOrder("A", "B"));
|
||||||
|
assertThat(nullSafeUnion(List.of("A"), Set.of("B")), containsInAnyOrder("A", "B"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -74,7 +74,6 @@ import ca.uhn.fhir.rest.param.binder.QueryParameterTypeBinder;
|
|||||||
import ca.uhn.fhir.rest.param.binder.StringBinder;
|
import ca.uhn.fhir.rest.param.binder.StringBinder;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.util.CollectionUtil;
|
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
@ -105,30 +104,27 @@ public class SearchParameter extends BaseQueryParameter {
|
|||||||
ourParamTypes.put(StringParam.class, RestSearchParameterTypeEnum.STRING);
|
ourParamTypes.put(StringParam.class, RestSearchParameterTypeEnum.STRING);
|
||||||
ourParamTypes.put(StringOrListParam.class, RestSearchParameterTypeEnum.STRING);
|
ourParamTypes.put(StringOrListParam.class, RestSearchParameterTypeEnum.STRING);
|
||||||
ourParamTypes.put(StringAndListParam.class, RestSearchParameterTypeEnum.STRING);
|
ourParamTypes.put(StringAndListParam.class, RestSearchParameterTypeEnum.STRING);
|
||||||
ourParamQualifiers.put(
|
ourParamQualifiers.put(RestSearchParameterTypeEnum.STRING, Set.of(new String[] {
|
||||||
RestSearchParameterTypeEnum.STRING,
|
Constants.PARAMQUALIFIER_STRING_EXACT,
|
||||||
CollectionUtil.newSet(
|
Constants.PARAMQUALIFIER_STRING_CONTAINS,
|
||||||
Constants.PARAMQUALIFIER_STRING_EXACT,
|
Constants.PARAMQUALIFIER_MISSING,
|
||||||
Constants.PARAMQUALIFIER_STRING_CONTAINS,
|
EMPTY_STRING
|
||||||
Constants.PARAMQUALIFIER_MISSING,
|
}));
|
||||||
EMPTY_STRING));
|
|
||||||
|
|
||||||
ourParamTypes.put(UriParam.class, RestSearchParameterTypeEnum.URI);
|
ourParamTypes.put(UriParam.class, RestSearchParameterTypeEnum.URI);
|
||||||
ourParamTypes.put(UriOrListParam.class, RestSearchParameterTypeEnum.URI);
|
ourParamTypes.put(UriOrListParam.class, RestSearchParameterTypeEnum.URI);
|
||||||
ourParamTypes.put(UriAndListParam.class, RestSearchParameterTypeEnum.URI);
|
ourParamTypes.put(UriAndListParam.class, RestSearchParameterTypeEnum.URI);
|
||||||
// TODO: are these right for URI?
|
// TODO: are these right for URI?
|
||||||
ourParamQualifiers.put(
|
ourParamQualifiers.put(RestSearchParameterTypeEnum.URI, Set.of(new String[] {
|
||||||
RestSearchParameterTypeEnum.URI,
|
Constants.PARAMQUALIFIER_STRING_EXACT, Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING
|
||||||
CollectionUtil.newSet(
|
}));
|
||||||
Constants.PARAMQUALIFIER_STRING_EXACT, Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING));
|
|
||||||
|
|
||||||
ourParamTypes.put(TokenParam.class, RestSearchParameterTypeEnum.TOKEN);
|
ourParamTypes.put(TokenParam.class, RestSearchParameterTypeEnum.TOKEN);
|
||||||
ourParamTypes.put(TokenOrListParam.class, RestSearchParameterTypeEnum.TOKEN);
|
ourParamTypes.put(TokenOrListParam.class, RestSearchParameterTypeEnum.TOKEN);
|
||||||
ourParamTypes.put(TokenAndListParam.class, RestSearchParameterTypeEnum.TOKEN);
|
ourParamTypes.put(TokenAndListParam.class, RestSearchParameterTypeEnum.TOKEN);
|
||||||
ourParamQualifiers.put(
|
ourParamQualifiers.put(RestSearchParameterTypeEnum.TOKEN, Set.of(new String[] {
|
||||||
RestSearchParameterTypeEnum.TOKEN,
|
Constants.PARAMQUALIFIER_TOKEN_TEXT, Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING
|
||||||
CollectionUtil.newSet(
|
}));
|
||||||
Constants.PARAMQUALIFIER_TOKEN_TEXT, Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING));
|
|
||||||
|
|
||||||
ourParamTypes.put(DateParam.class, RestSearchParameterTypeEnum.DATE);
|
ourParamTypes.put(DateParam.class, RestSearchParameterTypeEnum.DATE);
|
||||||
ourParamTypes.put(DateOrListParam.class, RestSearchParameterTypeEnum.DATE);
|
ourParamTypes.put(DateOrListParam.class, RestSearchParameterTypeEnum.DATE);
|
||||||
@ -136,35 +132,35 @@ public class SearchParameter extends BaseQueryParameter {
|
|||||||
ourParamTypes.put(DateRangeParam.class, RestSearchParameterTypeEnum.DATE);
|
ourParamTypes.put(DateRangeParam.class, RestSearchParameterTypeEnum.DATE);
|
||||||
ourParamQualifiers.put(
|
ourParamQualifiers.put(
|
||||||
RestSearchParameterTypeEnum.DATE,
|
RestSearchParameterTypeEnum.DATE,
|
||||||
CollectionUtil.newSet(Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING));
|
Set.of(new String[] {Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING}));
|
||||||
|
|
||||||
ourParamTypes.put(QuantityParam.class, RestSearchParameterTypeEnum.QUANTITY);
|
ourParamTypes.put(QuantityParam.class, RestSearchParameterTypeEnum.QUANTITY);
|
||||||
ourParamTypes.put(QuantityOrListParam.class, RestSearchParameterTypeEnum.QUANTITY);
|
ourParamTypes.put(QuantityOrListParam.class, RestSearchParameterTypeEnum.QUANTITY);
|
||||||
ourParamTypes.put(QuantityAndListParam.class, RestSearchParameterTypeEnum.QUANTITY);
|
ourParamTypes.put(QuantityAndListParam.class, RestSearchParameterTypeEnum.QUANTITY);
|
||||||
ourParamQualifiers.put(
|
ourParamQualifiers.put(
|
||||||
RestSearchParameterTypeEnum.QUANTITY,
|
RestSearchParameterTypeEnum.QUANTITY,
|
||||||
CollectionUtil.newSet(Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING));
|
Set.of(new String[] {Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING}));
|
||||||
|
|
||||||
ourParamTypes.put(NumberParam.class, RestSearchParameterTypeEnum.NUMBER);
|
ourParamTypes.put(NumberParam.class, RestSearchParameterTypeEnum.NUMBER);
|
||||||
ourParamTypes.put(NumberOrListParam.class, RestSearchParameterTypeEnum.NUMBER);
|
ourParamTypes.put(NumberOrListParam.class, RestSearchParameterTypeEnum.NUMBER);
|
||||||
ourParamTypes.put(NumberAndListParam.class, RestSearchParameterTypeEnum.NUMBER);
|
ourParamTypes.put(NumberAndListParam.class, RestSearchParameterTypeEnum.NUMBER);
|
||||||
ourParamQualifiers.put(
|
ourParamQualifiers.put(
|
||||||
RestSearchParameterTypeEnum.NUMBER,
|
RestSearchParameterTypeEnum.NUMBER,
|
||||||
CollectionUtil.newSet(Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING));
|
Set.of(new String[] {Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING}));
|
||||||
|
|
||||||
ourParamTypes.put(ReferenceParam.class, RestSearchParameterTypeEnum.REFERENCE);
|
ourParamTypes.put(ReferenceParam.class, RestSearchParameterTypeEnum.REFERENCE);
|
||||||
ourParamTypes.put(ReferenceOrListParam.class, RestSearchParameterTypeEnum.REFERENCE);
|
ourParamTypes.put(ReferenceOrListParam.class, RestSearchParameterTypeEnum.REFERENCE);
|
||||||
ourParamTypes.put(ReferenceAndListParam.class, RestSearchParameterTypeEnum.REFERENCE);
|
ourParamTypes.put(ReferenceAndListParam.class, RestSearchParameterTypeEnum.REFERENCE);
|
||||||
// --vvvv-- no empty because that gets added from OptionalParam#chainWhitelist
|
// --vvvv-- no empty because that gets added from OptionalParam#chainWhitelist
|
||||||
ourParamQualifiers.put(
|
ourParamQualifiers.put(
|
||||||
RestSearchParameterTypeEnum.REFERENCE, CollectionUtil.newSet(Constants.PARAMQUALIFIER_MISSING));
|
RestSearchParameterTypeEnum.REFERENCE, Set.of(new String[] {Constants.PARAMQUALIFIER_MISSING}));
|
||||||
|
|
||||||
ourParamTypes.put(CompositeParam.class, RestSearchParameterTypeEnum.COMPOSITE);
|
ourParamTypes.put(CompositeParam.class, RestSearchParameterTypeEnum.COMPOSITE);
|
||||||
ourParamTypes.put(CompositeOrListParam.class, RestSearchParameterTypeEnum.COMPOSITE);
|
ourParamTypes.put(CompositeOrListParam.class, RestSearchParameterTypeEnum.COMPOSITE);
|
||||||
ourParamTypes.put(CompositeAndListParam.class, RestSearchParameterTypeEnum.COMPOSITE);
|
ourParamTypes.put(CompositeAndListParam.class, RestSearchParameterTypeEnum.COMPOSITE);
|
||||||
ourParamQualifiers.put(
|
ourParamQualifiers.put(
|
||||||
RestSearchParameterTypeEnum.COMPOSITE,
|
RestSearchParameterTypeEnum.COMPOSITE,
|
||||||
CollectionUtil.newSet(Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING));
|
Set.of(new String[] {Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING}));
|
||||||
|
|
||||||
ourParamTypes.put(HasParam.class, RestSearchParameterTypeEnum.HAS);
|
ourParamTypes.put(HasParam.class, RestSearchParameterTypeEnum.HAS);
|
||||||
ourParamTypes.put(HasOrListParam.class, RestSearchParameterTypeEnum.HAS);
|
ourParamTypes.put(HasOrListParam.class, RestSearchParameterTypeEnum.HAS);
|
||||||
@ -174,7 +170,7 @@ public class SearchParameter extends BaseQueryParameter {
|
|||||||
ourParamTypes.put(SpecialOrListParam.class, RestSearchParameterTypeEnum.SPECIAL);
|
ourParamTypes.put(SpecialOrListParam.class, RestSearchParameterTypeEnum.SPECIAL);
|
||||||
ourParamTypes.put(SpecialAndListParam.class, RestSearchParameterTypeEnum.SPECIAL);
|
ourParamTypes.put(SpecialAndListParam.class, RestSearchParameterTypeEnum.SPECIAL);
|
||||||
ourParamQualifiers.put(
|
ourParamQualifiers.put(
|
||||||
RestSearchParameterTypeEnum.SPECIAL, CollectionUtil.newSet(Constants.PARAMQUALIFIER_MISSING));
|
RestSearchParameterTypeEnum.SPECIAL, Set.of(new String[] {Constants.PARAMQUALIFIER_MISSING}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Class<? extends IQueryParameterType>> myCompositeTypes = Collections.emptyList();
|
private List<Class<? extends IQueryParameterType>> myCompositeTypes = Collections.emptyList();
|
||||||
|
@ -1160,99 +1160,6 @@ public class JsonParserDstu3Test {
|
|||||||
assertThat(enc, containsString("\"valueId\": \"1\""));
|
assertThat(enc, containsString("\"valueId\": \"1\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeSummary() {
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.setId("Patient/1/_history/1");
|
|
||||||
patient.getText().setDivAsString("<div>THE DIV</div>");
|
|
||||||
patient.addName().setFamily("FAMILY");
|
|
||||||
patient.addPhoto().setTitle("green");
|
|
||||||
patient.getMaritalStatus().addCoding().setCode("D");
|
|
||||||
|
|
||||||
ourLog.debug(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
|
||||||
|
|
||||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).setSummaryMode(true).encodeResourceToString(patient);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
assertThat(encoded, containsString("Patient"));
|
|
||||||
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\",", "\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
|
|
||||||
assertThat(encoded, not(containsString("THE DIV")));
|
|
||||||
assertThat(encoded, containsString("family"));
|
|
||||||
assertThat(encoded, not(containsString("maritalStatus")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We specifically include extensions on CapabilityStatment even in
|
|
||||||
* summary mode, since this is behaviour that people depend on
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testEncodeSummaryCapabilityStatementExtensions() {
|
|
||||||
|
|
||||||
CapabilityStatement cs = new CapabilityStatement();
|
|
||||||
CapabilityStatement.CapabilityStatementRestComponent rest = cs.addRest();
|
|
||||||
rest.setMode(CapabilityStatement.RestfulCapabilityMode.CLIENT);
|
|
||||||
rest.getSecurity()
|
|
||||||
.addExtension()
|
|
||||||
.setUrl("http://foo")
|
|
||||||
.setValue(new StringType("bar"));
|
|
||||||
|
|
||||||
cs.getVersionElement().addExtension()
|
|
||||||
.setUrl("http://goo")
|
|
||||||
.setValue(new StringType("ber"));
|
|
||||||
|
|
||||||
String encoded = ourCtx.newJsonParser().setSummaryMode(true).setPrettyPrint(true).setPrettyPrint(true).encodeResourceToString(cs);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
assertThat(encoded, (containsString("http://foo")));
|
|
||||||
assertThat(encoded, (containsString("bar")));
|
|
||||||
assertThat(encoded, (containsString("http://goo")));
|
|
||||||
assertThat(encoded, (containsString("ber")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeSummaryPatientExtensions() {
|
|
||||||
|
|
||||||
Patient cs = new Patient();
|
|
||||||
Address address = cs.addAddress();
|
|
||||||
address.setCity("CITY");
|
|
||||||
address
|
|
||||||
.addExtension()
|
|
||||||
.setUrl("http://foo")
|
|
||||||
.setValue(new StringType("bar"));
|
|
||||||
address.getCityElement().addExtension()
|
|
||||||
.setUrl("http://goo")
|
|
||||||
.setValue(new StringType("ber"));
|
|
||||||
|
|
||||||
String encoded = ourCtx.newJsonParser().setSummaryMode(true).setPrettyPrint(true).setPrettyPrint(true).encodeResourceToString(cs);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
assertThat(encoded, not(containsString("http://foo")));
|
|
||||||
assertThat(encoded, not(containsString("bar")));
|
|
||||||
assertThat(encoded, not(containsString("http://goo")));
|
|
||||||
assertThat(encoded, not(containsString("ber")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeSummary2() {
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.setId("Patient/1/_history/1");
|
|
||||||
patient.getText().setDivAsString("<div>THE DIV</div>");
|
|
||||||
patient.addName().setFamily("FAMILY");
|
|
||||||
patient.getMaritalStatus().addCoding().setCode("D");
|
|
||||||
|
|
||||||
patient.getMeta().addTag().setSystem("foo").setCode("bar");
|
|
||||||
|
|
||||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).setSummaryMode(true).encodeResourceToString(patient);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
assertThat(encoded, containsString("Patient"));
|
|
||||||
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"foo\",", "\"code\": \"bar\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"",
|
|
||||||
"\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
|
|
||||||
assertThat(encoded, not(containsString("THE DIV")));
|
|
||||||
assertThat(encoded, containsString("family"));
|
|
||||||
assertThat(encoded, not(containsString("maritalStatus")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #205
|
* See #205
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,269 @@
|
|||||||
|
package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import org.hl7.fhir.r5.model.Address;
|
||||||
|
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||||
|
import org.hl7.fhir.r5.model.Patient;
|
||||||
|
import org.hl7.fhir.r5.model.StringType;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||||
|
|
||||||
|
public class JsonParserSummaryModeR5Test {
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(JsonParserSummaryModeR5Test.class);
|
||||||
|
|
||||||
|
private static final FhirContext ourCtx = FhirContext.forR5Cached();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeSummary() {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("Patient/1/_history/1");
|
||||||
|
patient.getText().setDivAsString("<div>THE DIV</div>");
|
||||||
|
patient.addName().setFamily("FAMILY");
|
||||||
|
patient.addPhoto().setTitle("green");
|
||||||
|
patient.getMaritalStatus().addCoding().setCode("D");
|
||||||
|
|
||||||
|
ourLog.debug(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
||||||
|
|
||||||
|
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).setSummaryMode(true).encodeResourceToString(patient);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
assertThat(encoded, containsString("Patient"));
|
||||||
|
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM_R4 + "\",", "\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
|
||||||
|
assertThat(encoded, not(containsString("THE DIV")));
|
||||||
|
assertThat(encoded, containsString("family"));
|
||||||
|
assertThat(encoded, not(containsString("maritalStatus")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeSummary2() {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("Patient/1/_history/1");
|
||||||
|
patient.getText().setDivAsString("<div>THE DIV</div>");
|
||||||
|
patient.addName().setFamily("FAMILY");
|
||||||
|
patient.getMaritalStatus().addCoding().setCode("D");
|
||||||
|
|
||||||
|
patient.getMeta().addTag().setSystem("foo").setCode("bar");
|
||||||
|
|
||||||
|
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).setSummaryMode(true).encodeResourceToString(patient);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
assertThat(encoded, containsString("Patient"));
|
||||||
|
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"foo\",", "\"code\": \"bar\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_R4 + "\"",
|
||||||
|
"\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
|
||||||
|
assertThat(encoded, not(containsString("THE DIV")));
|
||||||
|
assertThat(encoded, containsString("family"));
|
||||||
|
assertThat(encoded, not(containsString("maritalStatus")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We specifically include extensions on CapabilityStatment even in
|
||||||
|
* summary mode, since this is behaviour that people depend on
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEncodeSummaryCapabilityStatementExtensions() {
|
||||||
|
CapabilityStatement cs = createCapabilityStatementWithExtensions();
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newJsonParser();
|
||||||
|
parser.setSummaryMode(true);
|
||||||
|
parser.setPrettyPrint(true);
|
||||||
|
parser.setPrettyPrint(true);
|
||||||
|
String encoded = parser.encodeResourceToString(cs);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
assertThat(encoded, (containsString("\"rest\"")));
|
||||||
|
assertThat(encoded, (containsString("http://foo")));
|
||||||
|
assertThat(encoded, (containsString("bar")));
|
||||||
|
assertThat(encoded, (containsString("http://goo")));
|
||||||
|
assertThat(encoded, (containsString("ber")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We specifically include extensions on CapabilityStatment even in
|
||||||
|
* summary mode, since this is behaviour that people depend on
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEncodeSummaryCapabilityStatementExtensions_ExplicitlyExcludeExtensions() {
|
||||||
|
CapabilityStatement cs = createCapabilityStatementWithExtensions();
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newJsonParser();
|
||||||
|
parser.setSummaryMode(true);
|
||||||
|
parser.setPrettyPrint(true);
|
||||||
|
parser.setPrettyPrint(true);
|
||||||
|
parser.setDontEncodeElements(
|
||||||
|
"CapabilityStatement.version.extension",
|
||||||
|
"CapabilityStatement.rest.security.extension"
|
||||||
|
);
|
||||||
|
String encoded = parser.encodeResourceToString(cs);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
assertThat(encoded, (containsString("\"rest\"")));
|
||||||
|
assertThat(encoded, not(containsString("http://foo")));
|
||||||
|
assertThat(encoded, not(containsString("bar")));
|
||||||
|
assertThat(encoded, not(containsString("http://goo")));
|
||||||
|
assertThat(encoded, not(containsString("ber")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDontIncludeExtensions() {
|
||||||
|
Patient cs = createPatientWithVariousFieldsAndExtensions();
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newJsonParser();
|
||||||
|
parser.setSummaryMode(true);
|
||||||
|
parser.setPrettyPrint(true);
|
||||||
|
String encoded = parser.encodeResourceToString(cs);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
assertThat(encoded, containsString("\"id\": \"1\""));
|
||||||
|
assertThat(encoded, containsString("\"versionId\": \"1\""));
|
||||||
|
assertThat(encoded, containsString("\"city\": \"CITY\""));
|
||||||
|
assertThat(encoded, not(containsString("http://foo")));
|
||||||
|
assertThat(encoded, not(containsString("bar")));
|
||||||
|
assertThat(encoded, not(containsString("http://goo")));
|
||||||
|
assertThat(encoded, not(containsString("ber")));
|
||||||
|
assertThat(encoded, not(containsString("http://fog")));
|
||||||
|
assertThat(encoded, not(containsString("baz")));
|
||||||
|
assertThat(encoded, not(containsString("Married to work")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForceInclude() {
|
||||||
|
Patient cs = createPatientWithVariousFieldsAndExtensions();
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newJsonParser();
|
||||||
|
parser.setEncodeElements("Patient.maritalStatus", "Patient.address.city.extension");
|
||||||
|
parser.setSummaryMode(true);
|
||||||
|
parser.setPrettyPrint(true);
|
||||||
|
String encoded = parser.encodeResourceToString(cs);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
assertThat(encoded, containsString("\"id\": \"1\""));
|
||||||
|
assertThat(encoded, containsString("\"versionId\": \"1\""));
|
||||||
|
assertThat(encoded, containsString("\"city\": \"CITY\""));
|
||||||
|
assertThat(encoded, not(containsString("http://foo")));
|
||||||
|
assertThat(encoded, not(containsString("bar")));
|
||||||
|
assertThat(encoded, not(containsString("http://fog")));
|
||||||
|
assertThat(encoded, not(containsString("baz")));
|
||||||
|
assertThat(encoded, containsString("http://goo"));
|
||||||
|
assertThat(encoded, containsString("ber"));
|
||||||
|
assertThat(encoded, containsString("Married to work"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForceInclude_UsingStar() {
|
||||||
|
Patient cs = createPatientWithVariousFieldsAndExtensions();
|
||||||
|
|
||||||
|
IParser parser = ourCtx.newJsonParser();
|
||||||
|
parser.setEncodeElements("*.maritalStatus", "*.address.city.extension");
|
||||||
|
parser.setSummaryMode(true);
|
||||||
|
parser.setPrettyPrint(true);
|
||||||
|
String encoded = parser.encodeResourceToString(cs);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
assertThat(encoded, containsString("\"id\": \"1\""));
|
||||||
|
assertThat(encoded, containsString("\"versionId\": \"1\""));
|
||||||
|
assertThat(encoded, containsString("\"city\": \"CITY\""));
|
||||||
|
assertThat(encoded, not(containsString("http://foo")));
|
||||||
|
assertThat(encoded, not(containsString("bar")));
|
||||||
|
assertThat(encoded, not(containsString("http://fog")));
|
||||||
|
assertThat(encoded, not(containsString("baz")));
|
||||||
|
assertThat(encoded, containsString("http://goo"));
|
||||||
|
assertThat(encoded, containsString("ber"));
|
||||||
|
assertThat(encoded, containsString("Married to work"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForceInclude_ViaDefaultConfig() {
|
||||||
|
Patient cs = createPatientWithVariousFieldsAndExtensions();
|
||||||
|
|
||||||
|
FhirContext ctx = FhirContext.forR5();
|
||||||
|
ctx.getParserOptions().setEncodeElementsForSummaryMode("Patient.maritalStatus", "Patient.address.city.extension");
|
||||||
|
ctx.getParserOptions().setDontEncodeElementsForSummaryMode("Patient.id");
|
||||||
|
|
||||||
|
IParser parser = ctx.newJsonParser();
|
||||||
|
parser.setSummaryMode(true);
|
||||||
|
parser.setPrettyPrint(true);
|
||||||
|
String encoded = parser.encodeResourceToString(cs);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
assertThat(encoded, not(containsString("\"id\": \"1\"")));
|
||||||
|
assertThat(encoded, containsString("\"versionId\": \"1\""));
|
||||||
|
assertThat(encoded, containsString("\"city\": \"CITY\""));
|
||||||
|
assertThat(encoded, not(containsString("http://foo")));
|
||||||
|
assertThat(encoded, not(containsString("bar")));
|
||||||
|
assertThat(encoded, not(containsString("http://fog")));
|
||||||
|
assertThat(encoded, not(containsString("baz")));
|
||||||
|
assertThat(encoded, containsString("http://goo"));
|
||||||
|
assertThat(encoded, containsString("ber"));
|
||||||
|
assertThat(encoded, containsString("Married to work"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParserOptionsDontIncludeForSummaryModeDoesntApplyIfNotUsingSummaryMode() {
|
||||||
|
Patient cs = createPatientWithVariousFieldsAndExtensions();
|
||||||
|
|
||||||
|
FhirContext ctx = FhirContext.forR5();
|
||||||
|
ctx.getParserOptions().setEncodeElementsForSummaryMode("Patient.maritalStatus", "Patient.address.city.extension");
|
||||||
|
ctx.getParserOptions().setDontEncodeElementsForSummaryMode("Patient.id");
|
||||||
|
|
||||||
|
IParser parser = ctx.newJsonParser();
|
||||||
|
parser.setSummaryMode(false);
|
||||||
|
parser.setPrettyPrint(true);
|
||||||
|
String encoded = parser.encodeResourceToString(cs);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
assertThat(encoded, containsString("\"id\": \"1\""));
|
||||||
|
assertThat(encoded, containsString("\"versionId\": \"1\""));
|
||||||
|
assertThat(encoded, containsString("\"city\": \"CITY\""));
|
||||||
|
assertThat(encoded, containsString("http://foo"));
|
||||||
|
assertThat(encoded, containsString("bar"));
|
||||||
|
assertThat(encoded, containsString("http://fog"));
|
||||||
|
assertThat(encoded, containsString("baz"));
|
||||||
|
assertThat(encoded, containsString("http://goo"));
|
||||||
|
assertThat(encoded, containsString("ber"));
|
||||||
|
assertThat(encoded, containsString("Married to work"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nonnull CapabilityStatement createCapabilityStatementWithExtensions() {
|
||||||
|
CapabilityStatement cs = new CapabilityStatement();
|
||||||
|
CapabilityStatement.CapabilityStatementRestComponent rest = cs.addRest();
|
||||||
|
rest.setMode(CapabilityStatement.RestfulCapabilityMode.CLIENT);
|
||||||
|
rest.getSecurity()
|
||||||
|
.addExtension()
|
||||||
|
.setUrl("http://foo")
|
||||||
|
.setValue(new StringType("bar"));
|
||||||
|
|
||||||
|
cs.getVersionElement().addExtension()
|
||||||
|
.setUrl("http://goo")
|
||||||
|
.setValue(new StringType("ber"));
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nonnull Patient createPatientWithVariousFieldsAndExtensions() {
|
||||||
|
Patient retVal = new Patient();
|
||||||
|
retVal.setId("Patient/1/_history/1");
|
||||||
|
retVal.getMaritalStatus().setText("Married to work");
|
||||||
|
retVal.addExtension()
|
||||||
|
.setUrl("http://fog")
|
||||||
|
.setValue(new StringType("baz"));
|
||||||
|
Address address = retVal.addAddress();
|
||||||
|
address.setCity("CITY");
|
||||||
|
address
|
||||||
|
.addExtension()
|
||||||
|
.setUrl("http://foo")
|
||||||
|
.setValue(new StringType("bar"));
|
||||||
|
address.getCityElement().addExtension()
|
||||||
|
.setUrl("http://goo")
|
||||||
|
.setValue(new StringType("ber"));
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -19,7 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.jpa.conformance;
|
package ca.uhn.fhir.jpa.conformance;
|
||||||
|
|
||||||
import ca.uhn.fhir.util.CollectionUtil;
|
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
|
||||||
@ -127,7 +126,7 @@ public class DateSearchTestCase {
|
|||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
static List<DateSearchTestCase> expandPrefixCases(Reader theSource, String theFileName) {
|
static List<DateSearchTestCase> expandPrefixCases(Reader theSource, String theFileName) {
|
||||||
Set<String> supportedPrefixes = CollectionUtil.newSet("eq", "ge", "gt", "le", "lt", "ne");
|
Set<String> supportedPrefixes = Set.of(new String[] {"eq", "ge", "gt", "le", "lt", "ne"});
|
||||||
|
|
||||||
// expand these into individual tests for each prefix.
|
// expand these into individual tests for each prefix.
|
||||||
LineNumberReader lineNumberReader = new LineNumberReader(theSource);
|
LineNumberReader lineNumberReader = new LineNumberReader(theSource);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user