利用freemarker+SAX解析xml的方式對excel文件字段校驗

利用freemarker對參數進行校驗

這篇文章主要用到的技術點:

  • 自定義註解的使用
  • 反射機制
  • SAX解析xml
  • Freemarker的運用

我們在工作中經常需要上傳excel文件,然後在對文件中的字段進行校驗。如果文件裏的字段是反覆出現,或者文件的字段比較多的話,這是就會使代碼變得繁瑣,而且也不容易維護。
比如說像下面那張學生的表格
這裏插入圖片描述學生
我們需要對上面表中的每個字段做校驗
1.userName 必傳字段
2. telephone 手機電話號碼格式校驗
3. email格式的校驗
4. birthday 格式的校驗
5. userId的生成規則等

有時候我們用自定義註解+反射,往往可以使代碼變得清晰明瞭,不說了,直接上代碼:

package com.example.demo.Controller;

import com.example.demo.utils.DealException;
import com.example.demo.utils.ExcelUtils;
import com.example.demo.validate.TemplateXmlUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS;

@RestController
public class UploadExcelController {
    @RequestMapping("/upload")
    public Object uploadFile(MultipartFile file) throws IOException, TemplateException {
        //讀取文件的基本信息
        String filename = file.getOriginalFilename();
        InputStream inputStream = file.getInputStream();
        try {
         //   Workbook workbook = ExcelUtils.buildWorkookFromStream(filename, inputStream);
            //解析excel
            List<Map<String, Object>> maps = ExcelUtils.excelToList(filename, inputStream, 0, 0);
            Configuration configuration = new Configuration(DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
            URL url = this.getClass().getClassLoader().getResource("freemarkers");
            configuration.setDirectoryForTemplateLoading(new File(url.getFile()));
            Template template = configuration.getTemplate("userMapper.ftl");
            HashMap<String, Object> objMap = new HashMap<>();
            objMap.put("maps",maps);
            StringWriter sw = new StringWriter();
            template.process(objMap,sw);
            System.out.println(sw.toString());

            String s = TemplateXmlUtil.analysisXml(sw.toString(), "UTF-8");
            System.out.println(s);
            return s;

        } catch (DealException e) {
            e.printStackTrace();
        }
       return null;
    }
}

這段代碼首先解析了excel文件,將文件轉化成list,下面貼出excelutils的代碼

package com.example.demo.utils;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ExcelUtils {
    public static Workbook buildWorkookFromStream(String excelFileName, InputStream excelStream) throws IOException, DealException {
        Workbook book=null;
        if (excelFileName.endsWith("xls")){
            book = new HSSFWorkbook(excelStream);
        }else if(excelFileName.endsWith("xlsx")){
            book=new XSSFWorkbook(excelStream);
        }else{
            throw new DealException("000000","文件初始化失敗");
        }
        return book;
    }

    public static List<Map<String,Object>> excelToList(String excelName, InputStream excelStream, Integer sheetIndex, Integer startRow) throws IOException, DealException {
            List<Map<String,Object>> resultList=new ArrayList<>();
        Workbook workbook = buildWorkookFromStream(excelName, excelStream);
        Sheet sheet = workbook.getSheetAt(sheetIndex);
        //獲取第一行的擡頭
        Row row = sheet.getRow(startRow);
        int cellsNum = row.getPhysicalNumberOfCells();
        List<String> titleList = new ArrayList<>();
        for (int i = 0; i <cellsNum ; i++) {
            Cell cell = row.getCell(i);
            cell.setCellType(CellType.STRING);
            String cellValue = cell.getStringCellValue().trim();
            titleList.add(cellValue);
        }
        if(titleList.size()==0){
            throw new DealException("000001","表中沒有擡頭");
        }

        int lastRowNum = sheet.getLastRowNum();
        for (int i = startRow+1; i <=lastRowNum ; i++) {
             Map<String,Object> rowDataMap= new HashMap<>();
            Row rows = sheet.getRow(i);
            boolean blankFlag=true;
            for (int cellIndex = 0; cellIndex <titleList.size() ; cellIndex++) {
                Cell cell = rows.getCell(cellIndex);
                if(null==cell){
                    rowDataMap.put(titleList.get(cellIndex),"");
                    continue;
                }
                cell.setCellType(CellType.STRING);
                String cellVal = cell.getStringCellValue().trim();
                rowDataMap.put(titleList.get(cellIndex),cellVal);
                if(!StringUtils.isEmpty(cellVal)){
                    blankFlag=false;
                }
            }
            if(!blankFlag){
                resultList.add(rowDataMap);
            }
        }
        return  resultList;

    }
}

這裏我就不對這個工具類做具體的介紹了。
下面貼出userMapper.ftl

<?xml version="1.0" encoding="UTF-8"?>
<root>
<#list maps as map>
    <user>
    <#list map?keys as itemKey>
    <#if itemKey=="userName">
        <userName isRequired="true">${map[itemKey]}</userName>
    </#if>
    <#if itemKey=="telephone">
        <telephone isRequired="true">${map[itemKey]}</telephone>
    </#if>
    <#if itemKey=="email">
        <email isRequired="true">${map[itemKey]}</email>
    </#if>
    <#if itemKey=="birthday">
        <birthday isRequired="true">${map[itemKey]}</birthday>
    </#if>
    <#if itemKey=="userId">
        <userId isRequired="true">${map[itemKey]}</userId>
    </#if>
    </#list>
    </user>
</#list>
</root>

freemarker 中調用process方法,將數據模型和模板進行整合。
主要驗證的代碼如下

package com.example.demo.validate;

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidatorAnnotion {
    public String validatorType();
    public String message();

}

package com.example.demo.validate;

import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
public class TemplateValidator {
    private final static String regexEmail="^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*";
    private final static String regexPhone="^1(3|4|5|7|8)\\d{9}$";
    /**
     * 驗證是否爲電子郵箱
    *@param
    *@return
    */
    @ValidatorAnnotion(validatorType = "isEmail",message = "郵箱格式不正確")
    public static boolean isEmail(Map contentMap){
        String itemValue = (String)contentMap.get("itemValue");
        return isMatchString(itemValue,regexEmail,true);
    }

