【问题标题】:Failed to convert property value of type java.lang.String[] to required type java.util.List无法将 java.lang.String[] 类型的属性值转换为所需的 java.util.List 类型
【发布时间】:2020-05-24 07:59:51
【问题描述】:

我正在关注 Spring in Action 5,在按下提交按钮后创建 Taco 模型时遇到问题。这是我设计的 Taco 控制器类:

    @GetMapping
public String showDesignForm(Model model){
    List<Ingredient> ingredients = new ArrayList<>();
    ingredientRepository.findAll().forEach(i -> ingredients.add(i));

    Type[] types = Ingredient.Type.values();
    for (Type type : types){
        model.addAttribute(type.toString().toLowerCase(),
                filterByType(ingredients, type));
    }
    return "welcomePage";
}
    @ModelAttribute(name = "taco")
public Taco taco(){
    return new Taco();
}

    @PostMapping
    public String processDesign(@Valid Taco taco, Errors errors, @ModelAttribute Order order){
        if(errors.hasErrors()) {
            return "welcomePage";
        }
        Taco saved = tacoRepository.save(taco);
        order.addDesign(saved);
        return "redirect:/orders/current";
    }

我捕捉到的错误信息:

    org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'taco' on field 'ingredients': rejected value [CARN]; codes [typeMismatch.taco.ingredients,typeMismatch.ingredients,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [taco.ingredients,ingredients]; arguments []; default message [ingredients]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'ingredients'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'org.server.models.Ingredient' for property 'ingredients[0]': no matching editors or conversion strategy found]

Taco 实体看起来像:

@Data
@Entity
public class Taco {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private Date createdAt;
    @NotNull
    @Size(min = 3, message="Name must be at least 3 characters long")
    private String name;
    @ManyToMany(targetEntity = Ingredient.class)
    @Size(min=1, message="You must choose at least 1 ingredient")
    private List<Ingredient> ingredients = new ArrayList<>();

    @PrePersist
    void createdAt(){
        this.createdAt = new Date();
    }

}

还有我的带有成分的实体:

@Data
@RequiredArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
@Entity
public class Ingredient {

    @Id
    private final String id;
    private final String name;
    @Enumerated(EnumType.STRING)
    private final Type type;

    public static enum Type{
        WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
    }
}

这是一个 html 页面,它必须使用挑选的成分创建新的 Taco 对象:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Testing Firs Page</title>
</head>
<body>
<h1>Design your taco!</h1>
<img th:src="@{/images/taco.jpg}" alt="myImage"/>

<form method="POST" th:object="${taco}">
    <span class="validationError"
          th:if="${#fields.hasErrors('ingredients')}"
          th:errors="*{ingredients}">Ingredient Error</span>

    <div class="grid">
        <div class="ingredient-group" id="wraps">
            <h3>Designate your wrap:</h3>
            <div th:each="ingredient : ${wrap}">
                <input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
                <span th:text="${ingredient.name}">INGREDIENT</span><br/>
            </div>
        </div>

        <div class="ingredient-group" id="proteins">
            <h3>Pick your protein:</h3>
            <div th:each="ingredient : ${protein}">
                <input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
                <span th:text="${ingredient.name}">INGREDIENT</span><br/>
            </div>
        </div>

        <div class="ingredient-group" id="cheeses">
            <h3>Choose your cheese:</h3>
            <div th:each="ingredient : ${cheese}">
                <input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
                <span th:text="${ingredient.name}">INGREDIENT</span><br/>
            </div>
        </div>

        <div class="ingredient-group" id="veggies">
            <h3>Determine your veggies:</h3>
            <div th:each="ingredient : ${veggies}">
                <input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
                <span th:text="${ingredient.name}">INGREDIENT</span><br/>
            </div>
        </div>

        <div class="ingredient-group" id="sauces">
            <h3>Select your sauce:</h3>
            <div th:each="ingredient : ${sauce}">
                <input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
                <span th:text="${ingredient.name}">INGREDIENT</span><br/>
            </div>
        </div>
    </div>

    <div>


        <h3>Name your taco creation:</h3>
        <input type="text" th:field="*{name}"/>
        <span class="validationError"
              th:if="${#fields.hasErrors('name')}"
              th:errors="*{name}">Name Error</span>
        <br/>

        <button>Submit your taco</button>
    </div>
</form>
</body>
</html>

我该如何解决?感谢您的提前。

【问题讨论】:

  • 能否请您也发布错误跟踪。上面的代码 sn-ps 也是来自两个不同的文件(控制器和模型)吗?如果是,请相应地拆分 sn-ps。
  • Syed Affan Hamdani 我已编辑错误消息并添加 taco、成分实体
  • @Scroll 您还没有向我们展示POST 有效负载,根据错误消息,它只有一个ingredients 字段的字符串。您如何期望 String 将字符串映射到 List&lt;Ingredient&gt;
  • @Andreas 我已经使用 POST 方法添加了 html 页面
  • @Scroll 所以ingredients 的值是一个成分ID 列表。当您没有以任何方式告知 Spring 字符串是简单的成分 ID 时,您希望 Spring 如何处理字符串到 Ingredient 对象的映射?您是否期望 Spring 会猜测?重新思考你在做什么。

标签: java spring


【解决方案1】:

https://github.com/habuma/spring-in-action-5-samples/blob/ff98b2ec36eeb627e4547713c8acbbd26a0eaa33/ch03/tacos-jdbc/src/main/java/tacos/web/IngredientByIdConverter.java

package tacos.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import tacos.Ingredient;
import tacos.data.IngredientRepository;

@Component
public class IngredientByIdConverter implements Converter<String, Ingredient> {

  private IngredientRepository ingredientRepo;

  @Autowired
  public IngredientByIdConverter(IngredientRepository ingredientRepo) {
    this.ingredientRepo = ingredientRepo;
  }
  
  @Override
  public Ingredient convert(String id) {
    return ingredientRepo.findById(id);
  }

}

【讨论】:

    【解决方案2】:

    在 Spring in Action 中,您应该添加 IngredientByIdConverter 类。这个类是将成分转换为字符串。

    @Component
        public class IngredientByIdConverter 
        implements Converter<String, Ingredient> {
    
        private IngredientRepository ingredientRepo;
    
        @Autowired
        public IngredientByIdConverter(IngredientRepository ingredientRepo) {
            this.ingredientRepo = ingredientRepo;
        }
    
        @Override
        public Ingredient convert(String id) {
            return ingredientRepo.findById(id);
        }
    }
    

    【讨论】:

      【解决方案3】:

      错误是:

      无法将java.lang.String 类型的值转换为ingredients[0] 属性所需的类型org.server.models.Ingredient

      您没有共享 TacoIngredient 的代码或 POST 请求的有效负载,因此我们无法确定您需要更改什么。

      但是,如果您向 Ingredient 添加一个带有 String 参数的构造函数,我相信 Spring 会使用它。

      如何从String 值创建Ingredient 对象当然取决于字符串内容是什么,因此这完全取决于您自己。如果您需要这方面的帮助,请创建一个新问题,并包含相关信息,例如您的 POJO 类的代码和 POST 请求的内容。

      【讨论】:

      • 我已编辑错误消息并添加了 taco、成分实体
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-13
      • 2017-05-12
      • 2018-08-21
      • 2017-04-21
      • 2021-01-23
      • 2018-10-13
      • 2017-06-14
      相关资源
      最近更新 更多