Tweak to #1801 - Normalize double leading zeros in database (#1815)

This commit is contained in:
James Agnew 2020-04-23 09:44:08 -04:00 committed by GitHub
parent 371cda280a
commit 5f88d87027
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 37 deletions

View File

@ -1290,9 +1290,21 @@ class ParserState<T> {
ParseLocation location = ParseLocation.fromElementName(myChildName);
myErrorHandler.invalidValue(location, value, "Attribute value must not be empty (\"\")");
} else {
/*
* It may be possible to clean this up somewhat once the following PR is hopefully merged:
* https://github.com/FasterXML/jackson-core/pull/611
*
* See TolerantJsonParser
*/
if ("decimal".equals(myTypeName)) {
if (value != null && value.startsWith(".") && NumberUtils.isDigits(value.substring(1))) {
if (value != null)
if (value.startsWith(".") && NumberUtils.isDigits(value.substring(1))) {
value = "0" + value;
} else {
while (value.startsWith("00")) {
value = value.substring(1);
}
}
}
@ -1324,17 +1336,6 @@ class ParserState<T> {
pop();
}
// @Override
// public void enteringNewElementExtension(StartElement theElement,
// String theUrlAttr) {
// if (myInstance instanceof ISupportsUndeclaredExtensions) {
// UndeclaredExtension ext = new UndeclaredExtension(theUrlAttr);
// ((ISupportsUndeclaredExtensions)
// myInstance).getUndeclaredExtensions().add(ext);
// push(new ExtensionState(ext));
// }
// }
@Override
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
super.enteringNewElement(theNamespaceUri, theLocalPart);

View File

@ -2,5 +2,6 @@
type: fix
issue: 1801
title: "In previous versions of HAPI FHIR, the server incorrectly silently accepted decimal numbers in JSON with no
leading numbers (e.g. `.123`). These will now be rejected by the JSON parser. Any values with this string that
have previously been stored in the JPA server database will now automatically convert the value to `0.123`."
leading numbers (e.g. `.123`), as well as decimal numbers with more than one leading zero (e.g. 00.123). These will
now be rejected by the JSON parser. Any values with this string that have previously been stored in the JPA server
database will now automatically normalize the value to `0.123`."

View File

@ -25,17 +25,19 @@ import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParserErrorHandler;
import ca.uhn.fhir.parser.JsonParser;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.Map;
import static org.apache.commons.lang3.StringUtils.defaultString;
class TolerantJsonParser extends JsonParser {
private static final Logger ourLog = LoggerFactory.getLogger(TolerantJsonParser.class);
TolerantJsonParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
super(theContext, theParserErrorHandler);
}
@ -45,31 +47,33 @@ class TolerantJsonParser extends JsonParser {
try {
return super.parseResource(theResourceType, theMessageString);
} catch (DataFormatException e) {
if (defaultString(e.getMessage()).contains("Unexpected character ('.' (code 46))")) {
/*
* The following is a hacky and gross workaround until the following PR is hopefully merged:
* https://github.com/FasterXML/jackson-core/pull/611
*
* The issue this solves is that under Gson it was possible to store JSON containing
* decimal numbers with no leading integer, e.g. .123
* decimal numbers with no leading integer (e.g. .123) and numbers with double leading
* zeros (e.g. 000.123).
*
* These don't parse in Jackson, meaning we can be stuck with data in the database
* that can't be loaded back out.
* These don't parse in Jackson (which is valid behaviour, these aren't ok according to the
* JSON spec), meaning we can be stuck with data in the database that can't be loaded back out.
*
* Note that if we fix this in the future to rely on Jackson natively handing this
* nicely we may or may not be able to remove some code from
* ParserState.Primitive state too.
*/
String msg = defaultString(e.getMessage());
if (msg.contains("Unexpected character ('.' (code 46))") || msg.contains("Invalid numeric value: Leading zeroes not allowed")) {
Gson gson = new Gson();
JsonObject object = gson.fromJson(theMessageString, JsonObject.class);
String corrected = gson.toJson(object);
return super.parseResource(theResourceType, corrected);
}
throw e;
}
}

View File

@ -15,7 +15,7 @@ public class TolerantJsonParserR4Test {
private FhirContext myFhirContext = FhirContext.forR4();
@Test
public void testParseInvalidNumeric() {
public void testParseInvalidNumeric_LeadingDecimal() {
String input = "{\n" +
"\"resourceType\": \"Observation\",\n" +
"\"valueQuantity\": {\n" +
@ -30,6 +30,22 @@ public class TolerantJsonParserR4Test {
assertEquals("0.5", obs.getValueQuantity().getValueElement().getValueAsString());
}
@Test
public void testParseInvalidNumeric_LeadingZeros() {
String input = "{\n" +
"\"resourceType\": \"Observation\",\n" +
"\"valueQuantity\": {\n" +
" \"value\": 00.5\n" +
" }\n" +
"}";
TolerantJsonParser parser = new TolerantJsonParser(myFhirContext, new LenientErrorHandler());
Observation obs = parser.parseResource(Observation.class, input);
assertEquals("0.5", obs.getValueQuantity().getValueElement().getValueAsString());
}
@Test
public void testParseInvalidNumeric2() {
String input = "{\n" +