mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
add alias support
This commit is contained in:
parent
b1b8a29a3c
commit
e800b9cc3a
@ -26,6 +26,7 @@
|
||||
* 【core 】 增加PartitionIter(pr#402@Gitee)
|
||||
* 【all 】 增加异常爬栈开关(pr#403@Gitee)
|
||||
* 【core 】 优化Combination中C(n,n)的逻辑(pr#1792@Github)
|
||||
* 【core 】 Csv读写支持别名(issue#1791@Github)
|
||||
|
||||
### 🐞Bug修复
|
||||
* 【core 】 修复MapUtil.sort比较器不一致返回原map的问题(issue#I46AQJ@Gitee)
|
||||
|
@ -3,6 +3,8 @@ package cn.hutool.core.text.csv;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* CSV基础配置项,此配置项可用于读取和写出CSV,定义了包括字段分隔符、文本包装符等符号
|
||||
@ -11,6 +13,7 @@ import java.io.Serializable;
|
||||
* @author looly
|
||||
* @since 4.0.5
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
||||
private static final long serialVersionUID = -8069578249066158459L;
|
||||
|
||||
@ -26,6 +29,10 @@ public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
||||
* 注释符号,用于区分注释行,默认'#'
|
||||
*/
|
||||
protected char commentCharacter = '#';
|
||||
/**
|
||||
* 标题别名
|
||||
*/
|
||||
protected Map<String, String> headerAlias = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* 设置字段分隔符,默认逗号','
|
||||
@ -33,7 +40,6 @@ public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
||||
* @param fieldSeparator 字段分隔符,默认逗号','
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T setFieldSeparator(final char fieldSeparator) {
|
||||
this.fieldSeparator = fieldSeparator;
|
||||
return (T) this;
|
||||
@ -45,7 +51,6 @@ public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
||||
* @param textDelimiter 文本分隔符,文本包装符,默认双引号'"'
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T setTextDelimiter(char textDelimiter) {
|
||||
this.textDelimiter = textDelimiter;
|
||||
return (T) this;
|
||||
@ -58,9 +63,45 @@ public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
||||
* @return this
|
||||
* @since 5.5.7
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T setCommentCharacter(char commentCharacter) {
|
||||
this.commentCharacter = commentCharacter;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置标题行的别名Map
|
||||
*
|
||||
* @param headerAlias 别名Map
|
||||
* @return this
|
||||
* @since 5.7.10
|
||||
*/
|
||||
public T setHeaderAlias(Map<String, String> headerAlias) {
|
||||
this.headerAlias = headerAlias;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加标题别名
|
||||
*
|
||||
* @param header 标题
|
||||
* @param alias 别名
|
||||
* @return this
|
||||
* @since 5.7.10
|
||||
*/
|
||||
public T addHeaderAlias(String header, String alias) {
|
||||
this.headerAlias.put(header, alias);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除标题别名
|
||||
*
|
||||
* @param header 标题
|
||||
* @return this
|
||||
* @since 5.7.10
|
||||
*/
|
||||
public T removeHeaderAlias(String header) {
|
||||
this.headerAlias.remove(header);
|
||||
return (T) this;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package cn.hutool.core.text.csv;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.text.StrBuilder;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
@ -165,7 +166,11 @@ public final class CsvParser implements Closeable, Serializable {
|
||||
private void initHeader(final List<String> currentFields) {
|
||||
final Map<String, Integer> localHeaderMap = new LinkedHashMap<>(currentFields.size());
|
||||
for (int i = 0; i < currentFields.size(); i++) {
|
||||
final String field = currentFields.get(i);
|
||||
String field = currentFields.get(i);
|
||||
if (MapUtil.isNotEmpty(this.config.headerAlias)) {
|
||||
// 自定义别名
|
||||
field = ObjectUtil.defaultIfNull(this.config.headerAlias.get(field), field);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(field) && false == localHeaderMap.containsKey(field)) {
|
||||
localHeaderMap.put(field, i);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public final class CsvRow implements List<String> {
|
||||
* @param headerMap 标题Map
|
||||
* @param fields 数据列表
|
||||
*/
|
||||
public CsvRow(final long originalLineNumber, final Map<String, Integer> headerMap, final List<String> fields) {
|
||||
public CsvRow(long originalLineNumber, Map<String, Integer> headerMap, List<String> fields) {
|
||||
Assert.notNull(fields, "fields must be not null!");
|
||||
this.originalLineNumber = originalLineNumber;
|
||||
this.headerMap = headerMap;
|
||||
@ -53,10 +53,8 @@ public final class CsvRow implements List<String> {
|
||||
* @return 字段值,null表示无此字段值
|
||||
* @throws IllegalStateException CSV文件无标题行抛出此异常
|
||||
*/
|
||||
public String getByName(final String name) {
|
||||
if (headerMap == null) {
|
||||
throw new IllegalStateException("No header available");
|
||||
}
|
||||
public String getByName(String name) {
|
||||
Assert.notNull(this.headerMap, "No header available!");
|
||||
|
||||
final Integer col = headerMap.get(name);
|
||||
if (col != null) {
|
||||
|
@ -6,6 +6,7 @@ import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
@ -20,6 +21,7 @@ import java.io.Serializable;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -217,8 +219,9 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
||||
public CsvWriter write(CsvData csvData) {
|
||||
if (csvData != null) {
|
||||
// 1、写header
|
||||
if (CollUtil.isNotEmpty(csvData.getHeader())) {
|
||||
this.writeLine(csvData.getHeader().toArray(new String[0]));
|
||||
final List<String> header = csvData.getHeader();
|
||||
if (CollUtil.isNotEmpty(header)) {
|
||||
this.writeHeaderLine(header.toArray(new String[0]));
|
||||
}
|
||||
// 2、写内容
|
||||
this.write(csvData.getRows());
|
||||
@ -239,8 +242,8 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
||||
Map<String, Object> map;
|
||||
for (Object bean : beans) {
|
||||
map = BeanUtil.beanToMap(bean);
|
||||
if(isFirst){
|
||||
writeLine(map.keySet().toArray(new String[0]));
|
||||
if (isFirst) {
|
||||
writeHeaderLine(map.keySet().toArray(new String[0]));
|
||||
isFirst = false;
|
||||
}
|
||||
writeLine(Convert.toStrArray(map.values()));
|
||||
@ -250,6 +253,29 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出一行头部行,支持标题别名
|
||||
*
|
||||
* @param fields 字段列表 ({@code null} 值会被做为空值追加
|
||||
* @return this
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.7.10
|
||||
*/
|
||||
public CsvWriter writeHeaderLine(String... fields) throws IORuntimeException {
|
||||
final Map<String, String> headerAlias = this.config.headerAlias;
|
||||
if (MapUtil.isNotEmpty(headerAlias)) {
|
||||
// 标题别名替换
|
||||
String alias;
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
alias = headerAlias.get(fields[i]);
|
||||
if (null != alias) {
|
||||
fields[i] = alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
return writeLine(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出一行
|
||||
*
|
||||
|
@ -47,6 +47,31 @@ public class CsvReaderTest {
|
||||
Assert.assertEquals("22", result.get(2).get("age"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readAliasMapListTest(){
|
||||
final CsvReadConfig csvReadConfig = CsvReadConfig.defaultConfig();
|
||||
csvReadConfig.addHeaderAlias("姓名", "name");
|
||||
|
||||
final CsvReader reader = CsvUtil.getReader(csvReadConfig);
|
||||
final List<Map<String, String>> result = reader.readMapList(
|
||||
ResourceUtil.getUtf8Reader("test_bean.csv"));
|
||||
|
||||
Assert.assertEquals("张三", result.get(0).get("name"));
|
||||
Assert.assertEquals("男", result.get(0).get("gender"));
|
||||
Assert.assertEquals("无", result.get(0).get("focus"));
|
||||
Assert.assertEquals("33", result.get(0).get("age"));
|
||||
|
||||
Assert.assertEquals("李四", result.get(1).get("name"));
|
||||
Assert.assertEquals("男", result.get(1).get("gender"));
|
||||
Assert.assertEquals("好对象", result.get(1).get("focus"));
|
||||
Assert.assertEquals("23", result.get(1).get("age"));
|
||||
|
||||
Assert.assertEquals("王妹妹", result.get(2).get("name"));
|
||||
Assert.assertEquals("女", result.get(2).get("gender"));
|
||||
Assert.assertEquals("特别关注", result.get(2).get("focus"));
|
||||
Assert.assertEquals("22", result.get(2).get("age"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readBeanListTest(){
|
||||
final CsvReader reader = CsvUtil.getReader();
|
||||
|
@ -0,0 +1,24 @@
|
||||
package cn.hutool.core.text.csv;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CsvWriterTest {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void writeWithAliasTest(){
|
||||
final CsvWriteConfig csvWriteConfig = CsvWriteConfig.defaultConfig()
|
||||
.addHeaderAlias("name", "姓名")
|
||||
.addHeaderAlias("gender", "性别");
|
||||
|
||||
final CsvWriter writer = CsvUtil.getWriter(
|
||||
FileUtil.file("d:/test/csvAliasTest.csv"),
|
||||
CharsetUtil.CHARSET_GBK, false, csvWriteConfig);
|
||||
|
||||
writer.writeHeaderLine("name", "gender", "address");
|
||||
writer.close();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user