前言:
这里仅仅支持Excel文件导出的CSV文件,解析的核心是一个正则表达式,这个正则表达式取自<精通正则表达式>一书中,感谢作者。
1、解析引擎结构图
2、很懒很懒,直接上代码了
/**
* CSV 文件解析
*
* @param
* @param xmlInputStream
* @param clazz
* @param file
* @return
*/
protected List parse(InputStream xmlInputStream, Class clazz, InputStream file) {
//xml解析
Map metaDataMap = parseXmlConfig(xmlInputStream);
//获取对象实例
T obj = getInstance(clazz);
//校验Map中的metaData信息与clazz中的属性是否完全匹配
checkProperty(metaDataMap, obj);
//读取csv文件,返回解析结果
List datas = parseCsvFile(file, clazz, metaDataMap);
return datas;
}
/**
* 获取对象属性与csv头部文件的映射Map
* key:csv文件头部中文
* value:映射类的属性
*
* @param xmlPath
* @return
*/
@SuppressWarnings("unchecked")
protected Map parseXmlConfig(InputStream in) {
Map metaDataMap = new HashMap();
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(in);
Element root = document.getRootElement();
//循环模板(*-*!!)
for (Iterator iter = root.elementIterator(); iter.hasNext();) {
Element element = (Element) iter.next();
getPropertyValue(element, "name", metaDataMap);
}
} catch (Throwable e) {
throw new RuntimeException(e);
}
return metaDataMap;
}
/**
* 获取映射对象的运行实例
*
* @param
* @param clazz
* @return
*/
private T getInstance(Class clazz) {
T obj = null;
try {
obj = clazz.newInstance();
} catch (Throwable e) {
throw new RuntimeException(e);
}
return obj;
}
/**
* 校验Map中的metaData信息与clazz中的属性是否完全匹配
*
* @param
* @param metaDataMap key:中文描述 value:类属性
* @param obj
*/
private void checkProperty(Map metaDataMap, T obj) {
List fieldList = new ArrayList();
//获取obj属性名称列表
Field[] fields = obj.getClass().getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
String name = f.getName();
if (!StringUtil.equals(name, ConstantEnum.REFLECT_FIELD_GARGABE_ONE.getCode())
&& !StringUtil.equals(name, ConstantEnum.REFLECT_FIELD_GARGABE_TWO.getCode())) {
fieldList.add(f.getName());
}
}
for (String fieldName : fieldList) {
if (!metaDataMap.containsValue(fieldName)) {
throw new RevmngException(RevmngResultCode.HEAD_INFO_NOT_SAME_OBJ);
}
}
if (metaDataMap.size() != fieldList.size()) {
throw new RevmngException(RevmngResultCode.HEAD_INFO_NOT_SAME_OBJ);
}
}
/**
* 解析csv文件
*
* @param
* @param file
* @param obj
* @param metaDataMap key:中文 value:字段属性
*
* @return
*/
private List parseCsvFile(InputStream file, Class clazz,
Map metaDataMap) {
List datas = new ArrayList();
InputStreamReader fr = new InputStreamReader(file);
BufferedReader br = new BufferedReader(fr);
List headInfo = new ArrayList();
try {
String line = "";
int lineSeq = 0;
while ((line = br.readLine()) != null) {
T tempObj = getInstance(clazz);
int cellSeq = -1;
Map mapLine = new HashMap();
lineSeq++;
Matcher matcher = getMatcher(line);
Matcher mQuote = Pattern.compile("\"\"").matcher("");
while (matcher.find()) {
cellSeq++;
String field = getField(matcher, mQuote);
if (lineSeq == 1) {
//头部信息
if (StringUtil.isNotBlank(field)) {
headInfo.add(field);
}
continue;
}
//映射类的属性名称
if (cellSeq >= headInfo.size()) {
continue;
}
String propertyName = metaDataMap.get(headInfo.get(cellSeq));
if (StringUtil.isBlank(propertyName)) {
logger.warn("未取到导入文件头部的文字信息属性!");
throw new RevmngException(RevmngResultCode.CSV_HEAD_INFO_ERROR);
}
mapLine.put(propertyName, field);
}
//解决这个正则的一个缺陷,类似这样的csv格式解析有误",a,bab,a,c,c"(第一个单元格数据为空),现在会直接跳过
firstCellIsNotNull(cellSeq, line, headInfo);
//填充映射对象的属性值
if (lineSeq > 1) {
if (headInfo.size() != metaDataMap.size()) {
logger.warn("上传文件的模板不正确,请下载正确的模板!");
throw new RevmngException(RevmngResultCode.CSV_TEMPLATE_FILE_ERROR);
}
setObjectValue(tempObj, mapLine);
datas.add(tempObj);
}
}
} catch (RevmngException re) {
throw re;
} catch (Throwable e) {
throw new RuntimeException(e);
}
return datas;
}
/**
* 解决这个正则的一个缺陷,类似这样的csv格式解析有误",a,bab,a,c,c"(第一个单元格数据为空),现在会直接跳过
*
* @param cellSeq
* @param line
*/
@SuppressWarnings("unchecked")
private void firstCellIsNotNull(int cellSeq, String line, List headInfo) {
if (StringUtil.isBlank(line)) {
return;
}
String[] contens = line.split("[,]");
List lists = Arrays.asList(contens);
StringBuffer lineBuffer = new StringBuffer();
for (int i = 0; i < headInfo.size(); i++) {
lineBuffer.append(headInfo.get(i)).append(" : ");
if (i < lists.size()) {
lineBuffer.append(lists.get(i));
}
}
if (!CollectionUtils.isEmpty(lists) && cellSeq == 0) {
logger.warn("存在第一个单元格数据为空的文本行!line:" + line);
throw new RevmngException(RevmngResultCode.CSV_FIRST_CELL_NOT_NULL.getCode(),
"数据行: " + lineBuffer.toString() + " 的 " + lists.get(0) + "不允许为空!");
}
}
/**
* 获取Matcher
* @param line
* @return
*/
private Matcher getMatcher(String line) {
Matcher matcher = Pattern.compile(RegularExpressionEnum.REGEX_CSV_FOMAT.getCode(),
Pattern.COMMENTS).matcher("");
matcher.reset(line);
return matcher;
}
/**
* 获取解析的一个单元值
* @param matcher
* @param mQuote
* @return
*/
private String getField(Matcher matcher, Matcher mQuote) {
String field = "";
if (matcher.start(2) >= 0)
field = matcher.group(2);
else
field = mQuote.reset(matcher.group(1)).replaceAll("\"");
return field;
}
/**
* 设置一行映射对象的属性值
*
* @param
* @param object
* @param mapLine key :属性名称 value:属性值
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
private void setObjectValue(T object, Map mapLine)
throws IllegalArgumentException,
IllegalAccessException {
Field[] fields = object.getClass().getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
String name = f.getName();
//去除垃圾属性的影响
if (StringUtil.equals(name, ConstantEnum.REFLECT_FIELD_GARGABE_ONE.getCode())
|| StringUtil.equals(name, ConstantEnum.REFLECT_FIELD_GARGABE_TWO.getCode())) {
continue;
}
if (mapLine.containsKey(f.getName())) {
//去空格
String value = StringUtil.trim(mapLine.get(f.getName()));
f.set(object, value);
}
}
}
只是一些关键代码而已,别人不一定看得明白,自己mark一下。。哇哈哈哈。。。