I am having a problem converting input String to money. I am creating a spring boot application with thymeleaf. I have a web page where user inputs the data and there is a particular field where he inputs the String and it needs to be converted to type joda.money as in my pojo class that field has money data type. This is a full governor_form.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>[[${pageTitleG}]]</title> <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}"/> </head> <body> <div class="container-fluid"> <div class="text-center"><h2>[[${pageTitleG}]]</h2></div> <form th:action="@{/governors/save}" method="post" th:object="${governor}" style="max-width: 550px; margin: 0 auto;"> <input type="hidden" th:field="*{idGovernor}"> <div class="border border-secondary rounded p-3"> <div class="form-group row"> <label class="col-sm-4 col-form-label">Full name:</label> <div class="col-sm-8"> <input type="text" th:field="*{fullName}" class="form-control" required minlength="5" maxlength="70"> </div> </div> <div class="form-group row"> <label class="col-sm-4 col-form-label">Age</label> <div class="col-sm-8"> <input type="number" step="0.01" th:field="*{age}" class="form-control" required> </div> </div> <div class="form-group row"> <label class="col-sm-4 col-form-label">Position</label> <div class="col-sm-8"> <input type="text" step="0.01" th:field="*{position}" class="form-control" required> </div> </div> <div class="form-group row"> <label class="col-sm-4 col-form-label">Date of Intercession</label> <div class="col-sm-8"> <input type="date" th:field="*{dateOfIntercession}" class="form-control"> </div> </div> <div class="form-group row"> <label class="col-sm-4 col-form-label">Date of Resignation</label> <div class="col-sm-8"> <input type="date" th:field="*{dateOfResignation}" class="form-control"> </div> </div> <div class="form-group row"> <label class="col-sm-4 col-form-label">Per Capita Income</label> <div class="col-sm-8"> <input type="number" step="0.01" th:field="*{perCapitaIncome}" class="form-control" required> </div> </div> <div class="form-group row"> <label class="col-sm-4 col-form-label">Population Below Poverty</label> <div class="col-sm-8"> <input type="number" step="0.01" th:field="*{populationBelowPoverty}" class="form-control" required min="0" max="99"> </div> </div> <div class="form-group row"> <label class="col-sm-4 col-form-label">State</label> <div class="col-sm-8"> <select th:field="*{state}" class="form-control" required> <th:block th:each="state : ${listStates}"> <option th:text="${state.officialStateName}" th:value="${state.idState}"/> </th:block> </select> </div> </div> <div class="text-center"> <button type="submit" class="btn btn-primary m-2">Save</button> <button type="button" class="btn btn-secondary m-2" onclick="cancelForm()">Cancel</button> </div> </div> </form> </div> <script type="text/javascript"> function cancelForm() { window.location = "[[@{/governors}]]"; } </script> </body> </html>
This is where he inputs that data:
<div class="form-group row"> <label class="col-sm-4 col-form-label">Per Capita Income</label> <div class="col-sm-8"> <input type="number" step="0.01" th:field="*{perCapitaIncome}" class="form-control" required> </div> </div>
So if i’m not mistaken Hibernate is having a problem converting a String input from thymeleaf to type money as I get the following error:
: Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors<EOL>Field error in object 'governor' on field 'perCapitaIncome': rejected value [1100]; codes [typeMismatch.governor.perCapitaIncome,typeMismatch.perCapitaIncome,typeMismatch.org.joda.money.Money,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [governor.perCapitaIncome,perCapitaIncome]; arguments []; default message [perCapitaIncome]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.joda.money.Money' for property 'perCapitaIncome'; Cannot convert value of type 'java.lang.String' to required type 'org.joda.money.Money' for property 'perCapitaIncome': no matching editors or conversion strategy found]]
This is the joda.money
dependency:
<dependency> <groupId>org.joda</groupId> <artifactId>joda-money</artifactId> <version>1.0.1</version> </dependency>
This is a POJO class:
@Entity @Table(name = "governor") public class Governor implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id_governor") private Integer idGovernor; @Column(name = "pib") private String fullName; @Column(name = "age") private Integer age; @Column(name = "position") private String position; @Column(name = "date_of_intercession") private java.sql.Date dateOfIntercession; @Column(name = "date_of_resignation") private java.sql.Date dateOfResignation; @Column(name = "per_capita_income") private Money perCapitaIncome; @Column(name = "population_below_poverty") private Integer populationBelowPoverty; @ManyToOne @JoinColumn(name = "id_state", nullable = false) private State state; public State getState() {return state;} public void setState(State state) {this.state = state;} public Integer getIdGovernor() { return this.idGovernor; } public void setIdGovernor(Integer idGovernor) { this.idGovernor = idGovernor; } public String getFullName() { return this.fullName; } public void setFullName(String fullName) {this.fullName = fullName;} public Integer getAge() { return this.age; } public void setAge(Integer age) { this.age = age; } public String getPosition() { return this.position; } public void setPosition(String position) { this.position = position; } public java.sql.Date getDateOfIntercession() { return this.dateOfIntercession; } public void setDateOfIntercession(java.sql.Date dateOfIntercession) { this.dateOfIntercession = dateOfIntercession; } public java.sql.Date getDateOfResignation() { return this.dateOfResignation; } public void setDateOfResignation(java.sql.Date dateOfResignation) { this.dateOfResignation = dateOfResignation; } public Money getPerCapitaIncome() { return this.perCapitaIncome; } public void setPerCapitaIncome(Money perCapitaIncome) { this.perCapitaIncome = perCapitaIncome; } public Integer getPopulationBelowPoverty() { return this.populationBelowPoverty; } public void setPopulationBelowPoverty(Integer populationBelowPoverty) { this.populationBelowPoverty = populationBelowPoverty; } }
This is a controller:
@Controller public class GovernorController { @Autowired GovernorService governorService; @Autowired GovernorRepository governorRepository; @Autowired StateRepository stateRepository; @Autowired StateService stateService; @GetMapping("/governors") public String showAllGovernors(Model model){ List<Governor> listGovernors = governorService.findAllGovernors(); model.addAttribute("listGovernors", listGovernors); return "governors"; } @GetMapping("/governors/new") public String showNewGovernorForm(Model model){ List <State> listStates = stateService.findAll(); model.addAttribute("listStates", listStates); model.addAttribute("governor", new Governor()); model.addAttribute("pageTitleG", "Add New Governor"); return "governor_form"; } @PostMapping("/governors/save") public String saveGovernor (Governor requestGovernor, RedirectAttributes redirectAttributes){ governorRepository.save(requestGovernor); redirectAttributes.addFlashAttribute("messageG", "The governor has been saved successfully!"); return "redirect:/governors"; } @GetMapping("/governors/edit/{id}") public String showEditGovernorForm(@PathVariable("id") Integer id, Model model, RedirectAttributes redirectAttributes){ try { Governor governor = governorService.findGovernorById(id); List <State> listStates = stateService.findAll(); model.addAttribute("listStates", listStates); model.addAttribute("governor", governor); model.addAttribute("pageTitleG", "Edit Governor (ID: " + id + ")"); return "governor_form"; } catch (EntityNotFoundException e) { redirectAttributes.addFlashAttribute("messageG", e.getMessage()); return "redirect:/governors"; } } @GetMapping("/governors/delete/{id}") public String deleteGovernor(@PathVariable("id") Integer id, Model model, RedirectAttributes redirectAttributes){ try { governorService.deleteGovernor(id); redirectAttributes.addFlashAttribute("messageG", "The governor ID " + id + " has been deleted!"); } catch (StateNotFoundException e) { redirectAttributes.addFlashAttribute("messageG", e.getMessage()); } return "redirect:/governors"; }
}
How do I convert String to Money?
Advertisement
Answer
One solution would be to create custom deserilizer for Money field, and then use Jackson to set it.
Here is code snippet:
MoneyDeserializer.java :
public class MoneyDeserializer extends StdDeserializer<BigMoney> { private static final long serialVersionUID = 2518470236548239933L; public MoneyDeserializer() { super(Money.class); } @Override public BigMoney deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException, JsonProcessingException { return BigMoney.of(CurrencyUnit.USD, jp.readValueAs(Double.class)); } }
Governor.java
import com.example.demo.filter.MoneyDeserializer; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.joda.money.BigMoney; import org.joda.money.Money; import java.io.Serializable; public class Governor implements Serializable { private Integer idGovernor; private String fullName; private Integer age; private String position; private java.sql.Date dateOfIntercession; private java.sql.Date dateOfResignation; private BigMoney perCapitaIncome; private Integer populationBelowPoverty; public Integer getIdGovernor() { return this.idGovernor; } public void setIdGovernor(Integer idGovernor) { this.idGovernor = idGovernor; } public String getFullName() { return this.fullName; } public void setFullName(String fullName) {this.fullName = fullName;} public Integer getAge() { return this.age; } public void setAge(Integer age) { this.age = age; } public String getPosition() { return this.position; } public void setPosition(String position) { this.position = position; } public java.sql.Date getDateOfIntercession() { return this.dateOfIntercession; } public void setDateOfIntercession(java.sql.Date dateOfIntercession) { this.dateOfIntercession = dateOfIntercession; } public java.sql.Date getDateOfResignation() { return this.dateOfResignation; } public void setDateOfResignation(java.sql.Date dateOfResignation) { this.dateOfResignation = dateOfResignation; } public BigMoney getPerCapitaIncome() { return this.perCapitaIncome; } @JsonDeserialize(using = MoneyDeserializer.class) public void setPerCapitaIncome(BigMoney perCapitaIncome) { this.perCapitaIncome = perCapitaIncome; } public Integer getPopulationBelowPoverty() { return this.populationBelowPoverty; } public void setPopulationBelowPoverty(Integer populationBelowPoverty) { this.populationBelowPoverty = populationBelowPoverty; } @Override public String toString() { return "Governor{" + "idGovernor=" + idGovernor + ", fullName='" + fullName + ''' + ", age=" + age + ", position='" + position + ''' + ", dateOfIntercession=" + dateOfIntercession + ", dateOfResignation=" + dateOfResignation + ", perCapitaIncome=" + perCapitaIncome + ", populationBelowPoverty=" + populationBelowPoverty + '}'; } }
NOTE: On your form, you are passing age
field as Double, but in your class it’s declared as Integer
. So you will get exception during deserilization process.
Same thing applys for populationBelowPoverty
field . Also, your date format is dd/MM/YYYY
and I think this is not supported format for jackson. It should be YYYY-MM-dd
.
Anyway, for example, if you send a JSON like this:
{ "idGovernor":1, "fullName":"Test", "age":1, "dateOfIntercession":"2022-06-09", "dateOfResignation":"2022-06-17", "perCapitaIncome":"123.932", "position":"position", "populationBelowPoverty":"98" }
to this controller method:
@PostMapping(value = "/test/governor") public Governor testGovernor(@RequestBody Governor governor) { return governor; }
You should get response like this :
{ "idGovernor": 1, "fullName": "Test", "age": 1, "position": "position", "dateOfIntercession": "2022-06-09", "dateOfResignation": "2022-06-17", "perCapitaIncome": { "amount": 123.932, "zero": false, "negative": false, "positive": true, "amountMajorLong": 123, "negativeOrZero": false, "amountMinorLong": 12393, "amountMinor": 12393, "positiveOrZero": true, "minorPart": 93, "scale": 3, "amountMinorInt": 12393, "currencyUnit": { "code": "USD", "numericCode": 840, "decimalPlaces": 2, "symbol": "$", "numeric3Code": "840", "countryCodes": [ "PR", "MP", "IO", "FM", "PW", "GU", "BQ", "TC", "VG", "AS", "VI", "TL", "UM", "MH", "EC", "US" ], "pseudoCurrency": false }, "currencyScale": false, "amountMajorInt": 123, "amountMajor": 123 }, "populationBelowPoverty": 98 }
In your case, since you are using thymeleaf you can adjust this idea to your needs.