Scroll
Scroll

Reputation: 178

Failed to convert property value of type java.lang.String[] to required type java.util.List

I am following Spring in Action 5 and have problem with creating Taco model after pressing submit button. This is my design Taco controller class:

    @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";
    }

And the error message which I catch:

    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 entity looks like:

@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();
    }

}

And my entity with Ingredients:

@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
    }
}

This is html page which must create new Taco object with picked ingredients:

<!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>

How can I fix it? Thanks for advance.

Upvotes: 6

Views: 10228

Answers (3)

Recep Bashir
Recep Bashir

Reputation: 41

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);
  }

}

Upvotes: 2

PhongPhamIUH
PhongPhamIUH

Reputation: 74

In Spring in Action, you should add IngredientByIdConverter class. this class is convert Ingredient to String.

@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);
    }
}

Upvotes: 2

Andreas
Andreas

Reputation: 159096

The error is:

Cannot convert value of type java.lang.String to required type org.server.models.Ingredient for property ingredients[0]

You didn't share the code for Taco or Ingredient or the payload of the POST request, so we cannot say for sure what you need to change.

However, if you add a constructor to Ingredient that takes a String argument, I believe Spring will use that.

How you create an Ingredient object from a String value will of course depend on what the strings content is, so that's entirely up to you to figure out. If you need help with that, create a new question, and include the relevant information, such as the code of your POJO classes and the content of the POST request.

Upvotes: 0

Related Questions