mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-02-18 02:45:07 +00:00
Improve performance in parser when parsing large bundles
This commit is contained in:
parent
3eb805fb0a
commit
514da88787
@ -230,6 +230,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||||||
|
|
||||||
parseChildren(object, state);
|
parseChildren(object, state);
|
||||||
|
|
||||||
|
state.endingElement();
|
||||||
state.endingElement();
|
state.endingElement();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -1109,6 +1110,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||||||
|
|
||||||
parseBundleChildren(object, state);
|
parseBundleChildren(object, state);
|
||||||
|
|
||||||
|
state.endingElement();
|
||||||
state.endingElement();
|
state.endingElement();
|
||||||
|
|
||||||
Bundle retVal = state.getObject();
|
Bundle retVal = state.getObject();
|
||||||
@ -1191,8 +1193,6 @@ public class JsonParser extends BaseParser implements IParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void parseChildren(JsonObject theObject, ParserState<?> theState) {
|
private void parseChildren(JsonObject theObject, ParserState<?> theState) {
|
||||||
String elementId = null;
|
|
||||||
|
|
||||||
Set<String> keySet = theObject.keySet();
|
Set<String> keySet = theObject.keySet();
|
||||||
|
|
||||||
int allUnderscoreNames = 0;
|
int allUnderscoreNames = 0;
|
||||||
@ -1201,23 +1201,6 @@ public class JsonParser extends BaseParser implements IParser {
|
|||||||
for (String nextName : keySet) {
|
for (String nextName : keySet) {
|
||||||
if ("resourceType".equals(nextName)) {
|
if ("resourceType".equals(nextName)) {
|
||||||
continue;
|
continue;
|
||||||
} else if ("id".equals(nextName)) {
|
|
||||||
if (theObject.isNull(nextName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
elementId = theObject.getString(nextName);
|
|
||||||
if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if ("_id".equals(nextName)) {
|
|
||||||
if (theObject.isNull(nextName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// _id is incorrect, but some early examples in the FHIR spec used it
|
|
||||||
if (theObject.get(nextName).getValueType() == ValueType.STRING) {
|
|
||||||
elementId = theObject.getString(nextName);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else if ("extension".equals(nextName)) {
|
} else if ("extension".equals(nextName)) {
|
||||||
JsonArray array = grabJsonArray(theObject, nextName, "extension");
|
JsonArray array = grabJsonArray(theObject, nextName, "extension");
|
||||||
parseExtension(theState, array, false);
|
parseExtension(theState, array, false);
|
||||||
@ -1245,14 +1228,14 @@ public class JsonParser extends BaseParser implements IParser {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elementId != null) {
|
// if (elementId != null) {
|
||||||
IBase object = (IBase) theState.getObject();
|
// IBase object = (IBase) theState.getObject();
|
||||||
if (object instanceof IIdentifiableElement) {
|
// if (object instanceof IIdentifiableElement) {
|
||||||
((IIdentifiableElement) object).setElementSpecificId(elementId);
|
// ((IIdentifiableElement) object).setElementSpecificId(elementId);
|
||||||
} else if (object instanceof IBaseResource) {
|
// } else if (object instanceof IBaseResource) {
|
||||||
((IBaseResource) object).getIdElement().setValue(elementId);
|
// ((IBaseResource) object).getIdElement().setValue(elementId);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This happens if an element has an extension but no actual value. I.e.
|
* This happens if an element has an extension but no actual value. I.e.
|
||||||
@ -1394,6 +1377,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||||||
|
|
||||||
parseChildren(object, state);
|
parseChildren(object, state);
|
||||||
|
|
||||||
|
state.endingElement();
|
||||||
state.endingElement();
|
state.endingElement();
|
||||||
|
|
||||||
return state.getObject();
|
return state.getObject();
|
||||||
|
@ -149,14 +149,8 @@ class ParserState<T> {
|
|||||||
myState.enteringNewElementExtension(theElem, theUrlAttr, theIsModifier);
|
myState.enteringNewElementExtension(theElem, theUrlAttr, theIsModifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T getObject() {
|
public T getObject() {
|
||||||
IBase retVal = myState.getCurrentElement();
|
return myObject;
|
||||||
return (T) retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isComplete() {
|
|
||||||
return myObject != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPreResource() {
|
public boolean isPreResource() {
|
||||||
@ -191,10 +185,16 @@ class ParserState<T> {
|
|||||||
return newChildInstance;
|
return newChildInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private void pop() {
|
private void pop() {
|
||||||
myPreviousElement = myState.getCurrentElement();
|
myPreviousElement = myState.getCurrentElement();
|
||||||
myState = myState.myStack;
|
if (myState.myStack != null) {
|
||||||
myState.wereBack();
|
myState = myState.myStack;
|
||||||
|
myState.wereBack();
|
||||||
|
} else {
|
||||||
|
myObject = (T) myState.getCurrentElement();
|
||||||
|
myState = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void push(BaseState theState) {
|
private void push(BaseState theState) {
|
||||||
@ -250,7 +250,9 @@ class ParserState<T> {
|
|||||||
* intended for embedded XHTML content
|
* intended for embedded XHTML content
|
||||||
*/
|
*/
|
||||||
public void xmlEvent(XMLEvent theNextEvent) {
|
public void xmlEvent(XMLEvent theNextEvent) {
|
||||||
myState.xmlEvent(theNextEvent);
|
if (myState != null) {
|
||||||
|
myState.xmlEvent(theNextEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ParserState<Bundle> getPreAtomInstance(IParser theParser, FhirContext theContext, Class<? extends IBaseResource> theResourceType, boolean theJsonMode, IParserErrorHandler theErrorHandler) throws DataFormatException {
|
static ParserState<Bundle> getPreAtomInstance(IParser theParser, FhirContext theContext, Class<? extends IBaseResource> theResourceType, boolean theJsonMode, IParserErrorHandler theErrorHandler) throws DataFormatException {
|
||||||
@ -772,7 +774,7 @@ class ParserState<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endingElement() throws DataFormatException {
|
public void endingElement() throws DataFormatException {
|
||||||
// ignore
|
pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -795,7 +797,6 @@ class ParserState<T> {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public void wereBack() {
|
public void wereBack() {
|
||||||
myObject = (T) myInstance;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stitch together resource references
|
* Stitch together resource references
|
||||||
@ -1609,9 +1610,6 @@ class ParserState<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void endingElement() {
|
public void endingElement() {
|
||||||
pop();
|
pop();
|
||||||
if (myState == null) {
|
|
||||||
myObject = (T) myInstance;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1994,11 +1992,6 @@ class ParserState<T> {
|
|||||||
super(theResourceType);
|
super(theResourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endingElement() throws DataFormatException {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
|
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
|
||||||
if (!"feed".equals(theLocalPart)) {
|
if (!"feed".equals(theLocalPart)) {
|
||||||
@ -2018,11 +2011,6 @@ class ParserState<T> {
|
|||||||
super(theResourceType);
|
super(theResourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endingElement() throws DataFormatException {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
|
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
|
||||||
if (!"Bundle".equals(theLocalPart)) {
|
if (!"Bundle".equals(theLocalPart)) {
|
||||||
@ -2064,6 +2052,8 @@ class ParserState<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endingElement() throws DataFormatException {
|
public void endingElement() throws DataFormatException {
|
||||||
|
// postProcess();
|
||||||
|
stitchBundleCrossReferences();
|
||||||
pop();
|
pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2143,6 +2133,10 @@ class ParserState<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void wereBack() {
|
public void wereBack() {
|
||||||
|
postProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void postProcess() {
|
||||||
if (myContext.hasDefaultTypeForProfile()) {
|
if (myContext.hasDefaultTypeForProfile()) {
|
||||||
IBaseMetaType meta = myInstance.getMeta();
|
IBaseMetaType meta = myInstance.getMeta();
|
||||||
Class<? extends IBaseResource> wantedProfileType = null;
|
Class<? extends IBaseResource> wantedProfileType = null;
|
||||||
@ -2217,8 +2211,13 @@ class ParserState<T> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
populateTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stitchBundleCrossReferences() {
|
||||||
final boolean bundle = "Bundle".equals(myContext.getResourceDefinition(myInstance).getName());
|
final boolean bundle = "Bundle".equals(myContext.getResourceDefinition(myInstance).getName());
|
||||||
if (bundle) {
|
if (bundle) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stitch together resource references
|
* Stitch together resource references
|
||||||
*/
|
*/
|
||||||
@ -2258,8 +2257,6 @@ class ParserState<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
populateTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -2307,9 +2304,6 @@ class ParserState<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void wereBack() {
|
public void wereBack() {
|
||||||
super.wereBack();
|
super.wereBack();
|
||||||
if (myEntry == null && myMutator == null) {
|
|
||||||
myObject = (T) getCurrentElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
IResource nextResource = (IResource) getCurrentElement();
|
IResource nextResource = (IResource) getCurrentElement();
|
||||||
String version = ResourceMetadataKeyEnum.VERSION.get(nextResource);
|
String version = ResourceMetadataKeyEnum.VERSION.get(nextResource);
|
||||||
@ -2353,9 +2347,6 @@ class ParserState<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void wereBack() {
|
public void wereBack() {
|
||||||
super.wereBack();
|
super.wereBack();
|
||||||
if (myTarget == null) {
|
|
||||||
myObject = (T) getCurrentElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getCurrentElement() instanceof IDomainResource) {
|
if (getCurrentElement() instanceof IDomainResource) {
|
||||||
IDomainResource elem = (IDomainResource) getCurrentElement();
|
IDomainResource elem = (IDomainResource) getCurrentElement();
|
||||||
@ -2406,12 +2397,6 @@ class ParserState<T> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public void wereBack() {
|
|
||||||
myObject = (T) myTagList;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PrimitiveState extends BaseState {
|
private class PrimitiveState extends BaseState {
|
||||||
|
@ -242,6 +242,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||||||
parserState.attributeValue(name, elem.getValue());
|
parserState.attributeValue(name, elem.getValue());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case XMLStreamConstants.END_DOCUMENT:
|
||||||
case XMLStreamConstants.END_ELEMENT: {
|
case XMLStreamConstants.END_ELEMENT: {
|
||||||
if (!heldComments.isEmpty()) {
|
if (!heldComments.isEmpty()) {
|
||||||
for (String next : heldComments) {
|
for (String next : heldComments) {
|
||||||
@ -250,9 +251,9 @@ public class XmlParser extends BaseParser implements IParser {
|
|||||||
heldComments.clear();
|
heldComments.clear();
|
||||||
}
|
}
|
||||||
parserState.endingElement();
|
parserState.endingElement();
|
||||||
if (parserState.isComplete()) {
|
// if (parserState.isComplete()) {
|
||||||
return parserState.getObject();
|
// return parserState.getObject();
|
||||||
}
|
// }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case XMLStreamConstants.CHARACTERS: {
|
case XMLStreamConstants.CHARACTERS: {
|
||||||
@ -273,7 +274,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||||||
throw new DataFormatException("DataFormatException at [" + nextEvent.getLocation().toString() + "]: " + e.getMessage(), e);
|
throw new DataFormatException("DataFormatException at [" + nextEvent.getLocation().toString() + "]: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return parserState.getObject();
|
||||||
} catch (XMLStreamException e) {
|
} catch (XMLStreamException e) {
|
||||||
throw new DataFormatException(e);
|
throw new DataFormatException(e);
|
||||||
}
|
}
|
||||||
|
@ -681,10 +681,6 @@ public class JsonParserTest {
|
|||||||
|
|
||||||
ListResource parsed = ourCtx.newJsonParser().parseResource(ListResource.class, enc);
|
ListResource parsed = ourCtx.newJsonParser().parseResource(ListResource.class, enc);
|
||||||
assertEquals(Patient.class, parsed.getEntryFirstRep().getItem().getResource().getClass());
|
assertEquals(Patient.class, parsed.getEntryFirstRep().getItem().getResource().getClass());
|
||||||
|
|
||||||
enc = enc.replace("\"id\"", "\"_id\"");
|
|
||||||
parsed = ourCtx.newJsonParser().parseResource(ListResource.class, enc);
|
|
||||||
assertEquals(Patient.class, parsed.getEntryFirstRep().getItem().getResource().getClass());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -645,11 +645,7 @@ public class JsonParserHl7OrgDstu2Test {
|
|||||||
|
|
||||||
List_ parsed = ourCtx.newJsonParser().parseResource(List_.class,enc);
|
List_ parsed = ourCtx.newJsonParser().parseResource(List_.class,enc);
|
||||||
assertEquals(Patient.class, parsed.getEntry().get(0).getItem().getResource().getClass());
|
assertEquals(Patient.class, parsed.getEntry().get(0).getItem().getResource().getClass());
|
||||||
|
}
|
||||||
enc = enc.replace("\"id\"", "\"_id\"");
|
|
||||||
parsed = ourCtx.newJsonParser().parseResource(List_.class,enc);
|
|
||||||
assertEquals(Patient.class, parsed.getEntry().get(0).getItem().getResource().getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeInvalidChildGoodException() {
|
public void testEncodeInvalidChildGoodException() {
|
||||||
|
@ -22,9 +22,23 @@
|
|||||||
</action>
|
</action>
|
||||||
<action type="add" issue="346">
|
<action type="add" issue="346">
|
||||||
Server now respects the parameter <![CDATA[<code>_format=application/xml+fhir"</code>]]>
|
Server now respects the parameter <![CDATA[<code>_format=application/xml+fhir"</code>]]>
|
||||||
which is technically invalid since the + should be escaped, but is likely to be used.
|
which is technically invalid since the + should be escaped, but is likely to be used. Also,
|
||||||
|
a parameter of <![CDATA[<code>_format=html</code>]]> can now be used, which
|
||||||
|
forces SyntaxHighlightingInterceptor to use HTML even
|
||||||
|
if the headers wouldn't otherwise trigger it.
|
||||||
Thanks to Jim Steel for reporting!
|
Thanks to Jim Steel for reporting!
|
||||||
</action>
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Improve performance when parsing large bundles by fixing a loop over all of the
|
||||||
|
entries inthe bundle to stitch together cross-references, which was happening once
|
||||||
|
per entry instead of once overall. Thanks to Erick on the HAPI FHIR Google Group for
|
||||||
|
noticing that this was an issue!
|
||||||
|
</action>
|
||||||
|
<action type="remove">
|
||||||
|
JSON parser no longer allows the resource ID to be specified in an element called "_id"
|
||||||
|
(the correct one is "id"). Previously _id was allowed because some early FHIR examples
|
||||||
|
used that form, but this was never actually valid so it is now being removed.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.5" date="2016-04-20">
|
<release version="1.5" date="2016-04-20">
|
||||||
<action type="fix" issue="339">
|
<action type="fix" issue="339">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user