我有一个 Spring Boot 应用程序,它有一个 Game 模型,其中包含抽象 Die 对象的列表。有多个不同颜色的骰子扩展了这个 Die 类并由工厂创建。 该列表在进入数据库之前被转换为字符串,并在通过转换器类检索时转换回模具对象。
但是,当我发出发布请求并尝试将请求主体映射到游戏类时,它会尝试实例化模具列表,从而导致此错误:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `sagrada.model.dice.Die` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
是否可以使用某种类型的转换器或告诉它在将主体映射到游戏之前将其转换为正确的骰子对象?
游戏类:
@Entity
public class Game {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@OneToMany(mappedBy = "game")
private List<GamePlayer> gamePlayers;
@Convert(converter = GameStateConverter.class)
private GameState state;
@Convert(converter = GameDiceConverter.class)
private List<Die> die;
public List<Die> getDie() {
return die;
}
public void setDie(List<Die> die) {
this.die = die;
}
}
模具类:
public abstract class Die {
private int value;
private dieColor color;
public Die(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public dieColor getColor() {
return color;
}
public void setColor(dieColor color) {
this.color = color;
}
}
转换器类:
public class GameDiceConverter implements AttributeConverter<List<Die>, String> {
@Override
public String convertToDatabaseColumn(List<Die> dice) {
StringBuilder diestring = new StringBuilder();
for (Die die : dice) {
String dieColor = die.getColor().toString();
int dieValue = die.getValue();
diestring.append(dieColor.charAt(0));
diestring.append(dieValue);
}
return diestring.toString();
}
@Override
public List<Die> convertToEntityAttribute(String s) {
String[] dice = s.split("(?<=\\G.{2})");
List<Die> result = new ArrayList<>();
for (String die : dice) {
switch (die.charAt(0)) {
case 'B' -> result.add(new BlueDie(die.charAt(1) - '0'));
case 'G' -> result.add(new GreenDie(die.charAt(1) - '0'));
case 'P' -> result.add(new PurpleDie(die.charAt(1) - '0'));
case 'R' -> result.add(new RedDie(die.charAt(1) - '0'));
case 'Y' -> result.add(new YellowDie(die.charAt(1) - '0'));
}
}
return result;
}
}
还有要求:
@PostMapping("/")
ResponseEntity<EntityModel<Game>> newGame(@RequestBody Game game) {
game.setState(GameState.NEW);
Game newGame = repository.save(game);
return ResponseEntity
.created(linkTo(methodOn(GameController.class).getGame(newGame.getId())).toUri())
.body(assembler.toModel(newGame));
}
问题是,你通过 JSON 获得一个 die 对象,例如
{
value: 5,
color: {
value: "B"
}
}
但是 Die.java 没有这些值的构造函数。
您可以声明一个自定义反序列化器,例如
public class DieDeserializer extends StdDeserializer<Die> {
public DieDeserializer() {
this(null);
}
public DieDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Item deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
int value = (Integer) ((IntNode) node.get("value")).numberValue();
String color = node.get("color").get("value").asText();
return new Die(value) {
@Override
public dieColor getColor()
{
return new dieColor(color);
}
};
}
}
上面的解决方案将从派生自 Die 的匿名类创建实例,因为它是抽象的。您还可以通过开关或类似的东西在那里创建具体的类。
上面的代码包含一些假设,因为缺少 dieColor 类。
然后你必须在 Die 类中声明反序列化器:
@JsonDeserialize(using = DieDeserializer.class)
public class Die {
//...
}