From 6b3be8468c291f4065beb2760f2855a10302a41c Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 18 May 2020 09:38:20 +0800 Subject: [PATCH] add read bean for csv --- CHANGELOG.md | 1 + .../hutool/core/io/resource/ResourceUtil.java | 30 ++++++--- .../hutool/core/text/csv/CsvBaseReader.java | 58 ++++++++++++++---- .../java/cn/hutool/core/text/csv/CsvRow.java | 18 +++++- .../hutool/core/text/csv/CsvReaderTest.java | 61 ++++++++++++++++++- hutool-core/src/test/resources/test_bean.csv | 4 ++ 6 files changed, 148 insertions(+), 24 deletions(-) create mode 100644 hutool-core/src/test/resources/test_bean.csv diff --git a/CHANGELOG.md b/CHANGELOG.md index 18249e244..c69bf49a7 100644 --- a/CHANGELOG.md +++ b/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修复 diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java index bc7e8c47a..b8b9844d1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/resource/ResourceUtil.java +++ b/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,41 +62,52 @@ 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 } return null; } + /** + * 从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); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java index d65bb56ee..f41987037 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java +++ b/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; /** @@ -143,17 +144,6 @@ public class CsvBaseReader implements Serializable { return read(FileUtil.getReader(path, charset)); } - /** - * 从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 * @@ -170,6 +160,52 @@ public class CsvBaseReader implements Serializable { return new CsvData(header, rows); } + /** + * 从Reader中读取CSV数据,结果为Map,读取后关闭Reader。
+ * 此方法默认识别首行为标题行。 + * + * @param reader Reader + * @return {@link CsvData},包含数据列表和行信息 + * @throws IORuntimeException IO异常 + */ + public List> readMapList(Reader reader) throws IORuntimeException { + // 此方法必须包含标题 + this.config.setContainsHeader(true); + + final List> result = new ArrayList<>(); + read(reader, (row) -> result.add(row.getFieldMap())); + return result; + } + + /** + * 从Reader中读取CSV数据并转换为Bean列表,读取后关闭Reader。
+ * 此方法默认识别首行为标题行。 + * + * @param Bean类型 + * @param reader Reader + * @param clazz Bean类型 + * @return Bean列表 + */ + public List read(Reader reader, Class clazz) { + // 此方法必须包含标题 + this.config.setContainsHeader(true); + + final List 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 /** diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvRow.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvRow.java index 0f76e3987..aaeedee39 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvRow.java +++ b/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 { /** * 获取标题与字段值对应的Map * - * @return an unmodifiable map of header names and field values of this row + * @return 标题与字段值对应的Map * @throws IllegalStateException CSV文件无标题行抛出此异常 */ public Map getFieldMap() { @@ -82,7 +84,7 @@ public final class CsvRow implements List { throw new IllegalStateException("No header available"); } - final Map fieldMap = new LinkedHashMap<>(headerMap.size()); + final Map fieldMap = new LinkedHashMap<>(headerMap.size(), 1); String key; Integer col; String val; @@ -96,6 +98,18 @@ public final class CsvRow implements List { return fieldMap; } + /** + * 一行数据转换为Bean对象 + * + * @param Bean类型 + * @param clazz bean类 + * @return Bean + * @since 5.3.6 + */ + public T toBean(Class clazz){ + return BeanUtil.mapToBean(getFieldMap(), clazz, true); + } + /** * 获取字段格式 * diff --git a/hutool-core/src/test/java/cn/hutool/core/text/csv/CsvReaderTest.java b/hutool-core/src/test/java/cn/hutool/core/text/csv/CsvReaderTest.java index 59d9e61fe..0db3d8142 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/csv/CsvReaderTest.java +++ b/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> 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 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; + } } diff --git a/hutool-core/src/test/resources/test_bean.csv b/hutool-core/src/test/resources/test_bean.csv new file mode 100644 index 000000000..40d6862d8 --- /dev/null +++ b/hutool-core/src/test/resources/test_bean.csv @@ -0,0 +1,4 @@ +姓名,gender,focus,age +张三,男,无,33 +李四,男,好对象,23 +王妹妹,女,特别关注,22 \ No newline at end of file