Spring new object Binding Exception: Cannot convert String to long (primitive type) - spring

OS: Windows vista, Framework: Spring (latest), JQuery (latest), Hibernate (latest).
I have a domain class with primary key as long id.
public class domain{
private long id;
....
}
My Controller definition:
#RequestMapping("/domainJqgridData/save")
public #ResponseBody String saveJqgridData(DomainClass domainclass) throws Exception {
return "Saved successfully!";
}
When the JSP form is submitted to add a new DomainClass record, the Spring controller tries to automatically bind the request parameters to domain class. It throws a BindException as follows:
Request processing failed; nested exception is org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'domain' on field 'id': rejected value [_empty]; codes [typeMismatch.domainclass.id,typeMismatch.id,typeMismatch.long,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [domainclass.id,id]; arguments []; default message [id]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'long' for property 'id'; nested exception is org.springframework.core.convert.ConversionFailedException: Unable to convert value "_empty" from type 'java.lang.String' to type 'long'; nested exception is java.lang.NumberFormatException: For input string: "_empty"]
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:656)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
As I am adding a new DomainClass record, the id field is passed as null by the JSP form. Spring converts the null id to empty string value for binding purpose and throws the error. I browsed the net and found that I can register custom editors for such purpose. I changed the DomainClass primitive type definition long id, to Long id and tried to bind a custom editor as follows.
Controller class:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Long.class, new CustomPrimitiveFormat(Long.class, true));
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
My custom primitive editor class is as follows:
public class CustomPrimitiveFormat extends CustomNumberEditor{
public CustomPrimitiveFormat(Class numberClass, boolean allowEmpty)
throws IllegalArgumentException {
super(numberClass, allowEmpty);
// TODO Auto-generated constructor stub
}
public void setValue(Object value){
System.out.println("Entered CustomPrimitiveFormat setValue");
if (value == null) {
super.setValue(null);
return;
}
if (value.getClass().equals(String.class)){
if (StringUtils.isEmpty((String)value)){
super.setValue(null);
}
}
}
public void setAsText(Object value){
System.out.println("Entered CustomPrimitiveFormat setAsText");
if (value == null) {
super.setValue(null);
return;
}
if (value.getClass().equals(String.class)){
if (StringUtils.isEmpty((String)value)){
super.setValue(null);
}
}
}
}
I still receive the BindingException. Could not find any link that would guide me through how to overcome Spring BindException when adding a new Domain class record. I would like my primary key to remain primitive type, instead of using the Number object type.
Thanks in advance for your help.

As you can see in the error message, jqGrid uses _empty as an id of the new record (also see here), so you need to change your PropertyEditor to convert _empty to null.

Related

Spring validation: How to validate that a field in RequestBody is a string and not a number being casted [duplicate]

