Finish off #152
This commit is contained in:
parent
93c04bd939
commit
48917684fd
|
@ -4,6 +4,8 @@ import java.io.File;
|
|||
import java.io.FileReader;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -12,6 +14,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.ContactPointSystemEnum;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
@ -19,23 +22,54 @@ import ca.uhn.fhir.validation.FhirValidator;
|
|||
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ValidatorExamples {
|
||||
|
||||
// START SNIPPET: serverValidation
|
||||
public class MyRestfulServer extends RestfulServer {
|
||||
|
||||
@Override
|
||||
protected void initialize() throws ServletException {
|
||||
// ...Configure resource providers, etc...
|
||||
|
||||
// Create a context, set the error handler and instruct
|
||||
// the server to use it
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
ctx.setParserErrorHandler(new StrictErrorHandler());
|
||||
setFhirContext(ctx);
|
||||
}
|
||||
|
||||
}
|
||||
// END SNIPPET: serverValidation
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void enableValidation() {
|
||||
// START SNIPPET: enableValidation
|
||||
// START SNIPPET: clientValidation
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
|
||||
ctx.setParserErrorHandler(new StrictErrorHandler());
|
||||
|
||||
// This client will have strict parser validation enabled
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
|
||||
// END SNIPPET: clientValidation
|
||||
|
||||
// This server will have strict parser validation enabled
|
||||
RestfulServer server = new RestfulServer();
|
||||
server.setFhirContext(ctx);
|
||||
}
|
||||
|
||||
// END SNIPPET: enableValidation
|
||||
@SuppressWarnings("unused")
|
||||
public void parserValidation() {
|
||||
// START SNIPPET: parserValidation
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
|
||||
// Create a parser and configure it to use the strict error handler
|
||||
IParser parser = ctx.newXmlParser();
|
||||
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||
|
||||
// This example resource is invalid, as Patient.active can not repeat
|
||||
String input = "<Patient><active value=\"true\"/><active value=\"false\"/></Patient>";
|
||||
|
||||
// The following will throw a DataFormatException because of the StrictErrorHandler
|
||||
parser.parseResource(Patient.class, input);
|
||||
// END SNIPPET: parserValidation
|
||||
}
|
||||
|
||||
public void validateResource() {
|
||||
|
|
|
@ -35,4 +35,9 @@ public class ErrorHandlerAdapter implements IParserErrorHandler {
|
|||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,16 @@ package ca.uhn.fhir.parser;
|
|||
*/
|
||||
public interface IParserErrorHandler {
|
||||
|
||||
/**
|
||||
* Invoked when an element repetition (e.g. a second repetition of something) is found for a field
|
||||
* which is non-repeating.
|
||||
*
|
||||
* @param theLocation The location in the document. WILL ALWAYS BE NULL currently, as this is not yet implemented, but this parameter is included so that locations can be added in the future without changing the API.
|
||||
* @param theElementName The name of the element that was found.
|
||||
* @since 1.2
|
||||
*/
|
||||
void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName);
|
||||
|
||||
/**
|
||||
* Invoked when an unknown element is found in the document.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.parser;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
|
@ -24,19 +26,49 @@ package ca.uhn.fhir.parser;
|
|||
* The default error handler, which logs issues but does not abort parsing
|
||||
*
|
||||
* @see IParser#setParserErrorHandler(IParserErrorHandler)
|
||||
* @see FhirContext#setParserErrorHandler(IParserErrorHandler)
|
||||
*/
|
||||
public class LenientErrorHandler implements IParserErrorHandler {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LenientErrorHandler.class);
|
||||
private boolean myLogErrors;
|
||||
|
||||
/**
|
||||
* Constructor which configures this handler to log all errors
|
||||
*/
|
||||
public LenientErrorHandler() {
|
||||
myLogErrors = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theLogErrors Should errors be logged?
|
||||
* @since 1.2
|
||||
*/
|
||||
public LenientErrorHandler(boolean theLogErrors) {
|
||||
myLogErrors = theLogErrors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unknownElement(IParseLocation theLocation, String theElementName) {
|
||||
if (myLogErrors) {
|
||||
ourLog.warn("Unknown element '{}' found while parsing", theElementName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unknownAttribute(IParseLocation theLocation, String theElementName) {
|
||||
if (myLogErrors) {
|
||||
ourLog.warn("Unknown attribute '{}' found while parsing", theElementName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
|
||||
if (myLogErrors) {
|
||||
ourLog.warn("Multiple repetitions of non-repeatable element '{}' found while parsing", theElementName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,8 +24,10 @@ import static org.apache.commons.lang3.StringUtils.*;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.xml.stream.events.StartElement;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
|
@ -1506,6 +1508,7 @@ class ParserState<T> {
|
|||
|
||||
private BaseRuntimeElementCompositeDefinition<?> myDefinition;
|
||||
private IBase myInstance;
|
||||
private Set<String> myParsedNonRepeatableNames = new HashSet<String>();
|
||||
|
||||
public ElementCompositeState(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IBase theInstance) {
|
||||
super(thePreResourceState);
|
||||
|
@ -1550,6 +1553,13 @@ class ParserState<T> {
|
|||
push(new SwallowChildrenWholeState(getPreResourceState()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (child.getMax() < 2 && !myParsedNonRepeatableNames.add(theChildName)) {
|
||||
myErrorHandler.unexpectedRepeatingElement(null, theChildName);
|
||||
push(new SwallowChildrenWholeState(getPreResourceState()));
|
||||
return;
|
||||
}
|
||||
|
||||
BaseRuntimeElementDefinition<?> target = child.getChildByName(theChildName);
|
||||
if (target == null) {
|
||||
// This is a bug with the structures and shouldn't happen..
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.parser;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
|
@ -25,6 +27,7 @@ package ca.uhn.fhir.parser;
|
|||
* issue is found while parsing.
|
||||
*
|
||||
* @see IParser#setParserErrorHandler(IParserErrorHandler)
|
||||
* @see FhirContext#setParserErrorHandler(IParserErrorHandler)
|
||||
*/
|
||||
public class StrictErrorHandler implements IParserErrorHandler {
|
||||
|
||||
|
@ -38,4 +41,10 @@ public class StrictErrorHandler implements IParserErrorHandler {
|
|||
throw new DataFormatException("Unknown attribute '" + theAttributeName + "' found during parse");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
|
||||
throw new DataFormatException("Multiple repetitions of non-repeatable element '" + theElementName + "' found during parse");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -90,6 +90,11 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
|
|||
public void unknownElement(IParseLocation theLocation, String theElementName) {
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown element found: " + theElementName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Multiple repetitions of non-repeatable element found: " + theElementName);
|
||||
}
|
||||
});
|
||||
|
||||
FhirValidator validator = getContext().newValidator();
|
||||
|
|
|
@ -1719,6 +1719,20 @@ public class XmlParserTest {
|
|||
assertEquals(p.getName().get(0).getFamily().get(0).getValue(), "AAA");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseErrorHandlerDuplicateElement() {
|
||||
String input = "<Patient><active value=\"true\"/><active value=\"false\"/></Patient>";
|
||||
try {
|
||||
ourCtx.newXmlParser().setParserErrorHandler(new StrictErrorHandler()).parseResource(Patient.class, input);
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertThat(e.getMessage(), containsString("Multiple repetitions"));
|
||||
}
|
||||
|
||||
Patient p = ourCtx.newXmlParser().setParserErrorHandler(new LenientErrorHandler()).parseResource(Patient.class, input);
|
||||
assertEquals("true", p.getActive().getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseFeedWithListResource() throws ConfigurationException, DataFormatException, IOException {
|
||||
|
||||
|
|
|
@ -38,19 +38,14 @@
|
|||
takes an <code>IParserErrorHandler</code>, which is a callback that
|
||||
will be invoked any time a parse issue is detected.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="basicValidation" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
There are two implementations of <code>IParserErrorHandler</code> worth
|
||||
mentioning:
|
||||
mentioning.
|
||||
</p>
|
||||
<ul>
|
||||
<ul>
|
||||
<a href="./apidocs/ca/uhn/fhir/parser/LenientErrorHandler.html">LenientErrorHandler</a>
|
||||
logs any errors but does not abort parsing.
|
||||
logs any errors but does not abort parsing. This is the default.
|
||||
</ul>
|
||||
<ul>
|
||||
<a href="./apidocs/ca/uhn/fhir/parser/StrictErrorHandler.html">StrictErrorHandler</a>
|
||||
|
@ -58,9 +53,35 @@
|
|||
</ul>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
The following example shows how to configure a parser to use strict validation.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="parserValidation" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
You can also configure the error handler at the FhirContext level, which is useful
|
||||
for clients.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="clientValidation" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
FhirContext level validators can also be useful on servers.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="serverValidation" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- The body of the document contains a number of sections -->
|
||||
<!-- RESOURCE VALIDATION -->
|
||||
|
||||
<section name="Resource Validation">
|
||||
|
||||
<p>
|
||||
|
|
Loading…
Reference in New Issue