数据转化为对象设置对象属性值(自定义工具类)

在公司业务中,经常会遇到读取解析文件并保存内容到数据库的任务需求,一般文件里第一行记录着数据数目(行数)或标题或表头。不管怎样,对于后端人员,重要的是了解文件的统一规范,文件用途,文件结构,将表格的数据内容(一般为表头以下的数据)一字不差地存到数据库。

对文件的解析过程,除了对各种流的操作外,一般会自定义对象利用反射接收每一行数据,再将转化后的对象保存至数据库。
例如:一张记录着某超市一天交易数据的账单表,表主体的每一行数据对应着每一条‘账单项’,这时我们可以根据每一列数据对应的表头属性创建‘账单项’对象(checkItem),将每一行记录数据值作为对象的属性值逐个赋值。

工具类实现如下,并非能运用于各种项目,不同文件编写规范不同,这边仅供参考~

package com.dongdong.demo.web.util;

import com.dongdong.demo.web.service.annotation.Code2Desc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * 字符串数据转化对象
 */
public class DataUtils {

    private static Logger logger = LoggerFactory.getLogger(DataUtils.class);

    /**
     * 枚举类所在包名路径
     */
    private static final String EnumPackagePath = "com.dongdong.demo.web.service.enums";


    /**
     * 字符串转为对象,默认枚举类code为String类型;字符串转化后的数组长度与对象属性数组长度一致(对象属性数 - 忽视属性数 = String数组长度),属性顺序与数组元素顺序一一对应
     * @param line 需转为对象的数据字符串内容
     * @param seperator 分隔符
     * @param clazz 对应'转换结果对象'class对象
     * @param <T>   对象类型
     * @param num 忽视对象前几个属性,即设置前几个属性无需赋值转化
     * @return 返回对象
     */
    public static <T> T Str2Obj(String line, String seperator, Class<T> clazz , Integer num){
        T item = null;
        try {
        	//字符串转为字符串数组
            String[] val = line.split(seperator);
            
            //实例化对象
            item = clazz.newInstance();
            
            //获取属性列表
            Field[] fields = clazz.getDeclaredFields();
            
            //遍历字符串数组,逐个将数组元素为对象属性赋值
            for (int i = 0; i < val.length; i++) {
                if (val[i] != null && !val[i].trim().equals("")) {
                
                	//一般对象会有自身的唯一标识id属性且实现了serializable接口的对象会增多一个序列号,固对象属性列表长度会大于字符串数组长度,一旦超过了说明当前对象转化完毕,结束循环停止赋值。
                    if (num+i >= fields.length) {
                        break;
                    }
                    
                    //获取需要赋值的属性
                    Field field = fields[num+i];
                    field.setAccessible(true);
                    
                    //判断对象属性类型 如果类型为Integer类型转换为Integer类型赋值
                    //如果不是Integer类型转换为其他string类型赋值,之后逻辑判断同上
                    if (field.getType() == Integer.class) {
                        field.set(item, new Integer(val[i]));
                        continue;
                    } else if (field.getType() == Double.class) {
                        field.set(item, new Double(val[i]));
                        continue;
                    }
                    else if (field.getType() == LocalDate.class) {
                        LocalDate date = LocalDate.parse(val[i].trim(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                        field.set(item, date);
                        continue;
                    } else if (field.getType() == LocalTime.class) {
                        LocalTime date = LocalTime.parse(val[i].trim(), DateTimeFormatter.ofPattern("HH:mm:ss"));
                        field.set(item, date);
                        continue;
                    } else if (field.getType() == Date.class) {
                        field.set(item, new SimpleDateFormat("yyyy-MM-dd").parse(val[i].trim()));
                        continue;
                    } else {
                    
                        //获取'转换标志'注解,若有则说明有对应的枚举类,需转换(code转换desc)
                        Code2Desc annotation = field.getAnnotation(Code2Desc.class);
                        if (annotation != null) {
                            String fieldName = field.getName();
                            
                            //属性对应枚举类名
                            String enumName = fieldName.substring(0,1).toUpperCase().concat(fieldName.substring(1));
                            
                            //若属性有父类属性则获取父类属性code
                            String parentFieldName = annotation.parent();
                            if (parentFieldName != null && !"".equals(parentFieldName)) {
                                Field parentField = clazz.getDeclaredField(parentFieldName);
                                parentField.setAccessible(true);
                                
                                //获取父属性对应数组下标
                                int index = (int) printIndex(fields,parentField);
                                
                                //获取父属性对应字符串数组的值
                                String parentCode = val[index-num];
                                enumName = enumName.concat(parentCode);
                                //System.out.println("对应下标:"+index+"--父类属性方法:"+parentCode+"--枚举类名:"+enumName);
                            }
                            
							//获取枚举类class对象
                            Class enumClazz = null;
                            try {
                                enumClazz = Class.forName(EnumPackagePath+"."+enumName);
                            } catch (ClassNotFoundException e) {
                                logger.info("\n查找不到枚举类:"+enumName);
                                continue;
                            }

							//定义代码描述
                            String code2Desc = null;
                            try {
                            	//通过code(由val[i]取得)值获取枚举实例,.getDesc方法转相应代码描述
                                code2Desc = EnumUtils.getEnumObject(val[i],enumClazz).getDesc();
                            } catch (NullPointerException e) {
                                logger.info(enumName+"枚举类无对应code值:"+val[i]+";请确认");
                                continue;
                            }
							//赋值
                            field.set(item, code2Desc);
                            continue;
                        }
                        field.set(item, val[i]);
                    }
                }

            }

        } catch (IllegalAccessException | InstantiationException | NoSuchFieldException | ParseException e) {
            e.printStackTrace();
        }
        //返回转化后的对象
        return item;
    }

    private static Integer printIndex(Field[] arr, Field t){
        int i = 0;
        for (Field t1 : arr) {

            if (t.getName().equals(t1.getName())) {
                return i;
            }

            i++;
        }
        return null;
    }
}

一般对象会有自身的唯一标识id属性,且实现了serializable接口的对象会增多一个序列号,固对象属性列表长度会大于字符串数组长度,一旦超过了说明当前对象转化完毕,结束循环停止赋值,否则会报数组越界异常。

如果沒有代码转为相应描述的要求(ex:部分文件的某个字段是以代码形式记录数据的:常见的有00:“普通顾客”,01:“普通会员” ,02:“黄金会员”,在转化对象过程中,需将代码对应描述赋值于对象),忽视以上else{}代码块的内容。

对于需要将‘代码’(code)数据转‘描述’(desc)的属性,这边自定义@Code2Desc注解在属性上,之后通过field.getAnnotation(Code2Desc.class)方法识别需要转化的属性。

注释类

package com.dongdong.web.service.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 标记哪些属性需要转化,针对code转为desc
 */
@Target(ElementType.FIELD)//仅作用于属性
@Retention(RetentionPolicy.RUNTIME)
public @interface Code2Desc {

    /**
     * 标记属性名的父属性名,默认无
     * 如:String payMode; 与 String payMethod,因payMode根据payMethod确定属性值,所以payMethod为payMode的父属性
     *
     * @return
     */
    String parent() default "";
}

这里‘父属性’定义:依赖其它属性的值才能确定自身的值范围,此时,‘依赖属性’即是‘自身’的父属性。举个栗子:‘支付方式’与‘支付异常’,支付方式有微信支付,支付宝支付和各种银行卡支付等,而支付异常可根据不同支付方式来定义不同的异常范围;若出现异常结果,微信支付方式的话可能是因为微信服务器跌宕了(当然,概率很低嘻嘻),支付宝支付的话可能是支付宝服务器被黑了!!!(很难,也想碰见一次,看热闹不嫌事大嘛,嘿嘿)

自定义接收类

package com.dongdong.web.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.dongdong.web.service.annotation.Code2Desc;
import org.springframework.data.annotation.Id;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalTime;

public class Item implements Serializable {

    private static final long serialVersionUID = -4171125139888504392L;
    @Id
    private Long id;
    
    @TableField("txCode")
    @Code2Desc
    private String txCode;

    @TableField("payMethod")
    @Code2Desc
    private String payMethod;

    @TableField("payMode")
    @Code2Desc(parent = "payMethod")
    private String payMode;

    @TableField("payCompleteDate")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
    private LocalDate payCompleteDate;

    @TableField("payCompleteTime")
    @DateTimeFormat(pattern = "HH:mm:ss")
    @JsonFormat(timezone = "GMT+8", pattern = "HH:mm:ss")
    private LocalTime payCompleteTime;
}

这里每个被注释@Code2Desc的属性会为之创建对应枚举类

枚举类payMethod

package com.dongdong.web.service.enums;

import com.dongdong.web.service.EnumMessage;

public enum PayMethod implements EnumMessage {

    WECHAT("02","微信支付"),
    ZFB("03","支付宝支付"),
    OTHERS("99","其它");

    private final String code;
    private final String desc;

    PayMethod(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }
}

枚举类payMode

package com.dongdong.web.service.enums;

import com.dongdong.web.service.EnumMessage;

public enum PayMode02 implements EnumMessage {
    APP("01","微信线上 APP"),
    JSAPI("02","JSAPI,公众号"),

    private final String code;
    private final String desc;

    PayMode02(String code , String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }

   }
package com.dongdong.web.service.enums;

import com.dongdong.web.service.EnumMessage;

public enum PayMode03 implements EnumMessage {

    APP("10","支付宝线上APP");

    private final String code;
    private final String desc;

    PayMode03(String code , String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }

   
}

接口类

package com.dongdong.web.service;

public interface EnumMessage {
    String getCode();
    String getDesc();
}

看得全面的朋友或多或少注意到该行代码

code2Desc = EnumUtils.getEnumObject(val[i],enumClazz).getDesc();

这是笔者早前所写的工具类,用以通过‘code’值获取内部枚举类对象,然后getDesc()方法直接获取desc值
实际上工具类在这的作用可由下面的代码块一或二代替。
一:

//利用反射调用枚举类的valuse方法再遍历
Method method = enumClazz.getMethod("values");
EnumMessage[] inner = (EnumMessage[]) method.invoke(null, null);
for (EnumMessage enumMessage : inner) {
    if (val[i].equals(enumMessage.getCode())) {
        code2Desc = enumMessage.getDesc();
    }
}
// code2Desc = EnumUtils.getEnumObject(val[i],enumClazz).getDesc();

注意:values()方法是编译器插入到enum定义中的static方法,所以,当你将enum实例向上转型为父类Enum时,values()就不可访问了。解决办法:在Class中有一个getEnumConstants()方法,所以即便Enum接口中没有values()方法,我们仍然可以通过Class对象取得所有的enum实例

二:

//用getEnumConstants方法取得所有内部enum实例
for ( Object inner : enumClazz.getEnumConstants()) {
   	EnumMessage enumMessage = (inner instanceof EnumMessage) ? (EnumMessage) inner : null;
    if (val[i].equals(enumMessage.getCode())) {
        code2Desc = enumMessage.getDesc();
    }
}

每个功能实现方式是多样的,笔者踏入java帝国的足迹尚浅,相信会有更加灵活缜密的方法,望路过的大神指教~~以上。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页