Export Any Java Object to CSV/Excel with Reflection – A Universal Utility

This tutorial demonstrates how to create a universal CSV/Excel export utility in Java that can handle objects of any class by using reflection to generate headers and rows, leveraging Apache POI and Spring Boot for seamless file generation.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Export Any Java Object to CSV/Excel with Reflection – A Universal Utility

Introduction

In this article we show how to export objects of any class to a CSV/Excel file without limiting the type of the class, using Java reflection and Apache POI.

Core Idea

The class type is unknown (User, Student, District, etc.). The utility iterates a list of such objects, extracts field names via reflection for the header row, and writes field values to the CSV body.

Implementation Steps

1. Dependencies

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.15</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-scratchpad</artifactId>
    <version>3.15</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.69</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
</dependency>

2. Utility Class

The MyCsvFileUtil class provides methods to create CSV files, write files, build file names, generate header rows, convert objects to maps, and assemble the CSV body.

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import java.io.*;
import java.lang.reflect.*;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
public class MyCsvFileUtil {
    public static final String FILE_SUFFIX = ".csv";
    public static final String CSV_DELIMITER = ",";
    public static final String CSV_TAIL = "
";
    protected static final String DATE_STR_FILE_NAME = "yyyyMMddHHmmssSSS";

    public static void createCsvFile(String savePath, String contextStr) throws IOException {
        File file = new File(savePath);
        file.createNewFile();
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(contextStr.getBytes("gbk"));
        fos.flush();
        fos.close();
    }

    public static void writeFile(String fileName, String content) {
        FileOutputStream fos = null;
        OutputStreamWriter writer = null;
        try {
            fos = new FileOutputStream(fileName, true);
            writer = new OutputStreamWriter(fos, "GBK");
            writer.write(content);
            writer.flush();
        } catch (Exception e) {
            log.error("写文件异常|{}", e);
        } finally {
            if (fos != null) IOUtils.closeQuietly(fos);
            if (writer != null) IOUtils.closeQuietly(writer);
        }
    }

    public static String buildCsvFileFileName(List dataList) {
        return dataList.get(0).getClass().getSimpleName() + new SimpleDateFormat(DATE_STR_FILE_NAME).format(new Date()) + FILE_SUFFIX;
    }

    public static String buildCsvFileTableNames(List dataList) {
        Map<String, Object> map = toMap(dataList.get(0));
        StringBuilder tableNames = new StringBuilder();
        for (String key : map.keySet()) {
            tableNames.append(key).append(CSV_DELIMITER);
        }
        return tableNames.append(CSV_TAIL).toString();
    }

    public static String buildCsvFileBodyMap(List dataLists) {
        List<Map<String, Object>> mapList = new ArrayList<>();
        for (Object o : dataLists) {
            mapList.add(toMap(o));
        }
        StringBuilder lineBuilder = new StringBuilder();
        for (Map<String, Object> rowData : mapList) {
            for (String key : rowData.keySet()) {
                Object value = rowData.get(key);
                if (Objects.nonNull(value)) {
                    lineBuilder.append(value).append(CSV_DELIMITER);
                } else {
                    lineBuilder.append("--").append(CSV_DELIMITER);
                }
            }
            lineBuilder.append(CSV_TAIL);
        }
        return lineBuilder.toString();
    }

    public static <T> Map<String, Object> toMap(T entity) {
        Class<?> bean = entity.getClass();
        Field[] fields = bean.getDeclaredFields();
        Map<String, Object> map = new HashMap<>(fields.length);
        for (Field field : fields) {
            try {
                if (!"serialVersionUID".equals(field.getName())) {
                    String methodName = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
                    Method method = bean.getDeclaredMethod(methodName);
                    Object fieldValue = method.invoke(entity);
                    map.put(field.getName(), fieldValue);
                }
            } catch (Exception e) {
                log.warn("toMap() Exception={}", e.getMessage());
            }
        }
        return map;
    }
}

3. Usage Example

A Spring MVC controller method demonstrates how to export a list of District objects.

@RequestMapping("/createCsvFileJcTest")
public void createCsvFileJcTest() {
    List<District> districts = districtService.queryByParentCodes(Arrays.asList("110100"));
    String fileName = "D:\\mycsv\\" + MyCsvFileUtil.buildCsvFileFileName(districts);
    String tableNames = MyCsvFileUtil.buildCsvFileTableNames(districts);
    MyCsvFileUtil.writeFile(fileName, tableNames);
    String contentBody = MyCsvFileUtil.buildCsvFileBodyMap(districts);
    MyCsvFileUtil.writeFile(fileName, contentBody);
}

Extension with Custom Annotation

Define @JcExcelName to specify custom column titles, and use resolveExcelTableName to retrieve them.

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface JcExcelName {
    String name() default "";
}

public static <T> List<String> resolveExcelTableName(T entity) {
    List<String> tableNamesList = new ArrayList<>();
    Class<?> bean = entity.getClass();
    Field[] fields = bean.getDeclaredFields();
    for (Field field : fields) {
        try {
            if (!"serialVersionUID".equals(field.getName())) {
                String tableTitleName = field.getName();
                JcExcelName ann = field.getAnnotation(JcExcelName.class);
                if (ann != null && StringUtils.hasLength(ann.name())) {
                    tableTitleName = ann.name();
                }
                tableNamesList.add(tableTitleName);
            }
        } catch (Exception e) {
            log.warn("toMap() Exception={}", e.getMessage());
        }
    }
    return tableNamesList;
}

Combine the custom titles with the CSV builder to produce a nicely formatted Excel file.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaReflectionSpring BootCSVExcelApache POI
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.