Allow contained resources to parse and encode correctly

This commit is contained in:
jamesagnew 2014-06-03 16:52:21 -04:00
parent dfa88a442c
commit bb86724e12
16 changed files with 364 additions and 202 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
/target
*.log
*.log*
nohup.out

View File

@ -30,6 +30,12 @@
<action type="fix">
Support for Query resources fixed (in parser)
</action>
<action type="fix">
Nested contained resources (e.g. encoding a resource with a contained resource that itself contains a resource)
now parse and encode correctly, meaning that all contained resources are placed in the "contained" element
of the root resource, and the parser looks in the root resource for all container levels when stitching
contained resources back together.
</action>
</release>
</body>
</document>

View File

@ -46,6 +46,8 @@ public abstract class BaseRuntimeChildDefinition {
List<? extends IElement> getValues(Object theTarget);
}
public abstract String getElementName();
public abstract int getMax();
public abstract int getMin();

View File

@ -147,4 +147,9 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
return 0;
}
@Override
public String getElementName() {
return "extension";
}
}

View File

@ -71,6 +71,13 @@ public class CodeableConceptDt
// nothing
}
/**
* Constructor which creates a CodeableConceptDt with one coding repetition, containing
* the given system and code
*/
public CodeableConceptDt(String theSystem, String theCode) {
addCoding().setSystem(theSystem).setCode(theCode);
}
@Child(name="coding", type=CodingDt.class, order=0, min=0, max=Child.MAX_UNLIMITED)
@Description(

View File

@ -201,6 +201,9 @@ public class ResourceReferenceDt extends BaseResourceReference implements ICompo
@Override
public IdDt getResourceId() {
if (myReference == null) {
return new IdDt();
}
return new IdDt(myReference.getValue());
}

View File

@ -121,4 +121,18 @@ public class UriDt extends BasePrimitive<URI> {
return true;
}
/**
* Creates a new UriDt instance which uses the given OID as the content (and prepends "urn:oid:" to the
* OID string in the value of the newly created UriDt, per the FHIR specification).
*
* @param theOid The OID to use (<code>null</code> is acceptable and will result in a UriDt instance with a <code>null</code> value)
* @return A new UriDt instance
*/
public static UriDt fromOid(String theOid) {
if (theOid == null) {
return new UriDt();
}
return new UriDt("urn:oid:" + theOid);
}
}

View File

@ -33,6 +33,7 @@ import java.util.UUID;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IElement;
@ -44,6 +45,11 @@ import ca.uhn.fhir.model.primitive.IdDt;
public abstract class BaseParser implements IParser {
private boolean mySuppressNarratives;
private FhirContext myContext;
public BaseParser(FhirContext theContext) {
myContext = theContext;
}
@Override
public TagList parseTagList(String theString) {
@ -116,7 +122,13 @@ public abstract class BaseParser implements IParser {
}
public void containResourcesForEncoding(IResource theResource) {
List<ResourceReferenceDt> allElements = theResource.getAllPopulatedChildElementsOfType(ResourceReferenceDt.class);
containResourcesForEncoding(theResource, theResource);
}
private long myNextContainedId = 1;
private void containResourcesForEncoding(IResource theResource, IResource theTarget) {
List<ResourceReferenceDt> allElements = myContext.newTerser().getAllPopulatedChildElementsOfType(theResource, ResourceReferenceDt.class);
Set<String> allIds = new HashSet<String>();
@ -124,15 +136,18 @@ public abstract class BaseParser implements IParser {
IResource resource = next.getResource();
if (resource != null) {
if (resource.getId().isEmpty()) {
resource.setId(new IdDt(UUID.randomUUID().toString()));
resource.setId(new IdDt(myNextContainedId++));
// resource.setId(new IdDt(UUID.randomUUID().toString()));
}
if (!allIds.contains(resource.getId().getValue())) {
theResource.getContained().getContainedResources().add(resource);
theTarget.getContained().getContainedResources().add(resource);
allIds.add(resource.getId().getValue());
}
next.setReference("#" + resource.getId().getValue());
containResourcesForEncoding(resource, theTarget);
}
}

View File

@ -104,6 +104,7 @@ public class JsonParser extends BaseParser implements IParser {
private boolean myPrettyPrint;
public JsonParser(FhirContext theContext) {
super(theContext);
myContext = theContext;
}
@ -211,7 +212,7 @@ public class JsonParser extends BaseParser implements IParser {
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty()) {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content");
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content", false);
}
eventWriter.writeEnd(); // entry object
@ -299,7 +300,7 @@ public class JsonParser extends BaseParser implements IParser {
theWriter.writeStartArray(theChildName);
ContainedDt value = (ContainedDt) theValue;
for (IResource next : value.getContainedResources()) {
encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null);
encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true);
}
theWriter.writeEnd();
break;
@ -465,8 +466,10 @@ public class JsonParser extends BaseParser implements IParser {
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren());
}
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull) throws IOException {
super.containResourcesForEncoding(theResource);
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIncludedResource) throws IOException {
if (!theIncludedResource) {
super.containResourcesForEncoding(theResource);
}
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
@ -499,7 +502,7 @@ public class JsonParser extends BaseParser implements IParser {
JsonGenerator eventWriter = createJsonGenerator(theWriter);
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null);
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null,false);
eventWriter.flush();
}

