how to do validation with not well form XML while doing unmarshalling? - validation

I have an unmarshaller along with an MySchema.xsd file.
StreamSource sources = new StreamSource(getClass().getClassLoader().getResourceAsStream("/xmlValidation.xsd"));
SchemaFactory sf = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI );
unmarshaller.setSchema(sf.newSchema(sources));
And make a call to unmarshaller.setEventHandler() function, to specify a custom validation event handler, which basically format a error tips string , by:
final String errorString = new String();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent validationevent) {
if(validationevent.getSeverity()!= ValidationEvent.WARNING){
errorString.format( "Line:Col[" + validationevent.getLocator().getLineNumber()
+ ":" + validationevent.getLocator().getColumnNumber()
+ "]:" + validationevent.getMessage());
return false;
}
return true;
}
});
The above codes seem work ok(I can get java object when the input string is validated. and also the error tips string is formated as excepted)
The problem is that, when the input xml is not well form, it also throw a SaxParseException.
Thanks in advance.
Andrew

Well formed relates to the XML syntax itself, as opposed to being valid WRT an XML schema:
http://en.wikipedia.org/wiki/Well-formed_element
If you have XML that is not well formed then you will get a ValidationEvent.FATAL_ERROR and unmarshalling will not be able to continue, as the underlying parser used by JAXB cannot continue.
For more information:
http://bdoughan.blogspot.com/2010/12/jaxb-and-marshalunmarshal-schema.html

K, I mess up something and get this problem.
Now I figure it out. If I am wrong, please point me out. below it's what I find in javadoc and test on my project:
javax.xml.bind.ValidationEventHandler can handler the constrain error with the given schema constrains, when unmarshaller is unmarshaling.
unmarshaller.unmarshal(xmlInputStream);
The ValidationEventHandler will be called during the unmarshaling process if error occurs.
The SAXEception will be thrown, if the xmlInputStream is not well form.
And I cant find a way to catch the SAXException, throw by the sax parser, so I guess using validation during unmarshaling can't due with un-well form xml string.
I use javax.xml.validation.Validator to validate that the xml string is well form and under constrain.
jaxbValidator.validate(xmlSource);
The above code will throw SAXException.
If no exception is thrown, then unmarshall the xml string into object.

Related

Jackson XML "Undeclared general entity" caused by custom entity

I'm deserializing a large XML file (not mine) and it contains custom entities defined as:
<!ENTITY math "mathematics">
and elements used this way:
<field>&math;</field>
When I try to deserialize it by:
XmlMapper xmlMapper = new XmlMapper();
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
return xmlMapper.readValue(classloader.getResourceAsStream("file.xml"), MyClass.class);
I get this error:
com.fasterxml.jackson.databind.JsonMappingException: Undeclared general entity "math"
I think it might be a security measure to prevent Xml External Entity injections.
Is there a way to mark these custom entities as valid? Like create an Enum for them or something?
If not, is there a flag to just parse these as Strings?
Update:
I was able to work around this problem by basically doing a find-replace on the text file. It's quite an ugly solution and if anyone has a better idea, I'm all ears. :)
I know it may be a little late, but just in case someone else is stuck on the same issue:
You have to set a custom XMLResolver as XMLInputFactory's property:
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.ctc.wstx.api.WstxInputProperties;
import javax.xml.stream.XMLResolver;
import javax.xml.stream.XMLStreamException;
var xmlMapper = new XmlMapper();
xmlMapper.getFactory().getXMLInputFactory().setProperty(
WstxInputProperties.P_UNDECLARED_ENTITY_RESOLVER,
new XMLResolver() {
#Override
public Object resolveEntity(String publicId, String systemId, String baseUri, String ns) throws XMLStreamException {
// replace the entity with a string of your choice, e.g.
switch (ns) {
case "nbsp":
return " ";
default:
return "";
}
// some useful tool is org.apache.commons.text.StringEscapeUtils
// e.g.
// return StringEscapeUtils.escapeXml10(StringEscapeUtils.unescapeHtml4('&' + ns + ';'));
}
}
);
// then xmlMapper.readValue....

Find Error When Using Codec

