List/Map 导出到表格(使用注解和反射)
Java 的 POI 库可以用来创建和操作 Excel 表格,有时候我们只需要简单地将 List 或 Map 导出到表格,样板代码比较多,不够优雅。如果能像 Gson 那样,使用注解标记要导出的属性,就方便的多。
Github:https://github.com/imcloudfloating/ListToExcell
POI 的依赖:
<dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi</artifactId>
     <version>4.1.0</version>
</dependency>
1. 创建注解
package cloud.list2excel.annotation;
import org.apache.poi.hssf.util.HSSFColor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column{
    String title() default "";
    short fontSize() default 14;
    HSSFColor.HSSFColorPredefined fontColor() default HSSFColor.HSSFColorPredefined.BLACK;
    HSSFColor.HSSFColorPredefined borderColor() default HSSFColor.HSSFColorPredefined.BLACK;
}
创建一个 @Column 注解,用于标记字段
title:该字段在表格头部的名称,默认值为属性名。fontSize:字体大小,默认 14px。fontColor:字体颜色,默认黑色。borderColor:边框颜色,默认黑色。
2. 使用反射获取注解,用 POI 将数据写入到表格
这部分用的 Kotlin,但是反射和注解还是用的 Java,因为 Kotlin 反射获取的字段是排过序的,不是声明的顺序。
package cloud.list2excel.util
import cloud.list2excel.annotation.Column
import org.apache.poi.hssf.usermodel.HSSFCellStyle
import org.apache.poi.hssf.usermodel.HSSFSheet
import org.apache.poi.hssf.usermodel.HSSFWorkbook
import org.apache.poi.ss.usermodel.BorderStyle
/**
 * List/Map 导出 Excel 表格
 * @author Cloud
 */