I want to identify numerical values inserted without quotation marks (as strings) in JSON sent through the request body of a POST request:
For example, this would be the wrong JSON format as the age field does not contain quotation marks:
{
"Student":{
"Name": "John",
"Age": 12
}
}
The correct JSON format would be:
{
"Student":{
"Name": "John",
"Age": "12"
}
}
In my code, I've defined the datatype of the age field as a String, hence "12" should be the correct input. However, no error message is thrown, even when 12 is used.
It seems Jackson automatically converts the numerical values into strings. How can I identify numerical values and return a message?
This is what I tried so far to identify these numerical values:
public List<Student> getMultiple(StudentDTO Student) {
if(Student.getAge().getClass()==String.class) {
System.out.println("Age entered correctly as String");
} else{
System.out.println("Please insert age value inside inverted commas");
}
}
However, this is not printing "Please insert age value inside inverted commas" to the console when the age is inserted without quotation marks.
If you're using Spring boot, by default it uses Jackson to parse JSON. There's no configuration option within Jackson to disable this feature, as mentioned within this issue. The solution is to register a custom JsonDeserializer that throws an exception as soon as it encounters any other token than JsonToken.VALUE_STRING
public class StringOnlyDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
if (!JsonToken.VALUE_STRING.equals(jsonParser.getCurrentToken())) {
throw deserializationContext.wrongTokenException(jsonParser, String.class, JsonToken.VALUE_STRING, "No type conversion is allowed, string expected");
} else {
return jsonParser.getValueAsString();
}
}
}
If you only want to apply this to certain classes or fields, you can annotate those with the #JsonDeserialize annotation. For example:
public class Student {
private String name;
#JsonDeserialize(using = StringOnlyDeserializer.class)
private String age;
// TODO: Getters + Setters
}
Alternatively, you can register a custom Jackson module by registering a SimpleModule bean that automatically deserializes all strings using the StringOnlyDeserializer. For example:
#Bean
public Module customModule() {
SimpleModule customModule = new SimpleModule();
customModule.addDeserializer(String.class, new StringOnlyDeserializer());
return customModule;
}
This is similar to what Eugen suggested.
If you run your application now, and you pass an invalid age, such as 12, 12.3 or [12]it will throw an exception with a message like:
JSON parse error: Unexpected token (VALUE_NUMBER_FLOAT), expected VALUE_STRING: Not allowed to parse numbers to string; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_NUMBER_FLOAT), expected VALUE_STRING: Not allowed to parse numbers to string\n at [Source: (PushbackInputStream); line: 3, column: 9] (through reference chain: com.example.xyz.Student[\"age\"])
By default, Jackson converts the scalar values to String when the target field is of String type. The idea is to create a custom deserializer for String type and comment out the conversion part:
package jackson.deserializer;
import java.io.IOException;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
public class CustomStringDeserializer extends StringDeserializer
{
public final static CustomStringDeserializer instance = new CustomStringDeserializer();
#Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.hasToken(JsonToken.VALUE_STRING)) {
return p.getText();
}
JsonToken t = p.getCurrentToken();
// [databind#381]
if (t == JsonToken.START_ARRAY) {
return _deserializeFromArray(p, ctxt);
}
// need to gracefully handle byte[] data, as base64
if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
Object ob = p.getEmbeddedObject();
if (ob == null) {
return null;
}
if (ob instanceof byte[]) {
return ctxt.getBase64Variant().encode((byte[]) ob, false);
}
// otherwise, try conversion using toString()...
return ob.toString();
}
// allow coercions for other scalar types
// 17-Jan-2018, tatu: Related to [databind#1853] avoid FIELD_NAME by ensuring it's
// "real" scalar
/*if (t.isScalarValue()) {
String text = p.getValueAsString();
if (text != null) {
return text;
}
}*/
return (String) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
Now register this deserializer:
#Bean
public Module customStringDeserializer() {
SimpleModule module = new SimpleModule();
module.addDeserializer(String.class, CustomStringDeserializer.instance);
return module;
}
When an integer is send and String is expected, here is the error:
{"timestamp":"2019-04-24T15:15:58.968+0000","status":400,"error":"Bad
Request","message":"JSON parse error: Cannot deserialize instance of
java.lang.String out of VALUE_NUMBER_INT token; nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
deserialize instance of java.lang.String out of VALUE_NUMBER_INT
token\n at [Source: (PushbackInputStream); line: 3, column: 13]
(through reference chain:
org.hello.model.Student[\"age\"])","path":"/hello/echo"}

MessageHandlingException on a filter when using spring-integration 5.1.3.RELEASE

So I'm trying to run a test that runs through the following flow:
#EnableBinding({Sink.class,Source.class})
public class MyFlow {
public #Bean IntegrationFlow myIntegrationFlow() {
return IntegrationFlows.from("input") //
.transform(new JsonToObjectTransformer()) //
.filter(new MessageDroolsFilter())
.........
get();
}
public class MessageDroolsFilter implements MessageSelector {
#Override
public boolean accept(Message<?> message) {
return true;
}
}
}
So in spring-integration-core-5.1.2.RELEASE everything runs fine.
I want to upgrade to 5.1.3.RELEASE and I get the following exception. I can't see why it impacts the flow.
org.springframework.messaging.MessageHandlingException:
nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1004E:
Method call: Method accept(com.example.MyClass)
cannot be found on type com.example.myIntegrationFlow$$Lambda$942/1009480482, failedMessage=GenericMessage
[payload=com.example.MyClasst#72bd8702[id=100, timestamp=1563801840597}]
at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:109)
at org.springframework.integration.filter.AbstractMessageProcessingSelector.accept(AbstractMessageProcessingSelector.java:62)
at org.springframework.integration.router.RecipientListRouter$Recipient.accept(RecipientListRouter.java:320)
at org.springframework.integration.router.RecipientListRouter.lambda$determineTargetChannels$0(RecipientListRouter.java:258)
at java.util.stream.ReferencePipeline$2$1.accept(Unknown Source)
Edit:
I have a router and I want route messages based on type of the object received.
private IntegrationFlow messagesFlow() {
return sf -> sf //
.routeToRecipients(routeMessages());
}
private Consumer<RecipientListRouterSpec> routeMessages() {
return sf -> sf
.recipientFlow(new GenericSelector<MyObject1>() {
#Override
public boolean accept(MyObject1 source) {
return source instanceof MyObject1;
}},
f -> f.transform(myTransformer)
.filter(new DiscardHeaderMessageFilter())
.handle(myHandler))
.recipientFlow(new GenericSelector<MyObject2>() {
#Override
public boolean accept(MyObject2 source) {
return source instanceof MyObject2;
}
}
.defaultOutputChannel(DISCARD_CHANNEL);
}
I still get the same error:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method accept(com.example.MyObject1) cannot be found on type com.example.MyFlow$2
at org.springframework.expression.spel.ast.MethodReference.findAccessorForMethod(MethodReference.java:225)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:134)
at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:54)
at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:390)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:90)
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:114)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:365)
at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:172)
at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:160)
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.invokeExpression(MessagingMethodInvokerHelper.java:664)
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.invokeHandlerMethod(MessagingMethodInvokerHelper.java:655)
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.processInternal(MessagingMethodInvokerHelper.java:491)
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.process(MessagingMethodInvokerHelper.java:362)
at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:106)
Doesn't look like your problem is related to the mentioned .filter().
See stack trace carefully:
at org.springframework.integration.filter.AbstractMessageProcessingSelector.accept(AbstractMessageProcessingSelector.java:62)
at org.springframework.integration.router.RecipientListRouter$Recipient.accept(RecipientListRouter.java:320)
So, you have a routeToRecipients() somewhere and one of its recipient() doesn't fit expectations.
UPDATE
The error is expectable: a Recipient List Router consults with each recipient against the current message. When your message is MyObject1, the first one, .recipientFlow(new GenericSelector<MyObject1>(), works well because its method signature is compatible with object you call it. But when the same MyObject1 reaches the second one - .recipientFlow(new GenericSelector<MyObject2>() -, it can't call it because non-compatible type.
Fully unclear why would one does source instanceof MyObject1; in the method when argument is exactly MyObject1...
I would say that signature must be like this:
.recipientFlow(new GenericSelector<Object>() {
#Override
public boolean accept(Object source) {
return source instanceof MyObject1;
}}
I mean generic Object type compatible with any payload sent. It's fully unclear why your IDE didn't tell you that source instanceof MyObject2 is redundant since it is going always be true, when this method is called. Only the problem that this method fails against any other type when we call it via reflection, like in that case with SpEL.

spring mvc processing xml with relative path to dtd

My webservice receives an xml from a third-party source, which contains a !DOCTYPE declaration. Therefore I must use the second method in my controller to parse the xml document, the first one gives me this exception:
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not unmarshal to [class com.example.MeterBusXml]: null; nested exception is javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 48; DOCTYPE is disallowed when the feature "http://apache.org/xml/features/disallow-doctype-decl" set to true.]
I have no control over the application which posts the xml, so I must adapt my webservice to parse it with the dtd.
My question is, what is the spring framework's way of injecting the EntityResolver into every XMLReader instance?
#RestController
public class MeterBusDataController {
#RequestMapping (
consumes = APPLICATION_XML_VALUE,
method = POST,
path = "/meterbus1"
)
public void method1(#RequestBody MeterBusXml xml) {
System.out.println(xml);
}
#RequestMapping(
method = POST,
path = "/meterbus2"
)
public void method2(HttpServletRequest rq) throws IOException, ParserConfigurationException, SAXException, JAXBException {
JAXBContext jc = newInstance(MeterBusXml.class);
Unmarshaller um = jc.createUnmarshaller();
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(true);
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
xr.setEntityResolver(new EntityResolver() {
#Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
return new InputSource(new StringReader(""));
}
});
BufferedReader reader = rq.getReader();
InputSource inputSource = new InputSource(reader);
SAXSource saxSource = new SAXSource(xr, inputSource);
MeterBusXml xml = (MeterBusXml)um.unmarshal(saxSource);
System.out.println(xml);
}
}
See the following document for an example of the mbus.xml I'm trying to unmarshal.
http://prevodniky.sk/products/product_EthMBus_common/download/Ethernet_converters_exports_v1_02_EN.pdf
I've found the root of the problem. First I tried to create and configure a Jaxb2Marshaller bean, but that did not work out. Then I realized, I need a HttpMessageConverter, so I had to override the extendMessageConverters method in the WebMvcConfigurerAdapter class, and set the required properties on Jaxb2RootElementHttpMessageConverter. This message converter does not use a Jaxb2Marshaller, but it's internal workings are very similar.
setSupportDtd(true) is required, to force the parser to accept the !DOCTYPE declaration.
setProcessExternalEntities(false) is required, because if this property is false, then the converter uses a blank EntityResolver, just as I did in method2.
#Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void extendMessageConverters(List<HttpMessageConverter<Jaxb2RootElementHttpMessageConverter?>> converters) {
for (final Iterator<HttpMessageConverter<?>> iterator = converters.iterator(); iterator.hasNext();) {
HttpMessageConverter<?> next = iterator.next();
if (next instanceof Jaxb2RootElementHttpMessageConverter) {
Jaxb2RootElementHttpMessageConverter jaxbConverter = (Jaxb2RootElementHttpMessageConverter) next;
jaxbConverter.setProcessExternalEntities(false);
jaxbConverter.setSupportDtd(true);
}
}
}
}

