JsonParser contained working

This commit is contained in:
jamesagnew 2014-03-22 12:33:07 -04:00
parent cb19b17e59
commit 46d6387761
9 changed files with 4311 additions and 65 deletions

View File

@ -33,15 +33,11 @@
* Support "Binary" resource, which is a special resource type
* Submit "_pretty" as possible parameter to HL7
---------
Issues:
* Need to be able to bind Identifier.system to a set of allowable values in a profile. Graeme
has suggested making value[x] repeatable in the profile definition
* CarePlan.Activity has a type of "xmlIdRef" which is a weird datatype. The example here:
http://www.hl7.org/implement/standards/fhir/careplan-example-integrated.xml.html
seems to use an extension to model that element instead of the goal type too.
* Would like a standardized (but optional) HTTP parameter for pretty-printing. Suggest: _pretty=true
* Example-patient-general.xml/json has <reference value="Organization/1"/> but "resource":"Organization/1"

View File

@ -56,6 +56,7 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
if (!((IRuntimeDatatypeDefinition) next).isSpecialization()) {
String attrName = "value" + next.getName().substring(0, 1).toUpperCase() + next.getName().substring(1);
datatypeAttributeNameToDefinition.put(attrName, next);
datatypeAttributeNameToDefinition.put(attrName.toLowerCase(), next);
}
}
}

View File

