ソースを参照

add read bean for csv

Looly 5 年 前
コミット
6b3be8468c

+ 1 - 0
CHANGELOG.md

@@ -8,6 +8,7 @@
 ### 新特性
 * 【core   】     NumberConverter Long类型增加日期转换(pr#872@Github)
 * 【all    】     StrUtil and SymmetricCrypto注释修正(pr#873@Github)
+* 【core   】     CsvReader支持返回Bean(issue#869@Github)
 
 ### Bug修复
 

+ 21 - 9
hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.EnumerationIter;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.util.CharsetUtil;
 import cn.hutool.core.util.ClassLoaderUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.URLUtil;
@@ -61,25 +62,25 @@ public class ResourceUtil {
 	/**
 	 * 从ClassPath资源中获取{@link InputStream}
 	 * 
-	 * @param resurce ClassPath资源
+	 * @param resource ClassPath资源
 	 * @return {@link InputStream}
 	 * @throws NoResourceException 资源不存在异常
 	 * @since 3.1.2
 	 */
-	public static InputStream getStream(String resurce) throws NoResourceException {
-		return getResourceObj(resurce).getStream();
+	public static InputStream getStream(String resource) throws NoResourceException {
+		return getResourceObj(resource).getStream();
 	}
 
 	/**
 	 * 从ClassPath资源中获取{@link InputStream},当资源不存在时返回null
 	 * 
-	 * @param resurce ClassPath资源
+	 * @param resource ClassPath资源
 	 * @return {@link InputStream}
 	 * @since 4.0.3
 	 */
-	public static InputStream getStreamSafe(String resurce) {
+	public static InputStream getStreamSafe(String resource) {
 		try {
-			return getResourceObj(resurce).getStream();
+			return getResourceObj(resource).getStream();
 		} catch (NoResourceException e) {
 			// ignore
 		}
@@ -88,14 +89,25 @@ public class ResourceUtil {
 
 	/**
 	 * 从ClassPath资源中获取{@link BufferedReader}
+	 *
+	 * @param resource ClassPath资源
+	 * @return {@link InputStream}
+	 * @since 5.3.6
+	 */
+	public static BufferedReader getUtf8Reader(String resource) {
+		return getReader(resource, CharsetUtil.CHARSET_UTF_8);
+	}
+
+	/**
+	 * 从ClassPath资源中获取{@link BufferedReader}
 	 * 
-	 * @param resurce ClassPath资源
+	 * @param resource ClassPath资源
 	 * @param charset 编码
 	 * @return {@link InputStream}
 	 * @since 3.1.2
 	 */
-	public static BufferedReader getReader(String resurce, Charset charset) {
-		return getResourceObj(resurce).getReader(charset);
+	public static BufferedReader getReader(String resource, Charset charset) {
+		return getResourceObj(resource).getReader(charset);
 	}
 
 	/**

+ 47 - 11
hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java

@@ -14,6 +14,7 @@ import java.nio.charset.Charset;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -146,17 +147,6 @@ public class CsvBaseReader implements Serializable {
 	/**
 	 * 从Reader中读取CSV数据,读取后关闭Reader
 	 *
-	 * @param reader     Reader
-	 * @param rowHandler 行处理器,用于一行一行的处理数据
-	 * @throws IORuntimeException IO异常
-	 */
-	public void read(Reader reader, CsvRowHandler rowHandler) throws IORuntimeException {
-		read(parse(reader), rowHandler);
-	}
-
-	/**
-	 * 从Reader中读取CSV数据,读取后关闭Reader
-	 *
 	 * @param reader Reader
 	 * @return {@link CsvData},包含数据列表和行信息
 	 * @throws IORuntimeException IO异常
@@ -170,6 +160,52 @@ public class CsvBaseReader implements Serializable {
 		return new CsvData(header, rows);
 	}
 
+	/**
+	 * 从Reader中读取CSV数据,结果为Map,读取后关闭Reader。<br>
+	 * 此方法默认识别首行为标题行。
+	 *
+	 * @param reader Reader
+	 * @return {@link CsvData},包含数据列表和行信息
+	 * @throws IORuntimeException IO异常
+	 */
+	public List<Map<String, String>> readMapList(Reader reader) throws IORuntimeException {
+		// 此方法必须包含标题
+		this.config.setContainsHeader(true);
+
+		final List<Map<String, String>> result = new ArrayList<>();
+		read(reader, (row) -> result.add(row.getFieldMap()));
+		return result;
+	}
+
+	/**
+	 * 从Reader中读取CSV数据并转换为Bean列表,读取后关闭Reader。<br>
+	 * 此方法默认识别首行为标题行。
+	 *
+	 * @param <T>    Bean类型
+	 * @param reader Reader
+	 * @param clazz  Bean类型
+	 * @return Bean列表
+	 */
+	public <T> List<T> read(Reader reader, Class<T> clazz) {
+		// 此方法必须包含标题
+		this.config.setContainsHeader(true);
+
+		final List<T> result = new ArrayList<>();
+		read(reader, (row) -> result.add(row.toBean(clazz)));
+		return result;
+	}
+
+	/**
+	 * 从Reader中读取CSV数据,读取后关闭Reader
+	 *
+	 * @param reader     Reader
+	 * @param rowHandler 行处理器,用于一行一行的处理数据
+	 * @throws IORuntimeException IO异常
+	 */
+	public void read(Reader reader, CsvRowHandler rowHandler) throws IORuntimeException {
+		read(parse(reader), rowHandler);
+	}
+
 	//--------------------------------------------------------------------------------------------- Private method start
 
 	/**

+ 16 - 2
hutool-core/src/main/java/cn/hutool/core/text/csv/CsvRow.java

@@ -1,5 +1,7 @@
 package cn.hutool.core.text.csv;
 
+import cn.hutool.core.bean.BeanUtil;
+
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -74,7 +76,7 @@ public final class CsvRow implements List<String> {
 	/**
 	 * 获取标题与字段值对应的Map
 	 *
-	 * @return an unmodifiable map of header names and field values of this row
+	 * @return 标题与字段值对应的Map
 	 * @throws IllegalStateException CSV文件无标题行抛出此异常
 	 */
 	public Map<String, String> getFieldMap() {
@@ -82,7 +84,7 @@ public final class CsvRow implements List<String> {
 			throw new IllegalStateException("No header available");
 		}
 
-		final Map<String, String> fieldMap = new LinkedHashMap<>(headerMap.size());
+		final Map<String, String> fieldMap = new LinkedHashMap<>(headerMap.size(), 1);
 		String key;
 		Integer col;
 		String val;
@@ -97,6 +99,18 @@ public final class CsvRow implements List<String> {
 	}
 
 	/**
+	 * 一行数据转换为Bean对象
+	 *
+	 * @param <T> Bean类型
+	 * @param clazz bean类
+	 * @return Bean
+	 * @since 5.3.6
+	 */
+	public <T> T toBean(Class<T> clazz){
+		return BeanUtil.mapToBean(getFieldMap(), clazz, true);
+	}
+
+	/**
 	 * 获取字段格式
 	 *
 	 * @return 字段格式

+ 59 - 2
hutool-core/src/test/java/cn/hutool/core/text/csv/CsvReaderTest.java

@@ -1,10 +1,14 @@
 package cn.hutool.core.text.csv;
 
+import cn.hutool.core.annotation.Alias;
+import cn.hutool.core.io.resource.ResourceUtil;
+import cn.hutool.core.util.CharsetUtil;
+import lombok.Data;
 import org.junit.Assert;
 import org.junit.Test;
 
-import cn.hutool.core.io.resource.ResourceUtil;
-import cn.hutool.core.util.CharsetUtil;
+import java.util.List;
+import java.util.Map;
 
 public class CsvReaderTest {
 	
@@ -14,4 +18,57 @@ public class CsvReaderTest {
 		CsvData data = reader.read(ResourceUtil.getReader("test.csv", CharsetUtil.CHARSET_UTF_8));
 		Assert.assertEquals("关注\"对象\"", data.getRow(0).get(2));
 	}
+
+	@Test
+	public void readMapListTest(){
+		final CsvReader reader = CsvUtil.getReader();
+		final List<Map<String, String>> result = reader.readMapList(
+				ResourceUtil.getUtf8Reader("test_bean.csv"));
+
+		Assert.assertEquals("张三", result.get(0).get("姓名"));
+		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("姓名"));
+		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("姓名"));
+		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();
+		final List<TestBean> result = reader.read(
+				ResourceUtil.getUtf8Reader("test_bean.csv"), TestBean.class);
+
+		Assert.assertEquals("张三", result.get(0).getName());
+		Assert.assertEquals("男", result.get(0).getGender());
+		Assert.assertEquals("无", result.get(0).getFocus());
+		Assert.assertEquals(Integer.valueOf(33), result.get(0).getAge());
+
+		Assert.assertEquals("李四", result.get(1).getName());
+		Assert.assertEquals("男", result.get(1).getGender());
+		Assert.assertEquals("好对象", result.get(1).getFocus());
+		Assert.assertEquals(Integer.valueOf(23), result.get(1).getAge());
+
+		Assert.assertEquals("王妹妹", result.get(2).getName());
+		Assert.assertEquals("女", result.get(2).getGender());
+		Assert.assertEquals("特别关注", result.get(2).getFocus());
+		Assert.assertEquals(Integer.valueOf(22), result.get(2).getAge());
+	}
+
+	@Data
+	private static class TestBean{
+		@Alias("姓名")
+		private String name;
+		private String gender;
+		private String focus;
+		private Integer age;
+	}
 }

+ 4 - 0
hutool-core/src/test/resources/test_bean.csv

@@ -0,0 +1,4 @@
+姓名,gender,focus,age
+张三,男,无,33
+李四,男,好对象,23
+王妹妹,女,特别关注,22