View File

@ -109,6 +109,37 @@ class ParserState<T> {
return myState.isPreResource();
}
private void pop() {
myState = myState.myStack;
myState.wereBack();
}
private void push(BaseState theState) {
theState.setStack(myState);
myState = theState;
}
private void putPlacerResourceInDeletedEntry(BundleEntry entry) {
IdDt id = null;
if (entry.getLinkSelf() != null && entry.getLinkSelf().isEmpty() == false) {
id = new IdDt(entry.getLinkSelf().getValue());
} else {
id = entry.getId();
}
IResource resource = entry.getResource();
if (resource == null && id != null && isNotBlank(id.getResourceType())) {
resource = myContext.getResourceDefinition(id.getResourceType()).newInstance();
resource.setId(id);
entry.setResource(resource);
}
if (resource != null) {
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.DELETED_AT, entry.getDeletedAt());
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, id);
}
}
public void string(String theData) {
myState.string(theData);
}
@ -128,16 +159,6 @@ class ParserState<T> {
myState.xmlEvent(theNextEvent);
}
private void pop() {
myState = myState.myStack;
myState.wereBack();
}
private void push(BaseState theState) {
theState.setStack(myState);
myState = theState;
}
public static ParserState<Bundle> getPreAtomInstance(FhirContext theContext, Class<? extends IResource> theResourceType, boolean theJsonMode) throws DataFormatException {
ParserState<Bundle> retVal = new ParserState<Bundle>(theContext, theJsonMode);
retVal.push(retVal.new PreAtomState(theResourceType));
@ -191,13 +212,10 @@ class ParserState<T> {
private static final int STATE_LABEL = 2;
private static final int STATE_NONE = 0;
private static final int STATE_SCHEME = 3;
private static final int STATE_TERM = 1;
private int myCatState = STATE_NONE;
private Tag myInstance;
public AtomCategoryState(Tag theEntry) {
@ -261,32 +279,68 @@ class ParserState<T> {
}
private void putPlacerResourceInDeletedEntry(BundleEntry entry) {
IdDt id = null;
if (entry.getLinkSelf() != null && entry.getLinkSelf().isEmpty() == false) {
id = new IdDt(entry.getLinkSelf().getValue());
} else {
id = entry.getId();
public class AtomDeletedEntryState extends AtomEntryState {
public AtomDeletedEntryState(Bundle theInstance, Class<? extends IResource> theResourceType) {
super(theInstance, theResourceType);
}
IResource resource = entry.getResource();
if (resource == null && id != null && isNotBlank(id.getResourceType())) {
resource = myContext.getResourceDefinition(id.getResourceType()).newInstance();
resource.setId(id);
entry.setResource(resource);
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if ("ref".equals(theName)) {
getEntry().setId(new IdDt(theValue));
} else if ("when".equals(theName)) {
getEntry().setDeleted(new InstantDt(theValue));
}
}
if (resource != null) {
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.DELETED_AT, entry.getDeletedAt());
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, id);
@Override
public void endingElement() throws DataFormatException {
putPlacerResourceInDeletedEntry(getEntry());
super.endingElement();
}
}
private class AtomDeletedJsonWhenState extends BaseState {
private String myData;
private IPrimitiveDatatype<?> myPrimitive;
public AtomDeletedJsonWhenState(IPrimitiveDatatype<?> thePrimitive) {
super(null);
Validate.notNull(thePrimitive, "thePrimitive");
myPrimitive = thePrimitive;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
myData = theValue;
}
@Override
public void endingElement() throws DataFormatException {
myPrimitive.setValueAsString(myData);
pop();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
throw new DataFormatException("Unexpected nested element in atom tag: " + theLocalPart);
}
@Override
protected IElement getCurrentElement() {
return null;
}
}
public class AtomEntryState extends BaseState {
private boolean myDeleted;
private BundleEntry myEntry;
private Class<? extends IResource> myResourceType;
private boolean myDeleted;
public AtomEntryState(Bundle theInstance, Class<? extends IResource> theResourceType) {
super(null);
@ -295,10 +349,6 @@ class ParserState<T> {
theInstance.getEntries().add(myEntry);
}
protected BundleEntry getEntry() {
return myEntry;
}
@Override
public void endingElement() throws DataFormatException {
populateResourceMetadata();
@ -340,6 +390,10 @@ class ParserState<T> {
// TODO: handle category
}
protected BundleEntry getEntry() {
return myEntry;
}
@SuppressWarnings("deprecation")
private void populateResourceMetadata() {
if (myEntry.getResource() == null) {
@ -378,29 +432,6 @@ class ParserState<T> {
}
public class AtomDeletedEntryState extends AtomEntryState {
public AtomDeletedEntryState(Bundle theInstance, Class<? extends IResource> theResourceType) {
super(theInstance, theResourceType);
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if ("ref".equals(theName)) {
getEntry().setId(new IdDt(theValue));
} else if ("when".equals(theName)) {
getEntry().setDeleted(new InstantDt(theValue));
}
}
@Override
public void endingElement() throws DataFormatException {
putPlacerResourceInDeletedEntry(getEntry());
super.endingElement();
}
}
private class AtomLinkState extends BaseState {
private BundleEntry myEntry;
@ -460,77 +491,6 @@ class ParserState<T> {
}
private class BinaryResourceState extends BaseState {
private static final int SUBSTATE_CT = 1;
private static final int SUBSTATE_CONTENT = 2;
private Binary myInstance;
private String myData;
private int mySubState = 0;
public BinaryResourceState(PreResourceState thePreResourceState, Binary theInstance) {
super(thePreResourceState);
myInstance = theInstance;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if ("contentType".equals(theName)) {
myInstance.setContentType(theValue);
} else if (myJsonMode && "value".equals(theName)) {
string(theValue);
}
}
@Override
public void endingElement() throws DataFormatException {
if (mySubState == SUBSTATE_CT) {
myInstance.setContentType(myData);
mySubState = 0;
myData=null;
return;
} else if (mySubState == SUBSTATE_CONTENT) {
myInstance.setContentAsBase64(myData);
mySubState = 0;
myData=null;
return;
} else {
if (!myJsonMode) {
myInstance.setContentAsBase64(myData);
}
pop();
}
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if (myJsonMode && "contentType".equals(theLocalPart) && mySubState == 0) {
mySubState = SUBSTATE_CT;
} else if (myJsonMode && "content".equals(theLocalPart) && mySubState == 0) {
mySubState = SUBSTATE_CONTENT;
} else {
throw new DataFormatException("Unexpected nested element in atom tag: " + theLocalPart);
}
}
@Override
public void string(String theData) {
if (myData == null) {
myData = theData;
} else {
// this shouldn't generally happen so it's ok that it's
// inefficient
myData = myData + theData;
}
}
@Override
protected IElement getCurrentElement() {
return null;
}
}
private class AtomPrimitiveState extends BaseState {
private String myData;
@ -561,6 +521,11 @@ class ParserState<T> {
throw new DataFormatException("Unexpected nested element in atom tag: " + theLocalPart);
}
@Override
protected IElement getCurrentElement() {
return null;
}
@Override
public void string(String theData) {
if (myData == null) {
@ -572,45 +537,6 @@ class ParserState<T> {
}
}
@Override
protected IElement getCurrentElement() {
return null;
}
}
private class AtomDeletedJsonWhenState extends BaseState {
private String myData;
private IPrimitiveDatatype<?> myPrimitive;
public AtomDeletedJsonWhenState(IPrimitiveDatatype<?> thePrimitive) {
super(null);
Validate.notNull(thePrimitive, "thePrimitive");
myPrimitive = thePrimitive;
}
@Override
public void endingElement() throws DataFormatException {
myPrimitive.setValueAsString(myData);
pop();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
throw new DataFormatException("Unexpected nested element in atom tag: " + theLocalPart);
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
myData = theValue;
}
@Override
protected IElement getCurrentElement() {
return null;
}
}
private class AtomState extends BaseState {
@ -710,6 +636,10 @@ class ParserState<T> {
}
}
protected Object getCurrentElement() {
return null;
}
public PreResourceState getPreResourceState() {
return myPreResourceState;
}
@ -734,10 +664,77 @@ class ParserState<T> {
// ignore
}
protected Object getCurrentElement() {
}
private class BinaryResourceState extends BaseState {
private static final int SUBSTATE_CONTENT = 2;
private static final int SUBSTATE_CT = 1;
private String myData;
private Binary myInstance;
private int mySubState = 0;
public BinaryResourceState(PreResourceState thePreResourceState, Binary theInstance) {
super(thePreResourceState);
myInstance = theInstance;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if ("contentType".equals(theName)) {
myInstance.setContentType(theValue);
} else if (myJsonMode && "value".equals(theName)) {
string(theValue);
}
}
@Override
public void endingElement() throws DataFormatException {
if (mySubState == SUBSTATE_CT) {
myInstance.setContentType(myData);
mySubState = 0;
myData=null;
return;
} else if (mySubState == SUBSTATE_CONTENT) {
myInstance.setContentAsBase64(myData);
mySubState = 0;
myData=null;
return;
} else {
if (!myJsonMode) {
myInstance.setContentAsBase64(myData);
}
pop();
}
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if (myJsonMode && "contentType".equals(theLocalPart) && mySubState == 0) {
mySubState = SUBSTATE_CT;
} else if (myJsonMode && "content".equals(theLocalPart) && mySubState == 0) {
mySubState = SUBSTATE_CONTENT;
} else {
throw new DataFormatException("Unexpected nested element in atom tag: " + theLocalPart);
}
}
@Override
protected IElement getCurrentElement() {
return null;
}
@Override
public void string(String theData) {
if (myData == null) {
myData = theData;
} else {
// this shouldn't generally happen so it's ok that it's
// inefficient
myData = myData + theData;
}
}
}
private class ContainedResourcesState extends PreResourceState {
@ -952,7 +949,8 @@ class ParserState<T> {
return;
}
case UNDECL_EXT:
case RESOURCE: {
case RESOURCE:
case EXTENSION_DECLARED:{
// Throw an exception because this shouldn't happen here
break;
}
@ -1031,6 +1029,8 @@ class ParserState<T> {
case RESOURCE:
case RESOURCE_BLOCK:
case UNDECL_EXT:
case EXTENSION_DECLARED:
case CONTAINED_RESOURCES:
break;
}
}
@ -1068,17 +1068,17 @@ class ParserState<T> {
}
@Override
protected IElement getCurrentElement() {
return myInstance;
}
@SuppressWarnings("unchecked")
@Override
public void wereBack() {
myObject = (T) myInstance;
}
@Override
protected IElement getCurrentElement() {
return myInstance;
}
}
private class PreResourceState extends BaseState {
@ -1087,7 +1087,6 @@ class ParserState<T> {
private BundleEntry myEntry;
private IResource myInstance;
private List<ResourceReferenceDt> myResourceReferences = new ArrayList<ResourceReferenceDt>();
private Class<? extends IResource> myResourceType;
public PreResourceState(BundleEntry theEntry, Class<? extends IResource> theResourceType) {
@ -1113,7 +1112,7 @@ class ParserState<T> {
public void endingElement() throws DataFormatException {
pop();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
BaseRuntimeElementDefinition<?> definition;
@ -1136,9 +1135,9 @@ class ParserState<T> {
}
if ("Binary".equals(def.getName())) {
push(new BinaryResourceState(this, (Binary) myInstance));
push(new BinaryResourceState(getRootPreResourceState(), (Binary) myInstance));
} else {
push(new ElementCompositeState(this, def, myInstance));
push(new ElementCompositeState(getRootPreResourceState(), def, myInstance));
}
}
@ -1146,10 +1145,23 @@ class ParserState<T> {
return myContainedResources;
}
@Override
protected IResource getCurrentElement() {
return myInstance;
}
public List<ResourceReferenceDt> getResourceReferences() {
return myResourceReferences;
}
private PreResourceState getRootPreResourceState() {
if (getPreResourceState() != null) {
return getPreResourceState();
}else {
return this;
}
}
@Override
public boolean isPreResource() {
return true;
@ -1178,11 +1190,6 @@ class ParserState<T> {
}
@Override
protected IResource getCurrentElement() {
return myInstance;
}
}
private class PreTagListState extends BaseState {
@ -1208,6 +1215,11 @@ class ParserState<T> {
push(new TagListState(myTagList));
}
@Override
protected TagList getCurrentElement() {
return myTagList;
}
@Override
public boolean isPreResource() {
return true;
@ -1219,11 +1231,6 @@ class ParserState<T> {
myObject = (T) myTagList;
}
@Override
protected TagList getCurrentElement() {
return myTagList;
}
}
private class PrimitiveState extends BaseState {
@ -1507,6 +1514,11 @@ class ParserState<T> {
super.endingElement();
}
@Override
protected IElement getCurrentElement() {
return myDt;
}
@Override
public void xmlEvent(XMLEvent theEvent) {
if (theEvent.isEndElement()) {
@ -1529,11 +1541,6 @@ class ParserState<T> {
}
}
@Override
protected IElement getCurrentElement() {
return myDt;
}
}
}

View File

@ -95,6 +95,7 @@ public class XmlParser extends BaseParser implements IParser {
private XMLOutputFactory myXmlOutputFactory;
public XmlParser(FhirContext theContext) {
super(theContext);
myContext = theContext;
myXmlInputFactory = XMLInputFactory.newInstance();
myXmlOutputFactory = XMLOutputFactory.newInstance();
@ -543,7 +544,9 @@ public class XmlParser extends BaseParser implements IParser {
*
*/
private void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
super.containResourcesForEncoding(theResource);
if (!theIncludedResource) {
super.containResourcesForEncoding(theResource);
}
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
if (resDef == null) {

View File

@ -36,6 +36,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
public class FhirTerser {
@ -147,7 +148,13 @@ public class FhirTerser {
if (nextValue == null) {
continue;
}
if (nextValue.isEmpty()) {
continue;
}
BaseRuntimeElementDefinition<?> childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
if (childElementDef == null) {
throw new DataFormatException("Found value of type[" + nextValue.getClass().getSimpleName() + "] which is not valid for field[" + nextChild.getElementName() + "] in " + childDef.getName());
}
getAllChildElementsOfType(nextValue, childElementDef, theType, theList);
}
}

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@ -83,6 +84,44 @@ public class JsonParserTest {
}
@Test
public void testNestedContainedResources() {
Observation A = new Observation();
A.getName().setText("A");
Observation B = new Observation();
B.getName().setText("B");
A.addRelated().setTarget(new ResourceReferenceDt(B));
Observation C = new Observation();
C.getName().setText("C");
B.addRelated().setTarget(new ResourceReferenceDt(C));
String str = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(A);
ourLog.info(str);
assertThat(str, stringContainsInOrder(Arrays.asList("\"text\":\"B\"", "\"text\":\"C\"", "\"text\":\"A\"")));
// Only one (outer) contained block
int idx0 = str.indexOf("\"contained\"");
int idx1 = str.indexOf("\"contained\"",idx0+1);
assertNotEquals(-1, idx0);
assertEquals(-1, idx1);
Observation obs = ourCtx.newJsonParser().parseResource(Observation.class, str);
assertEquals("A",obs.getName().getText().getValue());
Observation obsB = (Observation) obs.getRelatedFirstRep().getTarget().getResource();
assertEquals("B",obsB.getName().getText().getValue());
Observation obsC = (Observation) obsB.getRelatedFirstRep().getTarget().getResource();
assertEquals("C",obsC.getName().getText().getValue());
}
@Test
public void testParseQuery() {
String msg = "{\n" +

View File

@ -17,6 +17,7 @@ import java.util.List;
import org.apache.commons.io.IOUtils;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.hamcrest.Matchers;
import org.hamcrest.core.IsNot;
import org.hamcrest.core.StringContains;
import org.hamcrest.text.StringContainsInOrder;
@ -80,6 +81,46 @@ public class XmlParserTest {
}
@Test
public void testNestedContainedResources() {
Observation A = new Observation();
A.getName().setText("A");
Observation B = new Observation();
B.getName().setText("B");
A.addRelated().setTarget(new ResourceReferenceDt(B));
Observation C = new Observation();
C.getName().setText("C");
B.addRelated().setTarget(new ResourceReferenceDt(C));
String str = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(A);
ourLog.info(str);
assertThat(str, stringContainsInOrder(Arrays.asList("<text value=\"B\"/>", "<text value=\"C\"/>", "<text value=\"A\"/>")));
assertThat(str, stringContainsInOrder(Arrays.asList("<contained>", "</contained>")));
// Only one (outer) contained block
int idx0 = str.indexOf("<contained>");
int idx1 = str.indexOf("<contained>",idx0+1);
assertNotEquals(-1, idx0);
assertEquals(-1, idx1);
Observation obs = ourCtx.newXmlParser().parseResource(Observation.class, str);
assertEquals("A",obs.getName().getText().getValue());
Observation obsB = (Observation) obs.getRelatedFirstRep().getTarget().getResource();
assertEquals("B",obsB.getName().getText().getValue());
Observation obsC = (Observation) obsB.getRelatedFirstRep().getTarget().getResource();
assertEquals("C",obsC.getName().getText().getValue());
}
@Test
public void testParseQuery() {
String msg = "<Query xmlns=\"http://hl7.org/fhir\">\n" +

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry including="**/*.java" kind="src" path="target/generated-sources/tinder"/>
<classpathentry kind="src" path="target/generated-sources/tinder"/>
<classpathentry kind="var" path="M2_REPO/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2.jar" sourcepath="M2_REPO/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar" sourcepath="M2_REPO/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0-sources.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>

View File

@ -43,6 +43,15 @@ public class ${className}
#########################
### Type-specific constructors
#########################
#if ( ${className} == "CodeableConceptDt" )
/**
* Constructor which creates a CodeableConceptDt with one coding repetition, containing
* the given system and code
*/
public CodeableConceptDt(String theSystem, String theCode) {
addCoding().setSystem(theSystem).setCode(theCode);
}
#end
#if ( ${className} == "CodingDt" )
/**
* Creates a new Coding with the given system and code