mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add alias support
This commit is contained in:
parent
b1b8a29a3c
commit
e800b9cc3a
@ -26,6 +26,7 @@
|
|||||||
* 【core 】 增加PartitionIter(pr#402@Gitee)
|
* 【core 】 增加PartitionIter(pr#402@Gitee)
|
||||||
* 【all 】 增加异常爬栈开关(pr#403@Gitee)
|
* 【all 】 增加异常爬栈开关(pr#403@Gitee)
|
||||||
* 【core 】 优化Combination中C(n,n)的逻辑(pr#1792@Github)
|
* 【core 】 优化Combination中C(n,n)的逻辑(pr#1792@Github)
|
||||||
|
* 【core 】 Csv读写支持别名(issue#1791@Github)
|
||||||
|
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
* 【core 】 修复MapUtil.sort比较器不一致返回原map的问题(issue#I46AQJ@Gitee)
|
* 【core 】 修复MapUtil.sort比较器不一致返回原map的问题(issue#I46AQJ@Gitee)
|
||||||
|
@ -3,6 +3,8 @@ package cn.hutool.core.text.csv;
|
|||||||
import cn.hutool.core.util.CharUtil;
|
import cn.hutool.core.util.CharUtil;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSV基础配置项,此配置项可用于读取和写出CSV,定义了包括字段分隔符、文本包装符等符号
|
* CSV基础配置项,此配置项可用于读取和写出CSV,定义了包括字段分隔符、文本包装符等符号
|
||||||
@ -11,6 +13,7 @@ import java.io.Serializable;
|
|||||||
* @author looly
|
* @author looly
|
||||||
* @since 4.0.5
|
* @since 4.0.5
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
||||||
private static final long serialVersionUID = -8069578249066158459L;
|
private static final long serialVersionUID = -8069578249066158459L;
|
||||||
|
|
||||||
@ -26,6 +29,10 @@ public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
|||||||
* 注释符号,用于区分注释行,默认'#'
|
* 注释符号,用于区分注释行,默认'#'
|
||||||
*/
|
*/
|
||||||
protected char commentCharacter = '#';
|
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 字段分隔符,默认逗号','
|
* @param fieldSeparator 字段分隔符,默认逗号','
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T setFieldSeparator(final char fieldSeparator) {
|
public T setFieldSeparator(final char fieldSeparator) {
|
||||||
this.fieldSeparator = fieldSeparator;
|
this.fieldSeparator = fieldSeparator;
|
||||||
return (T) this;
|
return (T) this;
|
||||||
@ -45,7 +51,6 @@ public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
|||||||
* @param textDelimiter 文本分隔符,文本包装符,默认双引号'"'
|
* @param textDelimiter 文本分隔符,文本包装符,默认双引号'"'
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T setTextDelimiter(char textDelimiter) {
|
public T setTextDelimiter(char textDelimiter) {
|
||||||
this.textDelimiter = textDelimiter;
|
this.textDelimiter = textDelimiter;
|
||||||
return (T) this;
|
return (T) this;
|
||||||
@ -58,9 +63,45 @@ public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
|||||||
* @return this
|
* @return this
|
||||||
* @since 5.5.7
|
* @since 5.5.7
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T setCommentCharacter(char commentCharacter) {
|
public T setCommentCharacter(char commentCharacter) {
|
||||||
this.commentCharacter = commentCharacter;
|
this.commentCharacter = commentCharacter;
|
||||||
return (T) this;
|
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.IORuntimeException;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.text.StrBuilder;
|
import cn.hutool.core.text.StrBuilder;
|
||||||
import cn.hutool.core.util.CharUtil;
|
import cn.hutool.core.util.CharUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
@ -165,7 +166,11 @@ public final class CsvParser implements Closeable, Serializable {
|
|||||||
private void initHeader(final List<String> currentFields) {
|
private void initHeader(final List<String> currentFields) {
|
||||||
final Map<String, Integer> localHeaderMap = new LinkedHashMap<>(currentFields.size());
|
final Map<String, Integer> localHeaderMap = new LinkedHashMap<>(currentFields.size());
|
||||||
for (int i = 0; i < currentFields.size(); i++) {
|
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)) {
|
if (StrUtil.isNotEmpty(field) && false == localHeaderMap.containsKey(field)) {
|
||||||
localHeaderMap.put(field, i);
|
localHeaderMap.put(field, i);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ public final class CsvRow implements List<String> {
|
|||||||
* @param headerMap 标题Map
|
* @param headerMap 标题Map
|
||||||
* @param fields 数据列表
|
* @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!");
|
Assert.notNull(fields, "fields must be not null!");
|
||||||
this.originalLineNumber = originalLineNumber;
|
this.originalLineNumber = originalLineNumber;
|
||||||
this.headerMap = headerMap;
|
this.headerMap = headerMap;
|
||||||
@ -53,10 +53,8 @@ public final class CsvRow implements List<String> {
|
|||||||
* @return 字段值,null表示无此字段值
|
* @return 字段值,null表示无此字段值
|
||||||
* @throws IllegalStateException CSV文件无标题行抛出此异常
|
* @throws IllegalStateException CSV文件无标题行抛出此异常
|
||||||
*/
|
*/
|
||||||
public String getByName(final String name) {
|
public String getByName(String name) {
|
||||||
if (headerMap == null) {
|
Assert.notNull(this.headerMap, "No header available!");
|
||||||
throw new IllegalStateException("No header available");
|
|
||||||
}
|
|
||||||
|
|
||||||
final Integer col = headerMap.get(name);
|
final Integer col = headerMap.get(name);
|
||||||
if (col != null) {
|
if (col != null) {
|
||||||
|
@ -6,6 +6,7 @@ import cn.hutool.core.convert.Convert;
|
|||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.CharUtil;
|
import cn.hutool.core.util.CharUtil;
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
@ -20,6 +21,7 @@ import java.io.Serializable;
|
|||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,8 +219,9 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
|||||||
public CsvWriter write(CsvData csvData) {
|
public CsvWriter write(CsvData csvData) {
|
||||||
if (csvData != null) {
|
if (csvData != null) {
|
||||||
// 1、写header
|
// 1、写header
|
||||||
if (CollUtil.isNotEmpty(csvData.getHeader())) {
|
final List<String> header = csvData.getHeader();
|
||||||
this.writeLine(csvData.getHeader().toArray(new String[0]));
|
if (CollUtil.isNotEmpty(header)) {
|
||||||
|
this.writeHeaderLine(header.toArray(new String[0]));
|
||||||
}
|
}
|
||||||
// 2、写内容
|
// 2、写内容
|
||||||
this.write(csvData.getRows());
|
this.write(csvData.getRows());
|
||||||
@ -239,8 +242,8 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
|||||||
Map<String, Object> map;
|
Map<String, Object> map;
|
||||||
for (Object bean : beans) {
|
for (Object bean : beans) {
|
||||||
map = BeanUtil.beanToMap(bean);
|
map = BeanUtil.beanToMap(bean);
|
||||||
if(isFirst){
|
if (isFirst) {
|
||||||
writeLine(map.keySet().toArray(new String[0]));
|
writeHeaderLine(map.keySet().toArray(new String[0]));
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
}
|
}
|
||||||
writeLine(Convert.toStrArray(map.values()));
|
writeLine(Convert.toStrArray(map.values()));
|
||||||
@ -250,6 +253,29 @@ public final class CsvWriter implements Closeable, Flushable, Serializable {
|
|||||||
return this;
|
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"));
|
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
|
@Test
|
||||||
public void readBeanListTest(){
|
public void readBeanListTest(){
|
||||||
final CsvReader reader = CsvUtil.getReader();
|
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