Java 字典 转换 实体字典互转 翻译

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;

转载首行注明出处 谢谢。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值