XStreamMarshaller ignore unknown elements

After change data model on server side my XStream client throw Exception
com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$UnknownFieldException: No such field {fieldName}
To prevent this behavior I try to do something to ignore unknown elements.
I am using XStreamMarshaller from Spring-oxm 4.0.5 and XStream 1.4.5. I known that since XStream version 1.4.5 is available method ignoreUnknownElements().
XStreamMarshaller marshaller = new XStreamMarshaller();
marshaller.setStreamDriver(streamDriver);
marshaller.setAutodetectAnnotations(autodetectAnnotations);
marshaller.getXStream().ignoreUnknownElements();
Above solution doesn't work and I still get mentioned exception.
I have client side model copied from server.
For example:
public class Device implements Serializable {
protected String device_id;
protected String device_model_code;
protected String device_model_name;
protected String device_name;
//getters, setters
}
If I comment field for example device_model I will have Exception
com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$UnknownFieldException: No such field device_name
How can I solve my problem? How can I implement XStreamMarshaller to ignore unknown elements?
First thing is that method ignoreUnknownElements() available since XStream 1.4.5 is only for marshaling not for unmarshaling. So it doesn't work if somebody adds a new field in data model on server side.
To solve described problem you have to override protected method constructXStream() from org.springframework.oxm.xstream.XStreamMarshaller implementation:
public class CustomXStreamMarshaller extends XStreamMarshaller {
#Override
protected XStream constructXStream() {
// The referenced XStream constructor has been deprecated as of 1.4.5.
// We're preserving this call for broader XStream 1.4.x compatibility.
return new XStream() {
#Override
protected MapperWrapper wrapMapper(MapperWrapper next) {
return new MapperWrapper(next) {
#Override
public boolean shouldSerializeMember(Class definedIn, String fieldName) {
if (definedIn == Object.class) {
return false;
}
return super.shouldSerializeMember(definedIn, fieldName);
}
};
}
};
}}
You only need to use your own implementation in XStreamMarshaller declaration:
CustomXStreamMarshaller marshaller = new CustomXStreamMarshaller();
marshaller.setStreamDriver(streamDriver);
marshaller.setAutodetectAnnotations(autodetectAnnotations);

class not found exception while adding validator class

0 with eclipse and glassfish, in fact i have added a custom validator to validate emai address, and i am getting class not found exception. although i have registered in faces.config too.
here is my validator code
public static final String EmailPattern= "^[_A-Za-z0-9-]+(\\." +
"[_A-Za-z0-9-]+)*#[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*" +
"(\\.[A-Za-z]{2,})$";
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
matcher = pattern.matcher(value.toString());
if(!matcher.matches())
{
FacesMessage message = new FacesMessage("Please enter a valid email address");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
i registerd in faces.config
<validator>
<validator-id>emailvalidator</validator-id>
<validator-class>com.jsf.validators.EmailValidator</validator-class>
</validator>
does someone know what is the problem in it.

Resources