object ListToExcel {
    private var workbook: HSSFWorkbook = HSSFWorkbook()
    private val cellStyles: MutableList<HSSFCellStyle> = ArrayList()
    /**
     * 处理单个 Sheet
     */
    fun from(data: List<Any>): HSSFWorkbook {
        toWorkbook("Sheet0", data)
        return workbook
    }
    /**
     * 处理多个 Sheet
     */
    fun from(data: Map<String, List<Any>>): HSSFWorkbook {
        if (data.isEmpty())
            return workbook
        for (sheet in data) {
            toWorkbook(sheet.key, sheet.value)
        }
        return workbook
    }
    private fun toWorkbook(sheetName: String, list: List<Any>) {
        val sheet = workbook.createSheet(sheetName)
        if (list.isEmpty())
            return
        val headers: MutableList<String> = ArrayList()
        val data: MutableList<MutableList<Any>> = ArrayList()
        // 获取注解并设置表头
        for (field in list[0].javaClass.declaredFields) {
            field.isAccessible = true
            val annotation = field.getAnnotation(Column::class.java)
            if (annotation != null) {
                headers.add(if (annotation.title == "") field.name else annotation.title)
                val cellStyle = workbook.createCellStyle().also { style ->
                    style.setFont(workbook.createFont().also {
                        it.fontHeightInPoints = annotation.fontSize
                        it.color = annotation.fontColor.index
                    })
                }
                cellStyle.run {
                    leftBorderColor = annotation.borderColor.index
                    topBorderColor = annotation.borderColor.index
                    rightBorderColor = annotation.borderColor.index
                    bottomBorderColor = annotation.borderColor.index
                    borderLeft = BorderStyle.THIN
                    borderTop = BorderStyle.THIN
                    borderRight = BorderStyle.THIN
                    borderBottom = BorderStyle.THIN
                }
                cellStyles.add(cellStyle)
            }
        }
        // 获取数据
        for (obj in list) {
            val rowData: MutableList<Any> = ArrayList()
            for (field in obj.javaClass.declaredFields) {
                field.isAccessible = true
                val annotation = field.getAnnotation(Column::class.java)
                if (annotation != null) {
                    val t = field.get(obj)
                    if (t == null) {
                        rowData.add("")
                    } else {
                        rowData.add(t)
                    }
                }
            }
            data.add(rowData)
        }
        setHeader(sheet, headers)
        setData(sheet, data)
    }
    /**
     * 设置表格头
     */
    private fun setHeader(sheet: HSSFSheet, headers: List<String>) {
        val row = sheet.createRow(0)
        for (i in headers.indices) {
            val cell = row.createCell(i)
            cell.setCellValue(headers[i])
            cell.setCellStyle(cellStyles[i])
        }
    }
    /**
     * 写入数据
     */
    private fun setData(sheet: HSSFSheet, data: List<List<Any>>) {
        for (i in data.indices) {
            val row = sheet.createRow(i + 1)
            for (j in data[i].indices) {
                val cell = row.createCell(j)
                cell.setCellValue(data[i][j].toString())
                cell.setCellStyle(cellStyles[j])
                sheet.autoSizeColumn(j)
            }
        }
    }
}
from() 的参数为 List 时,直接写入 workbook 然后返回,为 Map 时,将 Map 中的 List 逐个写入到 workbook,Map 的 key 作为 Sheet 的名称。
3. 使用
创建两个实体类测试:
package cloud.list2excel.util
import cloud.list2excel.annotation.Column
import org.apache.poi.hssf.util.HSSFColor
import java.sql.Date
data class Film(
    @Column(title = "ID", fontColor = HSSFColor.HSSFColorPredefined.RED)
    var id: Int? = null,
    @Column(title = "Title")
    var title: String? = null,
    @Column(title = "Release Date")
    var release_date: Date? = null,
    @Column(title = "Duration")
    var duration: String? = null
)
package cloud.list2excel.util
import cloud.list2excel.annotation.Column
import java.sql.Date
data class Actor(
    @Column(title = "ID")
    var id: Int? = null,
    @Column(title = "Full Name")
    var name: String? = null,
    @Column(title = "Birth")
    var birth: Date?=null
)
测试类:
此处写入了两个 Sheet。
package cloud.list2excel.util
import java.io.File
import java.sql.Date
class ListToExcelTest {
    private val films = listOf(
        Film(1, "Iron Man", Date.valueOf("2008-4-30"), "126 min"),
        Film(2, "Star Wars: Episode IV - A New Hope", Date.valueOf("1977-5-25"), "121 min"),
        Film(3, "Zootropolis", Date.valueOf("2016-3-4"), "109 min")
    )
    private val actors = listOf(
        Actor(1, "Robert John Downey Jr.", Date.valueOf("1965-4-4")),
        Actor(2, "Mark Hamill", Date.valueOf("1951-9-25")),
        Actor(3, "Ginnifer Goodwin", Date.valueOf("1978-5-22"))
    )
    private val data = mapOf(
        Pair("films", films),
        Pair("actors", actors)
    )
    @org.junit.Test
    fun toExcel() {
        val before = System.currentTimeMillis()
        val workbook = ListToExcel.from(data)
        val after = System.currentTimeMillis()
        println("Time Usage: ${after - before}ms")
        workbook.write(File("/home/data.xls"))
    }
}
导出结果:


效率似乎不怎么高 _
List/Map 导出到表格(使用注解和反射)的更多相关文章
- Spring Boot 导出Excel表格
		
Spring Boot 导出Excel表格 添加支持 <!--添加导入/出表格依赖--> <dependency> <groupId>org.apache.poi& ...
 - vue  导出excel表格
		
对于涉及到数据比较多的管理后台导出excel 表格这个需求就非常的常见了 所以? vue 怎么到处excel表格的? 有两种办法 1:请求接口后台直接给你的是excel文件,你需要做的就是怎么接收ex ...
 - poi导出word表格详解 超详细了
		