I have a simple object named Tag that has an id, name, and three numeric properties. I also have a codec for the object. The folloing code executes without error.
MongoDatabase tagsDatabase =
usersProcess.getMongoClient().getDatabase(tagsDB)
.withCodecRegistry(usersProcess.getCodecRegistry());
MongoCollection<Tag> tagsCollection =
tagsDatabase.getCollection(tagsCollectionName, Tag.class);
ArrayList<Tag> tagsList = new ArrayList<Tag>();
FindIterable<Tag> tagsByAlpha =
tagsCollection.find().sort(Sorts.ascending("name"));
Following this, the code
tagsByAlpha.forEach(new Consumer<Tag>() {
#Override
public void accept(Tag t) {
tagsList.add(t);
}
});
thows the exception "org.bson.BsonInvalidOperationException: readEndArray can only be called when ContextType is ARRAY, not when ContextType is DOCUMENT" at the first line (forEach). An alternative construct
MongoCursor<Tag> tagsCursor = tagsByAlpha.iterator();
throws the same exception. It seems to be implying that find() has returned Documents rather than Tag objects. At the same time, the code that does work suggests that what I'm trying is possible. What am I doing wrong?
Should have used org.bson.codecs.DoubleCodec

AWS Lex receives Invalid Response from lambda function - Can not construct instance of IntentResponse