    /**
     * 驗證手機號碼的正確性
     * @param contentMap
     * @return
     */
    @ValidatorAnnotion(validatorType = "isPhone",message = "電話號碼格式不正確")
    public static boolean isPhone(Map contentMap){
        String itemValue= (String) contentMap.get("itemValue");
        return  isMatchString(itemValue,regexPhone,true);
    }

    /**
     * 是否是必傳
     * @param contentMap
     * @return
     */
    @ValidatorAnnotion(validatorType = "isRequired",message = "必傳")
    public static boolean isRequired(Map contentMap){
        String itemValue = (String) contentMap.get("itemValue");
        return !StringUtils.isEmpty(itemValue);
    }


    /**
     *
     * @param itemValue
     * @param regex
     * @param caseSentivite
     * @return
     */

    private static boolean isMatchString(String itemValue, String regex, boolean caseSentivite) {
        boolean result=false;
        if(null==itemValue||null==regex){
            throw new NullPointerException("error,itemValue or regx is null");
        }
        Pattern pattern = null;
        if(!caseSentivite){
            pattern=Pattern.compile(regex,Pattern.CASE_INSENSITIVE);
        }else{
            pattern=Pattern.compile(regex);
        }
        Matcher matcher = pattern.matcher(itemValue);
        result= matcher.matches();
        return result;
    }


}

package com.example.demo.validate;

import com.example.demo.utils.DealException;
import com.example.demo.utils.ExcelUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

public class ValidatorBeanFactory {
    /**
     * 默認的校驗類路徑
     */
    private static  final String DEFAULT_VALIDATE_CLASSNAME=TemplateValidator.class.getName();

    static Class<?> validatorClass=null;
    public static Method[] methods=null;
    static{
        try {
            validatorClass=Class.forName(DEFAULT_VALIDATE_CLASSNAME);
            methods = validatorClass.getMethods();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void validate(StringBuffer buffer, Map<String,Object> attrMap) throws DealException {
        boolean result=true;
        for(Method method:methods){
            //獲取method方法上的註解
            if(method.isAnnotationPresent(ValidatorAnnotion.class)){
                ValidatorAnnotion annotation = method.getAnnotation(ValidatorAnnotion.class);
                String validatorType = annotation.validatorType();
                String attrName = (String) attrMap.get("attrName");
                if(validatorType.equals(attrName)){
                    try {
                        result = (boolean) method.invoke(validatorClass, attrMap);
                    } catch (Exception e) {
                        throw new DealException("000003","參數校驗異常");
                    }
                    if(!result){
                        buffer.append("節點").append(attrMap.get("itemName")).append(",").append(annotation.message());
                    }
                }
            }

        }
    }
}

package com.example.demo.validate;

import com.example.demo.utils.DealException;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class TemplateXmlUtil {
    public static String analysisXml(String xmlStr,String charset){
        xmlStr= xmlStr.replaceAll("\r","");
        SAXReader reader = new SAXReader();
        StringBuffer sb=null;
        try {
            Document document = reader.read(new ByteArrayInputStream(xmlStr.getBytes(charset)));
            Element xmlroot = document.getRootElement();
            sb = new StringBuffer();
            iteratorXml(xmlroot,sb);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }
private static void iteratorXml(Element xmlroot, StringBuffer sb) throws DealException {
        Iterator<Element> iterator = xmlroot.elementIterator();
        while (iterator.hasNext()){
            Element xmlItem = iterator.next();
            //獲取屬性名和屬性值
            List<Attribute> attributes = xmlItem.attributes();
            for (Attribute attribute:attributes){
                Map<String, Object> attrMap = new HashMap<>();
                attrMap.put("attrName",attribute.getName());
                attrMap.put("attrValue",attribute.getValue());
                attrMap.put("itemName",xmlItem.getName());
                attrMap.put("itemValue",xmlItem.getStringValue());
                ValidatorBeanFactory.validate(sb,attrMap);
            }
            iteratorXml(xmlItem,sb);
        }
    }
}

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章