转:非常感谢原作者 poi导出word表格详解 2018年07月20日 10:41:33 Z丶royAl 阅读数:36138 一.效果如下 二.js代码 function export_word( ...
 - Maven项目结合POI导出Excl表格Demo-亲测可用
		
Maven项目结合POI导出Excl表格 一.POM文件添加依赖 <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> ...
 - 从数据库导出数据到excel之List<map>导出
		
说明:很多时候取出来的数据是封装为List<Map<String,Object>>,可以直接导出excel表格 项目说明就在 “上一篇” 直接上代码(数据层和业务层不用说了,查 ...
 - Java基于注解和反射导入导出Excel
		
代码地址如下:http://www.demodashi.com/demo/11995.html 1. 构建项目 使用Spring Boot快速构建一个Web工程,并导入与操作Excel相关的POI包以 ...
 - vue中导出Excel表格
		
项目中我们可能会碰到导出Excel文件的需求,一般后台管理系统中居多,将table中展示的数据导出保存到本地.当然我们也可以通过一些处理来修改要导出的数据格式,具体需求具体对待. 1.首先我们需要安装 ...
 - React+后端实现导出Excle表格的功能
		
最近在做一个基于React+antd前端框架的Excel导出功能,我主要在后端做了处理,根据以下步骤,可以很容易就实现导出Excel表格数据的功能. 在做这类导出文件的功能,其实,在后端进行处理,会更 ...
 - 在vue中导出excel表格
		
初学者学习vue开发,想把前端项目中导出Excel表格,查了众多帖子,踩了很多坑,拿出来与大家分享一下经验. 安装依赖 //npm npm install file-saver -S npm inst ...
 
随机推荐
- mysql系列2 权限相关
			
mysql授权认证 请注意(大坑):mysql8.0以前的版本可以使用grant在授权的时候隐式的创建用户,8.0以后已经不支持,所以必须先创建用户,然后再授权!! 例子: 在170mysql主机上授 ...
 - VS2013 EF6连接MySQL步骤
			
1.安装MySql的VS插件(版本请下载最新版)mysql-for-visualstudio-1.2.3.msihttp://cdn.mysql.com/Downloads/MySQL-for-Vis ...
 - vue - 基础(3)
			
1.数据的双向绑定 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
 - Vue中swiper手动滑动后不能自动播放的解决方法
			
用户操作swiper之后,是否禁止autoplay.默认为true:停止.如果设置为false,用户操作swiper之后自动切换不会停止,每次都会重新启动autoplay.操作包括触碰,拖动,点击pa ...
 - nodejs,npm 安装配置步骤
			
http://xiaoyaojones.blog.163.com/blog/static/28370125201351501113581/ 参照上述网址中的方法 特别强调一下,在第三步的时候,在命令行 ...
 - CSP复习与模板
			
P3366 [模板]最小生成树 Kruskal 算法因为只与边相关,则适合求稀疏图的最小生成树.而 Prim 算法因为只与顶点有关,所以适合求稠密图的最小生成树. Prim 是以更新过的节点的连边找最 ...
 - 使用WIFI网卡的AP功能
			
前几篇博客中,wifi无线网卡都工作于STA模式,那么它能否工作于AP模式.本篇博客就研究使wifi 无线网卡工作于AP模式.使用一个应用程序hostapd,关于它的介绍可以去此网站https://w ...
 - 《MySQL性能优化篇》阅读笔记
			
建表的时候,不要用null赋默认值,如:字符串的设置'',数据类型的设为0,不要将null设为默认值. 在MySQL中没有 full [outer] join,用union代替 各种 JOIN SQL ...
 - USACO Clumsy Cows
			
洛谷 P3056 [USACO12NOV]笨牛Clumsy Cows 洛谷传送门 JDOJ 2323: USACO 2012 Nov Silver 1.Clumsy Cows JDOJ传送门 Desc ...
 - vue-cli3.0启动项目,在局域网内其他电脑通过自己ip访问
			
最近一直在使用vue-cli3.0做项目, package.json中配置后,自启动项目,也就没留意过小黑窗, "scripts": { "serve": &q ...