英文:
Java 8 to remove string value from each field where "string" value comes of the Custom Object
问题
我浏览了链接:https://stackoverflow.com/questions/2146129/is-it-possible-in-java-to-check-if-objects-fields-are-null-and-then-add-default 并实施了以下相同的解决方案 -
注意:我正在使用 **Swagger/Open API规范(使用springdoc-openapi-ui)**,在进行 ***POST*** 请求时,所有字符串字段的默认值都为"string",而我真的希望将其设置为null或空白。
有什么快速的指针吗?
public static Object getObject(Object obj) {
for (Field f : obj.getClass().getFields()) {
f.setAccessible(true);
try {
if (f.get(obj) == "string") {
f.set(obj, null);
}
} catch (IllegalArgumentException | IllegalAccessException e) {
log.error("Error While Setting default values for String");
}
}
return obj;
}
REST端点
@GetMapping(value = "/employees")
public ResponseEntity<PagedModel<EmployeeModel>> findEmployees(
EmployeeDto geoDto,
@Parameter(hidden=true) String sort,
@Parameter(hidden=true) String order,
@Parameter(hidden=true) Pageable pageRequest) {
EmployeeDto dto = (EmployeeDto) CommonsUtil.getObject(geoDto);
Page<CountryOut> response = countryService..............;
PagedModel<EmployeeModel> model = employeePagedAssembler.toModel(response, countryOutAssembler);
return new ResponseEntity<>(model, HttpStatus.OK);
}
英文:
I went through link: https://stackoverflow.com/questions/2146129/is-it-possible-in-java-to-check-if-objects-fields-are-null-and-then-add-default and implemented the same solution as below -
Note: I am using Swagger/Open API Specs (using springdoc-openapi-ui) and while making POST request all string fields are having default value as "string" which I really wanted to set it to null or space.
Any quick pointer ?
public static Object getObject(Object obj) {
for (Field f : obj.getClass().getFields()) {
f.setAccessible(true);
try {
if (f.get(obj) == "string") {
f.set(obj, null);
}
} catch (IllegalArgumentException | IllegalAccessException e) {
log.error("Error While Setting default values for String");
}
}
return obj;
}
REST endpoints
@GetMapping(value = "/employees")
public ResponseEntity<PagedModel<EmployeeModel>> findEmployees(
EmployeeDto geoDto,
@Parameter(hidden=true) String sort,
@Parameter(hidden=true) String order,
@Parameter(hidden=true) Pageable pageRequest) {
EmployeeDto dto = (EmployeeDto) CommonsUtil.getObject(geoDto);
Page<CountryOut> response = countryService..............;
PagedModel<EmployeeModel> model = employeePagedAssembler.toModel(response, countryOutAssembler);
return new ResponseEntity<>(model, HttpStatus.OK);
}
答案1
得分: 0
你可以简化一下,我猜。如果你控制EmployeeDto
,例如:
@Accessors(chain = true)
@Getter
@Setter
@ToString
static class EmployeeDto {
private String firstname;
private String lastname;
private int age;
}
你可以迭代类的字段,并使用MethodHandles
调用需要的setter,当一些getter返回你感兴趣的string
(并且字符串使用equals
而不是==
进行比较)。这甚至可以制作成一个小型库。这是一个开始:
private static final Lookup LOOKUP = MethodHandles.lookup();
/**
* 这计算了一些类(在你的情况下是EmployeeDTO)的所有已知字段及其getter/setter
*/
private static final Map<Class<?>, Map<Entry<String, ? extends Class<?>>, Entry<MethodHandle, MethodHandle>>> ALL_KNOWN =
Map.of(
EmployeeDto.class, metadata(EmployeeDto.class)
);
private Map<String, Entry<MethodHandle, MethodHandle>> MAP;
/**
* 例如,这将保存:{"firstname",String.class} -> getter/setter 到 "firstname"
*/
private static Map<Entry<String, ? extends Class<?>>, Entry<MethodHandle, MethodHandle>> metadata(Class<?> cls) {
return Arrays.stream(cls.getDeclaredFields())
.map(x -> new SimpleEntry<>(x.getName(), x.getType()))
.collect(Collectors.toMap(
Function.identity(),
entry -> {
try {
return new SimpleEntry<>(
LOOKUP.findGetter(cls, entry.getKey(), entry.getValue()),
LOOKUP.findSetter(cls, entry.getKey(), entry.getValue()));
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
));
}
有了这些信息,您可以为用户提供一个公共方法来调用,因此您需要提供DTO的实际实例,DTO类,要“默认为”的字段的Class,要检查的相等性以及实际的defaultValue
。
public static <T, R> T defaulter(T initial,
Class<T> dtoClass,
Class<R> fieldType,
R equality,
R defaultValue) throws Throwable {
Set<Entry<MethodHandle, MethodHandle>> all =
ALL_KNOWN.get(dtoClass)
.entrySet()
.stream()
.filter(x -> x.getKey().getValue() == fieldType)
.map(Entry::getValue)
.collect(Collectors.toSet());
for (Entry<MethodHandle, MethodHandle> getterAndSetter : all) {
R whatWeGot = (R) getterAndSetter.getKey().invoke(initial);
if (Objects.equals(whatWeGot, equality)) {
getterAndSetter.getValue().invoke(initial, defaultValue);
}
}
return initial;
}
这是您的调用者如何调用它:
public static void main(String[] args) throws Throwable {
EmployeeDto employeeDto = new EmployeeDto()
.setFirstname("string")
.setLastname("string");
EmployeeDto withDefaults = defaulter(employeeDto, EmployeeDto.class, String.class, "string", "defaultValue");
System.out.println(withDefaults);
}
英文:
You could do it a bit simpler, I guess. If you control EmployeeDto
, for example:
@Accessors(chain = true)
@Getter
@Setter
@ToString
static class EmployeeDto {
private String firstname;
private String lastname;
private int age;
}
You could iterate over fields of the class and using MethodHandles
invoke the needed setters, when some getters return the string
you are interested in (and Strings are compared using equals
, not ==
). This can even be made into a tiny library. Here is a start:
private static final Lookup LOOKUP = MethodHandles.lookup();
/**
* this computes all the know fields of some class (EmployeeDTO in your case) and their getter/setter
*/
private static final Map<Class<?>, Map<Entry<String, ? extends Class<?>>, Entry<MethodHandle, MethodHandle>>> ALL_KNOWN =
Map.of(
EmployeeDto.class, metadata(EmployeeDto.class)
);
private Map<String, Entry<MethodHandle, MethodHandle>> MAP;
/**
* For example this will hold : {"firstname", String.class} -> getter/setter to "firstname"
*/
private static Map<Entry<String, ? extends Class<?>>, Entry<MethodHandle, MethodHandle>> metadata(Class<?> cls) {
return Arrays.stream(cls.getDeclaredFields())
.map(x -> new SimpleEntry<>(x.getName(), x.getType()))
.collect(Collectors.toMap(
Function.identity(),
entry -> {
try {
return new SimpleEntry<>(
LOOKUP.findGetter(cls, entry.getKey(), entry.getValue()),
LOOKUP.findSetter(cls, entry.getKey(), entry.getValue()));
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
));
}
With that information you can provide a public method for users to call, So you need to provide the actual instance of your DTO, the DTO class, the Class of the fields you want to "default to", the equality to check against and the actual defaultValue
.
public static <T, R> T defaulter(T initial,
Class<T> dtoClass,
Class<R> fieldType,
R equality,
R defaultValue) throws Throwable {
Set<Entry<MethodHandle, MethodHandle>> all =
ALL_KNOWN.get(dtoClass)
.entrySet()
.stream()
.filter(x -> x.getKey().getValue() == fieldType)
.map(Entry::getValue)
.collect(Collectors.toSet());
for (Entry<MethodHandle, MethodHandle> getterAndSetter : all) {
R whatWeGot = (R) getterAndSetter.getKey().invoke(initial);
if (Objects.equals(whatWeGot, equality)) {
getterAndSetter.getValue().invoke(initial, defaultValue);
}
}
return initial;
}
And this is how your callers can call it:
public static void main(String[] args) throws Throwable {
EmployeeDto employeeDto = new EmployeeDto()
.setFirstname("string")
.setLastname("string");
EmployeeDto withDefaults = defaulter(employeeDto, EmployeeDto.class, String.class, "string", "defaultValue");
System.out.println(withDefaults);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论