I'm trying to update my DTO with Reflection.
The problem is that some fields in my DTO are enums and I get an error that I can not set the enum field to String.
DTO:
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@Table(name = "xxx")
public class Model {
@Id
@Column(name = "id")
private String runId;
@Column(name = "name")
private String name;
@Column(name = "status")
@Enumerated(EnumType.STRING)
private ExecutionStatus status;
}
Controller:
@PatchMapping(path = "/{id}", consumes = "application/json")
public ResponseEntity<Void> partialUpdateModel(@PathVariable String id, @RequestBody Map<Object, Object> fields)
throws Exception {
Optional<Model> model= service.getById(id);
if (model.isPresent()) {
fields.forEach((key, value) -> {
Field field = ReflectionUtils.findField(Model.class, (String) key);
field.setAccessible(true);
ReflectionUtils.setField(field, model.get(), value);
});
So when it comes to the enum field, the field can not be set. It says
cannot set ExecutionStatus to String
CodePudding user response:
What you are trying to do is this:
model.status = "STATUS_1";
// incompatible types: java.lang.String cannot be converted to so.A.ExecutionStatus
What you apparently want to do is finding the enum constant of the specified enum type with the specified string. That's what the Enum.valueOf or YourEnum.valueOf
method is doing.
Example:
enum ExecutionStatus {
STATUS_1,
STATUS_2,
}
static class Model {
public String runId;
public String name;
public ExecutionStatus status;
@Override
public String toString() {
return "Model{"
"runId='" runId '\''
", name='" name '\''
", status=" status
'}';
}
}
public static void main(String[] args) {
Map<Object, Object> fields = Map.of(
"runId", "MyRunId",
"name", "MyName",
"status", "STATUS_1"
);
Model model = new Model();
fields.forEach((key, value) -> {
Field field = null;
try {
field = Model.class.getDeclaredField((String) key);
field.setAccessible(true);
if (field.getType().isEnum()) {
// First variant (YourEnum.valueOf(String)
Method valueOf = field.getType().getMethod("valueOf", String.class);
Object enumConstant = valueOf.invoke(null, value);
field.set(model, enumConstant);
// Alternative (Enum.valueOf(Class, String) (cast is safe due to isEnum)
field.set(model, Enum.valueOf((Class<Enum>) field.getType(), (String) value));
} else {
field.set(model, value);
}
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
});
System.out.println(model);
}
CodePudding user response:
You can do like this, Also understand reflection is costly it involves types that are being dynamically resolved, i'd recommend writing setters or constructor instead
if ("ExecutionStatus".equalsIgnoreCase(field.getType().getSimpleName())) {
ReflectionUtils.setField(field, model.get(), ExecutionStatus.valueOf(value));
} else {
ReflectionUtils.setField(field, model.get(), value);
}
CodePudding user response:
This is because your source map is of type <Object, Object>
What you try to do is to set a field of type ExecutionStatus
reading a value of type Object
. Types on your fields must match. First convert a value to ExecutionStatus
then use the method .setField(..)
.