Using Java8 in eclipse AWS SDK, I've created and uploaded a lambda function that is hooked in upon fulfillment of my lex intent.
Lambda has not problem receiving JSON request and parsing.
Then, I format a simple "Close" dialogAction response and send back to lex and receive the following error from the Test Bot page in the lex console:
An error has occurred: Received invalid response from Lambda:
Can not construct instance of IntentResponse:
no String-argument constructor/factory method to deserialize
from String value
('{"dialogAction
{"type":"Close","fulfillmentState":"Fulfilled","message":
{"contentType":"PlainText","content":"Thanks I got your info"}}}')
at [Source: "{\"dialogAction\":
{\"type\":\"Close\",\"fulfillmentState\":\"Fulfilled\",\"message\":
{\"contentType\":\"PlainText\",\"content\":\"Thanks I got your
info\"}}}";line: 1, column: 1]
It seems to have a problem right away with the format (line 1, column 1), but my JSON string looks ok to me. Before returning the output string in the handleRequest java function, I am writing the it to the Cloudwatch log and it writes as follows:
{
"dialogAction": {
"type": "Close",
"fulfillmentState": "Fulfilled",
"message": {
"contentType": "PlainText",
"content": "Thanks I got your info"
}
}
}
Things I've tried:
Removing the message element as it's not required
Adding in non-required properties like sessionAttributes,
responseCard, etc
removing the double quotes
replacing double quotes with single quotes
hardcoding json from sample response format message in documentation
Is there something hidden at the http headers level or is java8 doing something to the JSON that is not visible?
Not sure if this is because I'm using Java8 or not, but a return value of "String" from the RequestHandler class handleRequest method will not work.
Yes, String is an object, but the constructors on the Lex side are expecting an "Object". I was converting my lex response POJO to a String before returning it in the handleRequest method. That was my mistake.
I fixed it by changing the return type of the handleRequest method to be "Object" instead of "String".
public Object handleRequest(Object input, Context context)
instead of
public String handleRequest(Object input, Context context)
You also have to implement the
public class LambdaFunctionHandler implements RequestHandler<Object, Object>
not
public class LambdaFunctionHandler implements RequestHandler<Object, String>
This solved my issue.
In my case I was facing exactly the same issue and was able to fix it by creating specific response POJO type and using this POJO as the return type for 'handleRequest' method. E.g. BotResponse.java as follow:
public class BotResponse implements Serializable{
private static final long serialVersionUID = 1L;
public DialogAction dialogAction = new DialogAction();
public DialogAction getDialogAction() {
return dialogAction;
}
public void setDialogAction(DialogAction dialogAction) {
this.dialogAction = dialogAction;
}
}
Note, I have also added the 'implements Serializable' just to be on safer side. Probably it is an overkill.
Not sure why but for me returning a well formatted JSON String object did not worked even after changing the return type of 'handleRequest' method to 'Object'.
I know this is an old question however thought this might help some else
#Mattbob Solution dint fix my issue, However he is in the right path. Best approach is to use a Response object, a custom response object and make the lambda return the custom response object. So i went to the Documentation and created a custom object that looks Response format here
http://docs.aws.amazon.com/lex/latest/dg/lambda-input-response-format.html
At the time of answering question i couldnt find an object in SDK that matched the response Object so i had to recreate but if some one knows please comment below
Class xxxxx implements RequestHandler<Object, AccountResponse> {
#Override
public AccountResponse handleRequest(Object input, Context context) {
}
}
Lambda will look somewhat like this and just populate and return the object to match response structure and error goes away. Hope this helps.
Whenever we are returning the object to the bot from the backend make sure we need to pass content type along with content. But here we are passing wrong. So wE need to pass as like below. It is in Node.js
let message = {
contentType: "PlainText",
content: 'Testing bot'
};

Access to pre-interpolated bean validation message template in SpringMVC?

I'm using Spring Validation within a Spring MVC application that delegates validation to Hibernate Validator 5. I'm successfully able to have beans validated and have the messages interpolated by the validator. However, it's important that I also be able to have access to the message template itself, pre-interpolation.
For example, in some bean I have validation #Size(min=5,max=15,message="{my.custom.message}". In a messages.properties file I have entry my.custom.message=test min {min} and max {max}. In my BindingResult, I see the ObjectError with error message "test min 5 and max 15", but I need to look a value up at this point based on the non-interpolated my.custom.message raw value.
Can this be done? If it can't out of the box, can someone point me in the right direction for how I might customize spring's LocalValidatorFactoryBean to preserve this?
Update
I'm looking at extending org.springframework.validation.beanvalidation.SpringValidatorAdapter, and wrapping the getArgumentsForConstraint to automatically append the pre-interpolated message to the returned list of arguments. The notion of exactly what these 'arguments' are and how they're used is unclear to me, but if it's purely used for message interpolation, it seems relatively safe for me to append at the end. Any reason this might not work? Problems it might cause? Better ideas?
Solution
Didn't find any great solutions other than my 'update' above, so I ended up subclassing LocalValidatorFactoryBean with this:
#Override
protected Object[] getArgumentsForConstraint(String objectName, String field, ConstraintDescriptor<?> descriptor) {
if (null == descriptor) return super.getArgumentsForConstraint(objectName, field, descriptor);
Object[] orig = super.getArgumentsForConstraint(objectName, field, descriptor);
if (null == orig || orig.length < 1) return new Object[] { descriptor };
Object[] retval = new Object[orig.length+1];
System.arraycopy(orig, 0, retval, 0, orig.length);
retval[retval.length-1] = descriptor;
return retval;
}
In subsequent code, I look at the last object in this array and test to see if it's an instance of ConstraintDescriptor. Good enough I suppose.
However, it's important that I also be able to have access to the message template itself, pre-interpolation.
In which context do you need to access the template? If it is after validation, then getMessageTemplate() on ConstraintViolation gives you this. If it is within a constraint validator implementation, then you could use getDefaultConstraintMessageTemplate() on ConstraintValidatorContext.

jaxb validation event locator - row and col number of validation error

When doing JAXB marshalling I am collecting validation error and also want to get the line number and column number where the error occurs. I keep getting columnNumber=-1 and lineNumber=-1 for all the errors. Is there anything that I am forgetting?
Code sample:
Marshaller marshaler = jaxbCtx.createMarshaller();
marshaler.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
ValidationErrorCollector errorCollector = new ValidationErrorCollector();
marshaler.setEventHandler(errorCollector);
marshaler.setSchema(getSchema());
marshaler.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());
JAXBElement<RootObject> jaxbElement = new JAXBElement<RootObject>(ROOT_QNAME, RootObject.class, (RootObject) rootObject);
marshaler.marshal(jaxbElement, new StringWriter());
for (ValidationEvent validationEvent : errorCollector.getValidationEvents()) {
validationEvent.getLocator().getColumnNumber(); // returns -1
validationEvent.getLocator().getLineNumber(); // returns -1
...
public class ValidationErrorCollector implements ValidationEventHandler {
/** List of validation events (with validation errors). */
private List<ValidationEvent> validationEvents = new ArrayList<ValidationEvent>();
#Override
public boolean handleEvent(ValidationEvent validationEvent) {
// record the validation error
validationEvents.add(validationEvent);
// let validation continue
return true;
}
That is the expected behavior. When you're marshaling the source of the error comes from the object being marshalled. The object can also be found through the event.
For more information see:
http://bdoughan.blogspot.com/2010/12/jaxb-and-marshalunmarshal-schema.html
Which column and line number (in which file) do you expect to retrieve?
I think this is by design. Line number and column number are for ValidationEvent which happen on unmarshalling. In such a case you get the position of the problem in the XML file to be unmarshalled.
But in your case you are trying to marshall an object - hence there is no XML file and therefore you don't get a column or a line number.

Resources