@ -9,10 +9,13 @@ import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonString;
@ -20,7 +23,6 @@ import javax.json.JsonValue;
import javax.json.JsonValue.ValueType;
import javax.json.stream.JsonGenerator;
import javax.json.stream.JsonGeneratorFactory;
import javax.json.stream.JsonParser.Event;
import org.apache.commons.lang3.StringUtils;
@ -45,6 +47,7 @@ import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
@ -164,45 +167,168 @@ public class JsonParser extends BaseParser implements IParser {
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString) resourceTypeObj).getString();
RuntimeResourceDefinition def;
if (theResourceType != null) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResourceType);
def = myContext.getResourceDefinition(theResourceType);
} else {
RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceType);
def = myContext.getResourceDefinition(resourceType);
}
PushbackJsonParser parser = new PushbackJsonParser(Json.createParser(theReader));
ParserState<? extends IResource> state = ParserState.getPreResourceInstance(def.getImplementingClass(), myContext, true);
state.enteringNewElement(null, def.getName());
while (parser.hasNext()) {
parseChildren(object, state);
Event next = parser.next();
switch (next) {
case END_ARRAY:
state.endingElement(null);
@SuppressWarnings("unchecked")
T retVal = (T) state.getObject();
return retVal;
}
private void parseChildren(JsonObject theObject, ParserState<? extends IResource> theState) {
String elementId = null;
for (String nextName : theObject.keySet()) {
if ("resourceType".equals(nextName)) {
continue;
} else if ("id".equals(nextName)) {
elementId = theObject.getString(nextName);
continue;
} else if ("extension".equals(nextName)) {
JsonArray array = theObject.getJsonArray(nextName);
parseExtension(theState, array, false);
continue;
} else if ("modifierExtension".equals(nextName)) {
JsonArray array = theObject.getJsonArray(nextName);
parseExtension(theState, array, true);
continue;
} else if (nextName.charAt(0) == '_') {
continue;
}
JsonValue nextVal = theObject.get(nextName);
JsonValue alternateVal = theObject.get('_' + nextName);
parseChildren(theState, nextName, nextVal, alternateVal);
}
if (elementId != null) {
// System.out.checkError(); // TODO: remove
IResource object = theState.getObject();
object.setId(new IdDt(elementId));
}
}
private void parseChildren(ParserState<? extends IResource> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal) {
switch (theJsonVal.getValueType()) {
case ARRAY: {
JsonArray nextArray = (JsonArray) theJsonVal;
JsonArray nextAlternateArray = (JsonArray) theAlternateVal;
for (int i = 0; i < nextArray.size(); i++) {
JsonValue nextObject = nextArray.get(i);
JsonValue nextAlternate = null;
if (nextAlternateArray != null) {
nextAlternate = nextAlternateArray.get(i);
}
parseChildren(theState, theName, nextObject, nextAlternate);
}
break;
case END_OBJECT:
}
case OBJECT: {
theState.enteringNewElement(null, theName);
JsonObject nextObject = (JsonObject) theJsonVal;
boolean preResource = false;
if (theState.isPreResource()) {
String resType = nextObject.getString("resourceType");
if (isBlank(resType)) {
throw new DataFormatException("Missing 'resourceType' from resource");
}
theState.enteringNewElement(null, resType);
preResource = true;
}
parseChildren(nextObject, theState);
if (preResource) {
theState.endingElement(null);
}
theState.endingElement(null);
break;
case KEY_NAME:
}
case STRING: {
JsonString nextValStr = (JsonString) theJsonVal;
theState.enteringNewElement(null, theName);
theState.attributeValue("value", nextValStr.getString());
parseAlternates(theAlternateVal, theState);
theState.endingElement(null);
break;
case START_ARRAY:
}
case NUMBER:
case FALSE:
case TRUE:
theState.enteringNewElement(null, theName);
theState.attributeValue("value", theJsonVal.toString());
parseAlternates(theAlternateVal, theState);
theState.endingElement(null);
break;
case START_OBJECT:
case NULL:
break;
case VALUE_FALSE:
case VALUE_TRUE:
}
}
private void parseAlternates(JsonValue theAlternateVal, ParserState<? extends IResource> theState) {
if (theAlternateVal == null || theAlternateVal.getValueType() == ValueType.NULL) {
return;
}
JsonObject alternate = (JsonObject) theAlternateVal;
for (Entry<String, JsonValue> nextEntry : alternate.entrySet()) {
String nextKey = nextEntry.getKey();
JsonValue nextVal = nextEntry.getValue();
if ("extension".equals(nextKey)) {
boolean isModifier = false;
JsonArray array = (JsonArray) nextEntry.getValue();
parseExtension(theState, array, isModifier);
} else if ("modifierExtension".equals(nextKey)) {
boolean isModifier = true;
JsonArray array = (JsonArray) nextEntry.getValue();
parseExtension(theState, array, isModifier);
} else if ("id".equals(nextKey)) {
switch (nextVal.getValueType()) {
case STRING:
theState.attributeValue("id", ((JsonString) nextVal).getString());
break;
case VALUE_NULL:
break;
case VALUE_NUMBER:
break;
case VALUE_STRING:
case NULL:
break;
default:
break;
}
}
}
}
private void parseExtension(ParserState<? extends IResource> theState, JsonArray array, boolean theIsModifier) {
// TODO: use theIsModifier
for (int i = 0; i < array.size(); i++) {
JsonObject nextExtObj = array.getJsonObject(i);
String url = nextExtObj.getString("url");
theState.enteringNewElementExtension(null, url, false);
for (Iterator<String> iter = nextExtObj.keySet().iterator(); iter.hasNext();) {
String next = iter.next();
if ("url".equals(next)) {
continue;
} else if ("extension".equals(next)) {
JsonArray jsonVal = (JsonArray) nextExtObj.get(next);
parseExtension(theState, jsonVal, false);
} else if ("modifierExtension".equals(next)) {
JsonArray jsonVal = (JsonArray) nextExtObj.get(next);
parseExtension(theState, jsonVal, true);
} else {
JsonValue jsonVal = nextExtObj.get(next);
parseChildren(theState, next, jsonVal, null);
}
}
theState.endingElement(null);
}
return null;
}
@Override

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.parser;
import static org.apache.commons.lang3.StringUtils.*;
import java.util.ArrayList;
@ -49,9 +48,11 @@ class ParserState<T extends IElement> {
private FhirContext myContext;
private T myObject;
private BaseState myState;
private boolean myJsonMode;
private ParserState(FhirContext theContext) {
private ParserState(FhirContext theContext, boolean theJsonMode) {
myContext = theContext;
myJsonMode = theJsonMode;
}
public void attributeValue(String theName, String theValue) throws DataFormatException {
@ -70,8 +71,9 @@ class ParserState<T extends IElement> {
myState.enteringNewElementExtension(theElem, theUrlAttr, theIsModifier);
}
@SuppressWarnings("unchecked")
public T getObject() {
return myObject;
return (T) myState.getCurrentElement();
}
public boolean isComplete() {
@ -105,8 +107,8 @@ class ParserState<T extends IElement> {
myState = theState;
}
public static ParserState<Bundle> getPreAtomInstance(FhirContext theContext) throws DataFormatException {
ParserState<Bundle> retVal = new ParserState<Bundle>(theContext);
public static ParserState<Bundle> getPreAtomInstance(FhirContext theContext, boolean theJsonMode) throws DataFormatException {
ParserState<Bundle> retVal = new ParserState<Bundle>(theContext, theJsonMode);
retVal.push(retVal.new PreAtomState());
return retVal;
}
@ -115,8 +117,8 @@ class ParserState<T extends IElement> {
* @param theResourceType
* May be null
*/
public static <T extends IResource> ParserState<T> getPreResourceInstance(Class<T> theResourceType, FhirContext theContext) throws DataFormatException {
ParserState<T> retVal = new ParserState<T>(theContext);
public static <T extends IResource> ParserState<T> getPreResourceInstance(Class<T> theResourceType, FhirContext theContext, boolean theJsonMode) throws DataFormatException {
ParserState<T> retVal = new ParserState<T>(theContext, theJsonMode);
retVal.push(retVal.new PreResourceState(theResourceType));
return retVal;
}
@ -212,7 +214,7 @@ class ParserState<T extends IElement> {
} else if ("content".equals(theLocalPart)) {
push(new PreResourceState(myEntry));
} else if ("summary".equals(theLocalPart)) {
push(new XhtmlState(getPreResourceState(),myEntry.getSummary(), false));
push(new XhtmlState(getPreResourceState(), myEntry.getSummary(), false));
} else if ("category".equals(theLocalPart)) {
push(new AtomCategoryState(myEntry.addCategory()));
} else {
@ -433,10 +435,13 @@ class ParserState<T extends IElement> {
return null;
}
public boolean isPreResource() {
return false;
}
private class ContainedResourcesState extends PreResourceState
{
}
private class ContainedResourcesState extends PreResourceState {
public ContainedResourcesState(PreResourceState thePreResourcesState) {
super(thePreResourcesState);
@ -451,7 +456,7 @@ class ParserState<T extends IElement> {
public void wereBack() {
IResource res = getCurrentElement();
assert res != null;
if (res.getId()==null || res.getId().isEmpty()) {
if (res.getId() == null || res.getId().isEmpty()) {
ourLog.debug("Discarding contained resource with no ID!");
} else {
getPreResourceState().getContainedResources().put(res.getId().getValueAsString(), res);
@ -459,7 +464,6 @@ class ParserState<T extends IElement> {
getPreResourceState().getCurrentElement().getContained().getContainedResources().add(res);
}
}
private class DeclaredExtensionState extends BaseState {
@ -471,7 +475,7 @@ class ParserState<T extends IElement> {
public DeclaredExtensionState(PreResourceState thePreResourceState, RuntimeChildDeclaredExtensionDefinition theDefinition, IElement theParentInstance) {
super(thePreResourceState);
myPreResourceState=thePreResourceState;
myPreResourceState = thePreResourceState;
myDefinition = theDefinition;
myParentInstance = theParentInstance;
}
@ -625,8 +629,14 @@ class ParserState<T extends IElement> {
}
case CONTAINED_RESOURCES: {
RuntimeElemContainedResources targetElem = (RuntimeElemContainedResources) target;
ContainedDt newDt = targetElem.newInstance();
List<? extends IElement> values = child.getAccessor().getValues(myInstance);
ContainedDt newDt;
if (values == null || values.isEmpty() || values.get(0) == null) {
newDt = targetElem.newInstance();
child.getMutator().addValue(myInstance, newDt);
}else {
newDt = (ContainedDt) values.get(0);
}
ContainedResourcesState state = new ContainedResourcesState(getPreResourceState());
push(state);
return;
@ -723,7 +733,6 @@ class ParserState<T extends IElement> {
}
private class PreAtomState extends BaseState {
private Bundle myInstance;
@ -771,6 +780,11 @@ class ParserState<T extends IElement> {
private Class<? extends IResource> myResourceType;
@Override
public boolean isPreResource() {
return true;
}
public PreResourceState(BundleEntry theEntry) {
super(null);
myEntry = theEntry;
@ -835,7 +849,7 @@ class ParserState<T extends IElement> {
for (ResourceReferenceDt nextRef : myResourceReferences) {
String ref = nextRef.getReference().getValue();
if (isNotBlank(ref)){
if (isNotBlank(ref)) {
if (ref.startsWith("#")) {
IResource target = myContainedResources.get(ref.substring(1));
if (target != null) {
@ -868,6 +882,8 @@ class ParserState<T extends IElement> {
public void attributeValue(String theName, String theValue) throws DataFormatException {
if ("value".equals(theName)) {
myInstance.setValueAsString(theValue);
} else if ("id".equals(theName)) {
myInstance.setId(new IdDt(theValue));
}
}
@ -889,7 +905,10 @@ class ParserState<T extends IElement> {
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
throw new Error("?? can this happen?"); // TODO: can this happen?
throw new Error("Element " + theLocalPart + " in primitive!"); // TODO:
// can
// this
// happen?
}
@Override
@ -952,6 +971,9 @@ class ParserState<T extends IElement> {
} else if ("reference".equals(theLocalPart)) {
mySubState = ResourceReferenceSubState.REFERENCE;
break;
} else if ("resource".equals(theLocalPart)) {
mySubState = ResourceReferenceSubState.REFERENCE;
break;
}
//$FALL-THROUGH$
case DISPLAY:
@ -971,7 +993,6 @@ class ParserState<T extends IElement> {
DISPLAY, INITIAL, REFERENCE
}
private class XhtmlState extends BaseState {
private int myDepth;
private XhtmlDt myDt;
@ -985,6 +1006,24 @@ class ParserState<T extends IElement> {
myIncludeOuterEvent = theIncludeOuterEvent;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if (myJsonMode) {
myDt.setValueAsString(theValue);
return;
}
super.attributeValue(theName, theValue);
}
@Override
public void endingElement(EndElement theElem) throws DataFormatException {
if (myJsonMode) {
pop();
return;
}
super.endingElement(theElem);
}
@Override
public void xmlEvent(XMLEvent theEvent) {
if (theEvent.isEndElement()) {
@ -1014,4 +1053,8 @@ class ParserState<T extends IElement> {
}
public boolean isPreResource() {
return myState.isPreResource();
}
}

View File

@ -552,12 +552,12 @@ public class XmlParser extends BaseParser implements IParser {
}
private Bundle parseBundle(XMLEventReader theStreamReader) {
ParserState<Bundle> parserState = ParserState.getPreAtomInstance(myContext);
ParserState<Bundle> parserState = ParserState.getPreAtomInstance(myContext,false);
return doXmlLoop(theStreamReader, parserState);
}
private <T extends IResource> T parseResource(Class<T> theResourceType, XMLEventReader theStreamReader) {
ParserState<T> parserState = ParserState.getPreResourceInstance(theResourceType, myContext);
ParserState<T> parserState = ParserState.getPreResourceInstance(theResourceType, myContext,false);
return doXmlLoop(theStreamReader, parserState);
}

View File

@ -18,6 +18,7 @@ import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.UndeclaredExtension;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient;
@ -86,7 +87,7 @@ public class JsonParserTest {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/example-patient-general.json"));
FhirContext ctx = new FhirContext(Patient.class);
IParser p = ctx.newJsonParser();
ourLog.info("Reading in message: {}", msg);
// ourLog.info("Reading in message: {}", msg);
Patient res = p.parseResource(Patient.class, msg);
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
@ -94,6 +95,26 @@ public class JsonParserTest {
}
@Test
public void testParseWithContained() throws DataFormatException, IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/diagnostic-report.json"));
FhirContext ctx = new FhirContext(Patient.class);
IParser p = ctx.newJsonParser();
// ourLog.info("Reading in message: {}", msg);
DiagnosticReport res = p.parseResource(DiagnosticReport.class, msg);
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
ourLog.info(encoded);
ResourceReferenceDt reference = res.getResult().get(1);
Observation obs = (Observation) reference.getResource();
assertEquals("789-8", obs.getName().getCoding().get(0).getCode().getValue());
}
@Test
public void testEncodeContainedResourcesMore() throws IOException {

View File

@ -428,7 +428,9 @@ public class ResfulServerMethodTest {
assertEquals(200, status.getStatusLine().getStatusCode());
Patient patient = (Patient) ourCtx.newJsonParser().parseResource(responseContent);
assertEquals("PatientOne", patient.getName().get(0).getGiven().get(0).getValue());
// assertEquals("PatientOne", patient.getName().get(0).getGiven().get(0).getValue());
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient));
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long