JDK版本:1.8 (由于用到了lambda、stream)
Maven
<!-- 用到了hutool的一些工具类方法 hutool版本比较老了 可去 https://mvnrepository.com 使用最新版 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.11</version>
</dependency>
<!-- lombok简化对象封装 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
转换工具类(重点关注 getAllDict())
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* dict convert tools
*
* @author Pans
**/
public class DictConvertUtil {
public static <T> void convertToCode(T data) {
convertToCode(data, null);
}
/**
* 男 to 1
*
* @param data 需要转换的对象
* @param consumer 转换后做一些事情
*/
public static <T> void convertToCode(T data, Consumer<T> consumer) {
if (Objects.isNull(data)) {
return;
}
convert(data, true);
if (consumer != null) {
consumer.accept(data);
}
}
public static <T> void convertToCodeList(List<T> data) {
convertToCodeList(data, null);
}
/**
* 男 to 1
*
* @param data 需要转换的对象
* @param consumer 转换后做一些事情
*/
public static <T> void convertToCodeList(List<T> data, Consumer<T> consumer) {
if (Objects.isNull(data) || CollUtil.isEmpty(data)) {
return;
}
data.parallelStream().forEach(d -> {
convert(d, true);
if (consumer != null) {
consumer.accept(d);
}
});
}
public static <T> void convertToDictionary(T data) {
convertToDictionary(data, null);
}
/**
* 1 to 男
*
* @param data 需要转换的对象
* @param consumer 转换后做一些事情
*/
public static <T> void convertToDictionary(T data, Consumer<T> consumer) {
if (Objects.isNull(data)) {
return;
}
convert(data, false);
if (consumer != null) {
consumer.accept(data);
}
}
public static <T> void convertToDictionaryList(List<T> data) {
convertToDictionaryList(data, null);
}
/**
* 1 to 男
*
* @param data 需要转换的对象
* @param consumer 转换后做一些事情
*/
public static <T> void convertToDictionaryList(List<T> data, Consumer<T> consumer) {
if (Objects.isNull(data) || CollUtil.isEmpty(data)) {
return;
}
data.parallelStream().forEach(d -> {
convert(d, false);
if (consumer != null) {
consumer.accept(d);
}
});
}
/**
* 转换字典中的值
*
* @param data 需要转换的对象
* @param isToCode {
* eg: 男-1、女-0
* true: 则将男转为1
* false: 则将1转为男
* }
*/
private static <T> void convert(T data, boolean isToCode) {
// 获取当前类的所有字段
Field[] fields = data.getClass().getDeclaredFields();
// 过滤 static、 final、private static final字段
final List<Field> filteredFields = Stream.of(fields).filter(f -> !(f.getModifiers() == Modifier.FINAL
|| f.getModifiers() == Modifier.STATIC
|| f.getModifiers() == (Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL)
|| f.getAnnotation(DictConvert.class) == null)).collect(Collectors.toList());
// 处理
for (Field f : filteredFields) {
// 获取当前字段注解
DictConvert annotation = f.getAnnotation(DictConvert.class);
// 没有加注解的字段不处理
if (annotation == null) {
continue;
}
// 反射获取字段值
Object value;
// 字典类型
String dictType = annotation.value();
// 如果是引用其他字段则值从其他字段取
if (StrUtil.isNotEmpty(annotation.refField())) {
value = ReflectUtil.getFieldValue(data, annotation.refField());
}
// 否则获取当前字段值
else {
value = ReflectUtil.getFieldValue(data, f);
}
// 转换字典时字段值为空 不进行后续处理
if (value == null) {
continue;
}
// 类型
final Class<?> classType = value.getClass();
// 如果不是基本类型
if (!ClassUtil.isBasicType(classType) && classType != String.class) {
// 是List 循环则递归调用
if (value instanceof List) {
for (Object o : (List) value) {
convert(o, isToCode);
}
}
// 不是 List 则视为对象反射调用
else {
convert(value, isToCode);
}
}
// 自定义的字典
final String dicts = annotation.dicts();
// 转换字典时字段字典类型未配置(字典key都不配置转个毛线)
if (StrUtil.isEmpty(dictType) && StrUtil.isEmpty(dicts)) {
continue;
}
// 获取字典的对应 映射关系 (建议此处做缓存提高转换速度)
final List<Dict> currDictList;
if (StrUtil.isNotBlank(dicts)) {
final List<String> dictList = StrUtil.splitTrim(dicts, ",");
currDictList = Optional.ofNullable(dictList)
.filter(CollUtil::isNotEmpty)
.map(s -> s.parallelStream().map(d -> {
final List<String> dTrim = StrUtil.splitTrim(d, ":");
return dTrim.size() == 2 ? Dict.builder().dictValue(dTrim.get(0)).dictLabel(dTrim.get(1)).build() : null;
}).filter(Objects::nonNull).collect(Collectors.toList())).orElse(new ArrayList<>(0));
} else {
currDictList = getDictByDictType(dictType);
}
// 是否匹配到了字典中的值
boolean isMatchSuccess = false;
// 获取当前字典值
final String beanValue = Convert.toStr(value);
// 支持类似 , 逗号隔开的字典转换, 如果需要支持其他 DictConvert#delimiter() 可在此设置
// eg : 兴趣爱好 (足球,篮球,奥利给)
// 转换后则为 (football,basketball,aoligei)
final String delimiter = annotation.delimiter();
final List<String> beanValues = StrUtil.splitTrim(beanValue, delimiter);
// 1 to 男
if (!isToCode) {
// 逗号隔开字典转换支持
if (CollUtil.isNotEmpty(beanValues) && beanValues.size() > 1) {
final Map<String, String> dictMap = currDictList.stream().collect(Collectors.toMap(Dict::getDictValue, Dict::getDictLabel));
final List<String> matchesDict = beanValues.stream()
.filter(dictMap::containsKey)
.map(dm -> Objects.nonNull(dictMap.get(dm)) ? dictMap.get(dm) : "")
.collect(Collectors.toList());
if (CollUtil.isNotEmpty(matchesDict)) {
isMatchSuccess = true;
ReflectUtil.setFieldValue(data, f, CollUtil.join(matchesDict, delimiter));
}
} else {
for (Dict sysDictData : currDictList) {
if (Objects.equals(Convert.toStr(value), sysDictData.getDictValue())) {
ReflectUtil.setFieldValue(data, f, Objects.nonNull(sysDictData.getDictLabel()) ? sysDictData.getDictLabel() : value);
isMatchSuccess = true;
break;
}
}
}
}
// 男 to 1
else {
// 逗号隔开字典转换支持
if (CollUtil.isNotEmpty(beanValues) && beanValues.size() > 1) {
final Map<String, String> dictMap = currDictList.stream().collect(Collectors.toMap(Dict::getDictLabel, Dict::getDictValue));
final List<String> matchesDict = beanValues.stream()
.filter(dictMap::containsKey)
.map(dm -> Objects.nonNull(dictMap.get(dm)) ? dictMap.get(dm) : "")
.collect(Collectors.toList());
if (CollUtil.isNotEmpty(matchesDict)) {
isMatchSuccess = true;
ReflectUtil.setFieldValue(data, f, CollUtil.join(matchesDict, delimiter));
}
} else {
for (Dict sysDictData : currDictList) {
if (Objects.equals(Convert.toStr(value), sysDictData.getDictLabel())) {
ReflectUtil.setFieldValue(data, f, Objects.nonNull(sysDictData.getDictValue()) ? sysDictData.getDictValue() : value);
isMatchSuccess = true;
break;
}
}
}
}
// 如果匹配不到字典中的值 且 字段中明确表示如果匹配不到就置为NULL
if (!isMatchSuccess && annotation.ifNotMatchConvertToNull()) {
ReflectUtil.setFieldValue(data, f, null);
}
}
}
/**
* 根据字典类型查询所有该类型的字典信息
* 可以在此处添加缓存(加快转换速度)
* eg:使用 spring cache
*
* @Cacheable(value = "dictConvert",key = "#dictType")
* public static List<Dict> getDictByDictType(String dictType) {
* return dictData;
* }
*
* @param dictType 字典类型
* @return 匹配到的字典集合
*/
private static List<Dict> getDictByDictType(String dictType) {
return getAllDict().parallelStream()
.filter(d -> dictType.equals(d.getDictType()))
.collect(Collectors.toList());
}
/**
* 获取所有字典
*
* 字典信息 可以从数据库 查询 或者 放入缓存都可以
*/
private static List<Dict> getAllDict() {
List<Dict> data = new ArrayList<>();
// 性别 字典
data.add(Dict.builder().dictType("sex").dictLabel("女").dictValue("0").build());
data.add(Dict.builder().dictType("sex").dictLabel("男").dictValue("1").build());
data.add(Dict.builder().dictType("sex").dictLabel("未知").dictValue("2").build());
return data;
}
@Data
@Builder
public static class Dict {
/**
* dict type
* eg: sex
*/
private String dictType;
/**
* dict label
* eg: 男
*/
private String dictLabel;
/**
* dict value
* eg: 1
*/
private String dictValue;
}
}
需求,实现对象属性值的字典互转
核心注解:
基础注解
import java.lang.annotation.*;
/**
* 转换字典
*
* @author Pans
**/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DictConvert {
/**
* 字典类型 dict_type 如果是 List 则无需指定 该值
*/
String value() default "";
/**
* 填写该字段后 该字段会拿到 refField 配置的字段的值并且根据字典转换
*/
String refField() default "";
/**
* 如果转换的值不匹配是否要转换为NULL
*/
boolean ifNotMatchConvertToNull() default false;
/**
* 带逗号的多字典 分隔符 默认逗号
*/
String delimiter() default ",";
/**
* 指定字典的转换数据(若指定则以当前字典数据为准)
* eg: 0:女,1:男,2:未知
*/
String dicts() default "";
}
创建测试类
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class User {
private Integer id;
@DictConvert("sex")
private String sex;
}
测试需求No1. 将对象的属性值转换为数据字典的值
例如 将 男 -> 1
public static void main(String[] args) throws Exception {
final User man = User.builder().id(1).sex("男").build();
System.out.println("转换之前 = " + man);
DictConvertUtil.convertToCode(man);
System.out.println("转换之后 = " + man);
}
控制台打印
有些情况字典中的源值我们也是需要的,例如前端在渲染下拉框是需要知道哪个是选中的
我们可以创建一个新的字段在不影响源字典值的情况下获得转换结果
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class User {
private Integer id;
private String sex;
@DictConvert(value = "sex", refField = "sex")
private String sexDict;
}
refField 表示引用其他字段的值来进行转换逻辑 再次运行Main得到以下结果
支持逗号分割的转换(如果不是逗号分割指定 delimiter 即可 默认逗号)
public static void main(String[] args) {
final User man = User.builder().id(1).sex("男,男,女,男,女").build();
System.out.println("转换之前 = " + man);
DictConvertUtil.convertToCode(man);
System.out.println("转换之后 = " + man);
}
结果
支持对象套集合 、对象套对象(嵌套只需要添加**@DictConvert**即可无需做其他属性设置)
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class User {
private Integer id;
private String sex;
@DictConvert(value = "sex", refField = "sex")
private String sexDict;
/**
* 我的儿子
*/
@DictConvert
private List<User> mySons;
/**
* 媳妇
*/
@DictConvert
private User wife;
}
修改Main
public static void main(String[] args) {
final User man = User.builder().id(1).sex("男,男,女,男,女").build();
List<User> sons = Arrays.asList(
User.builder().id(666).sex("男").build(),
User.builder().id(777).sex("女").build(),
User.builder().id(888).sex("女").build()
);
man.setMySons(sons);
final User wife = User.builder().id(2).sex("女").build();
man.setWife(wife);
System.out.println("转换之前 = " + JSONUtil.toJsonPrettyStr(JSONUtil.wrap(man, false)));
DictConvertUtil.convertToCode(man);
System.out.println("转换之后 = " + JSONUtil.toJsonPrettyStr(JSONUtil.wrap(man, false)));
}
结果(转换前)
{
"wife": {
"wife": null,
"sex": "女",
"id": 2,
"sexDict": null,
"mySons": null
},
"sex": "男,男,女,男,女",
"id": 1,
"sexDict": null,
"mySons": [
{
"wife": null,
"sex": "男",
"id": 666,
"sexDict": null,
"mySons": null
},
{
"wife": null,
"sex": "女",
"id": 777,
"sexDict": null,
"mySons": null
},
{
"wife": null,
"sex": "女",
"id": 888,
"sexDict": null,
"mySons": null
}
]
}
转换后
{
"wife": {
"wife": null,
"sex": "女",
"id": 2,
"sexDict": "0",
"mySons": null
},
"sex": "男,男,女,男,女",
"id": 1,
"sexDict": "1,1,0,1,0",
"mySons": [
{
"wife": null,
"sex": "男",
"id": 666,
"sexDict": "1",
"mySons": null
},
{
"wife": null,
"sex": "女",
"id": 777,
"sexDict": "0",
"mySons": null
},
{
"wife": null,
"sex": "女",
"id": 888,
"sexDict": "0",
"mySons": null
}
]
}
可以再转换后做一些逻辑
public static void main(String[] args) {
final User man = User.builder().id(1).sex("男,男,女,男,女").build();
System.out.println("转换之前 = " + man);
DictConvertUtil.convertToCode(man, m -> {
m.setId(99999999);
});
System.out.println("转换之后 = " + man);
}
输出
单独指定字典数据(不演试了)
@DictConvert(dicts = "1:小汉堡,2:老干妈,3:韭菜花")
private String ailigei;
转载首行注明出处 谢谢。