I wrote a custom Spring formatter (which implements the org.springframework.format.Formatter
interface) for converting form input values to BigDecimal
values for dollar inputs. The formatter does not accept values with more than two digits after the decimal place. In that case, a ParseException
is thrown by the formatter's parse()
method.
public class InputDollarBigDecimalFormatter
implements Formatter<BigDecimal>
{
@Override
public BigDecimal parse(String text, Locale locale)
throws ParseException
{
// ...
}
@Override
public String print(BigDecimal amount, Locale locale)
{
// ...
}
}
The formatter is registered so it can be applied using an annotation named @InputDollarBigDecimalFormat
.
A field in my form-backing object, to which the formatter is applied, looks like this:
@InputDollarBigDecimalFormat
private BigDecimal price;
The formatter is working fine.
The problem is that when the ParseException
is thrown, Spring still attempts to convert the input value to a BigDecimal
using the default conversion. This means an input value of 100.123
is still successfully converted to a BigDecimal
, even though my custom formatter throws a ParseException
.
How can I prevent Spring from converting the input value to a BigDecimal
when my custom Formatter has rejected the value by throwing a ParseException
?
I'm using Spring 3.1.0.
Spring formatters are documented here.
UPDATE:
After stepping through the code with a debugger, I see that the logic for this is in the org.springframework.beans.TypeConverterDelegate
class. Spring is clearly doing this on purpose, so I can see only the following possible solutions:
(1) If possible, un-register the default PropertyEditor
for BigDecimal
values. It appears Spring registers a org.springframework.beans.propertyeditors.CustomNumberEditor
for converting a string to a BigDecimal
. Of course, this solution has the downside that the default PropertyEditor
would not be available for other BigDecimal
fields (that do not use the custom formatter).
(2) Create a Dollar
class that wraps a BigDecimal
and change the custom formatter to work with the Dollar
class. The type of field would also have to change to Dollar
. This would be a bit of a nuisance when working with the values.
(3) Perhaps Spring has realized it is incorrect to fall back to the default PropertyEditor
when a custom formatter has rejected a value and so this has been changed in a more recent version of Spring. I'm doubtful, but if anyone knows either way, your help would be appreciated.
(4) But perhaps the correct solution is to view the custom formatter as just helping out the default PropertyEditor
by allowing more formats. In addtion to limiting the value to two decimal places, my custom formatter also allows for the input value to include a dollar sign and commas. I could leave that logic, but remove the decimal place restriction. Then I could add a custom bean validation (JSR 303) constraint that rejects the value if it does not have exactly two digits after the decimal place (unless it has none). I would just have to annotate my field with the constraint:
@InputDollarBigDecimalFormat
@WholeDollarOrCentsConstraint
private BigDecimal price;
Of course, I would also have to add an error message for the constraint, in addition to the one for the formatter.
If any of these seems like the correct solution to you, feel free to add an answer with your reasoning.