英文:
Thymeleaf + Spring Boot, dynamic fields with ArrayList
问题
以下是您提供的内容的翻译部分:
我正在使用Spring Boot与Thymeleaf,并尝试使用表单设置列表中的元素(这些元素是对象)。
更准确地说,我有两个实体:Question(它聚合了Answers列表),我将Question对象传递给模型,并尝试在迭代中设置每个Answer对象。在按下保存(提交表单)时,我遇到了异常:
> 对象'question'的字段错误,在字段'answers'上:被拒绝的值[[Answer(id=0, answerNumber=1, text=, correct=false),Answer(id=0, answerNumber=2, text=, correct=false),Answer(id=0, answerNumber=3, text=, correct=false),Answer(id=0, answerNumber=4, text=, correct=false)]];代码[typeMismatch.question.answers,typeMismatch.answers,typeMismatch.java.util.List,typeMismatch];参数[org.springframework.context.support.DefaultMessageSourceResolvable:代码[question.answers,answers];参数[];默认消息[answers];默认消息[无法将类型为'java.lang.String'的属性值转换为所需类型'java.util.List'以用于属性'answers';嵌套异常是java.lang.IllegalStateException:无法将类型为'java.lang.String'的值转换为属性'answers[0]'所需的类型'com.easetest.website.model.Answer':找不到匹配的编辑器或转换策略]]
我的控制器类的question方法
@PostMapping("/editQuestion")
public String editQuestion(Model model, @RequestParam("test_id") int id, @RequestParam(value = "question_id", required = false) Integer question_id) {
// ...
}
@PostMapping("/saveQuestion")
public String saveQuestion(Model model, @ModelAttribute("question") Question question, @RequestParam("test_id") int id) {
// ...
}
Question Form
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<!-- Bootstrap依赖项 -->
<!-- ... -->
</head>
<body class="mb-2 bg-secondary text-white">
<div class="container">
<!-- ... -->
</div>
</body>
</html>
Question实体
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Question {
// ...
}
Answer实体
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Answer {
// ...
}
有人知道是什么导致了这个错误,以及我如何解决它吗?
英文:
I'm using Spring Boot with Thymeleaf and trying to set elements (that are objects) of list using form.
More precisely I have 2 entities: Question that aggregates List of Answers, I'm passing Question object to the model and trying to set each Answer object in iteration. When pressing save (submiting form) i got exception:
> Field error in object 'question' on field 'answers': rejected value [[Answer(id=0, answerNumber=1, text=, correct=false), Answer(id=0, answerNumber=2, text=, correct=false), Answer(id=0, answerNumber=3, text=, correct=false), Answer(id=0, answerNumber=4, text=, correct=false)]]; codes [typeMismatch.question.answers,typeMismatch.answers,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [question.answers,answers]; arguments []; default message [answers]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'answers'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.easetest.website.model.Answer' for property 'answers[0]': no matching editors or conversion strategy found]]
My Controller class question methods
@PostMapping("/editQuestion")
public String editQuestion(Model model, @RequestParam("test_id") int id, @RequestParam(value = "question_id", required = false) Integer question_id) {
Test test = testService.getById(id);
List<Question> questions = test.getQuestions();
System.out.println(questions);
int realNumberOfQuestion = questions.size(); // mozliwe ze nie potrzebne
test.setMultipleAnswers(false);
Question q = question_id == null ? null : questionService.getById(question_id);
if(q == null) {
q = new Question();
if(realNumberOfQuestion < 1) {
q.setQuestionNumber(1);
} else if(questions.get(realNumberOfQuestion - 1).getQuestionBody().isEmpty()){
q = questions.get(realNumberOfQuestion - 1);
} else {
q.setQuestionNumber(realNumberOfQuestion);
}
}
model.addAttribute("test", test);
model.addAttribute("question", q);
System.out.println(q);
return "business/question_form";
}
@PostMapping("/saveQuestion")
public String saveQuestion(Model model, @ModelAttribute("question") Question question, @RequestParam("test_id") int id) {
System.out.println("Lubu dubu");
Test test = testService.getById(id);
test.setQuestion(question);
testService.save(test);
model.addAttribute("test", test);
model.addAttribute("question", question);
return "business/question_form";
}
Question Form
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<!--Bootstrap dependencies-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"
integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI"
crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script th:unless="${test.multipleAnswers}">
$(document).on('click', 'input[type="checkbox"]', function () {
$('input[type="checkbox"]').not(this).prop('checked', false);
});
</script>
<meta charset="UTF-8">
<title>Create Question</title>
</head>
<body class="mb-2 bg-secondary text-white">
<div class="container">
<div>
<h2 th:text="${test.testName}"></h2>
</div>
<div class="form-group">
<h4>Add question</h4>
<form action="#" th:action="@{/business/saveQuestion}" th:object="${question}" method="post">
<div class="form-group">
<input type="hidden" th:field="*{id}">
<input type="hidden" th:field="*{answers}">
<input type="hidden" th:name="test_id" th:value="${test.id}">
<div class="form-group">
<label for="exampleFormControlTextarea1">Question body</label>
<textarea class="form-control" id="exampleFormControlTextarea1"
th:value="${question.questionBody}" rows="3"></textarea>
</div>
<br>
<div th:each="answer, itemStat : *{answers}">
<div class="input-group mb-3">
<div class="input-group-prepend">
<div class="input-group-text">
<input type="hidden" th:field="*{answers[__${itemStat.index}__].id}">
<input type="hidden" th:field="*{answers[__${itemStat.index}__].answerNumber}">
<input type="checkbox" th:field="*{answers[__${itemStat.index}__].correct}">
</div>
</div>
<input type="text" class="form-control" th:field="*{answers[__${itemStat.index}__].text}">
</div>
</div>
</div>
<br>
<input type="submit" th:value="Save" class="btn btn-info">
</form>
</div>
<ul class="pagination">
<th:block th:each="question: ${test.questions}">
<form method="post" action="#" th:action="@{/business/editQuestion}">
<input type="hidden" th:name="test_id" th:value="${test.id}">
<input type="hidden" th:name="question_id" th:value="${question.id}">
<input type="submit" class="btn btn-danger" th:value=${question.questionNumber}>
</form>
</th:block>
<form method="post" action="#" th:action="@{/business/addQuestion}">
<input type="hidden" th:name="test_id" th:value="${test.id}">
<input type="hidden" th:name="question_id" th:value="${question.id}">
<input type="submit" class="btn btn-success" value="New Question">
</form>
</ul>
</div>
</body>
</html>
Question entity
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Question {
@Id
@GeneratedValue(strategy= GenerationType.AUTO, generator="native")
@GenericGenerator(name = "native", strategy = "native")
private int id;
@EqualsAndHashCode.Exclude
private int questionNumber;
@EqualsAndHashCode.Exclude
private String questionBody = "";
@EqualsAndHashCode.Exclude
@ToString.Exclude
@ManyToOne
@JoinColumn(name = "test_id", nullable = false)
private Test test;
@EqualsAndHashCode.Exclude
@OneToMany(cascade = CascadeType.ALL, mappedBy = "question", orphanRemoval = true)
private List<Answer> answers = Arrays.asList(
new Answer(1, "", false),
new Answer(2, "", false),
new Answer(3, "", false),
new Answer(4, "", false));
public Question(int questionNumber) {
this.questionNumber = questionNumber;
}
public void setAnswer(Integer num, String answer, boolean correct) {
System.out.println(answers);
if(answers == null) {
answers = new ArrayList<>();
}
Answer a = answers.get(num - 1) != null ? answers.get(num - 1) : new Answer();
a.setAnswerNumber(num);
a.setText(answer);
a.setCorrect(correct);
a.setQuestion(this);
answers.set(num-1, a);
}
public void removeAnswer(Integer num) {
if(answers == null) {
return;
}
answers.set(num-1, new Answer(num, "", false));
}
}
Answer entity
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Answer {
@Id
@GeneratedValue(strategy= GenerationType.AUTO, generator="native")
@GenericGenerator(name = "native", strategy = "native")
private int id;
@EqualsAndHashCode.Exclude
private int answerNumber;
@EqualsAndHashCode.Exclude
private String text;
@EqualsAndHashCode.Exclude
private boolean correct;
@EqualsAndHashCode.Exclude
@ToString.Exclude
@ManyToOne
@JoinColumn(name = "question_id")
private Question question;`enter code here`
public Answer(int answerNumber, String text, boolean correct) {
this.answerNumber = answerNumber;
this.text = text;
this.correct = correct;
}
}
Does anyone know what cause that error and how can I solve it?
答案1
得分: 0
找到解决方案。
错误消息中最重要的信息是:
无法将类型为 'java.lang.String' 的值转换为所需类型 'com.easetest.website.model.Answer'。
为了解决这个问题,我必须:
-
创建实现 Converter<String, Answer> 接口的转换器
-
在 WebConfig 中添加该转换器
下面是实现:
转换器类:
import com.easetest.website.model.Answer;
import lombok.RequiredArgsConstructor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class StringToAnswerConverter implements Converter<String, Answer> {
@Override
public Answer convert(String s) {
String[] data = s.split(",");
return new Answer(
Integer.parseInt(getSubstringAfterChar(data[1], '=')),
new String(getSubstringAfterChar(data[2], '=')),
Boolean.parseBoolean(getSubstringAfterChar(data[3], '=')));
}
private String getSubstringAfterChar(String text, char character) {
return text.substring(text.lastIndexOf(character) + 1);
}
}
WebConfig 类
import com.easetest.website.converters.StringToAnswerConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToAnswerConverter());
}
}
希望对某人有用。
英文:
Found solution.
The most important message of the error message is:
Cannot convert value of type 'java.lang.String' to required type 'com.easetest.website.model.Answer'.
To solve the problem I had to:
-
create converter that implements Converter<String, Answer>
-
add that converter in WebConfig
implementation below:
Converter class:
import com.easetest.website.model.Answer;
import lombok.RequiredArgsConstructor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class StringToAnswerConverter implements Converter<String, Answer> {
@Override
public Answer convert(String s) {
String[] data = s.split(",");
return new Answer(
Integer.parseInt(getSubstringAfterChar(data[1], '=')),
new String(getSubstringAfterChar(data[2], '=')),
Boolean.parseBoolean(getSubstringAfterChar(data[3], '=')));
}
private String getSubstringAfterChar(String text, char character) {
return text.substring(text.lastIndexOf(character) + 1);
}
}
WebConfig class
import com.easetest.website.converters.StringToAnswerConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToAnswerConverter());
}
}
Hope it will be usefull to somebody.
专注分享java语言的经验与见解,让所有开发者获益!
评论