Backend Development 10 min read

Generic Excel Export for Arbitrary Java Objects via Reflection

The article shows how to generically export any Java object's data to an Excel‑compatible CSV file by using reflection to convert objects into maps, building headers (optionally via a @JcExcelName annotation) and writing the result with a reusable utility class and Maven‑managed Apache POI, FastJSON, and Commons‑IO dependencies, illustrated through SpringBoot controller and main‑method examples.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Generic Excel Export for Arbitrary Java Objects via Reflection

This article demonstrates how to export data of any Java class to an Excel (CSV) file without knowing the class structure in advance.

The approach uses the CSV format as an intermediate representation and Java reflection to read field names and values from a list of objects, then writes the data to a file.

Key steps:

Define Maven dependencies (Apache POI, FastJSON, Commons‑IO).

Create a utility class MyCsvFileUtil that provides methods to build file names, header rows, and body content by converting objects to maps via reflection.

Implement a generic toMap method that extracts all fields (excluding serialVersionUID ) using getter methods.

Provide helper methods buildCsvFileTableNames , buildCsvFileBodyMap , and file‑writing methods.

An optional annotation @JcExcelName allows custom column titles. A resolver method resolveExcelTableName reads this annotation to generate a header row with user‑defined names.

Example usage in a SpringBoot controller shows how to export a list of District objects, and a simple main method demonstrates exporting a User object with custom headers.

Running the endpoints produces an Excel file where the first row contains field names (or annotation values) and subsequent rows contain the corresponding data.

All code snippets are included unchanged within ... blocks.

Maven 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>

Utility class (excerpt):

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 = "\r\n";
    public 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
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
> mapList = new ArrayList<>();
        for (Object o : dataLists) {
            mapList.add(toMap(o));
        }
        StringBuilder lineBuilder = new StringBuilder();
        for (Map
rowData : mapList) {
            for (String key : rowData.keySet()) {
                Object value = rowData.get(key);
                lineBuilder.append(value != null ? value : "--").append(CSV_DELIMITER);
            }
            lineBuilder.append(CSV_TAIL);
        }
        return lineBuilder.toString();
    }

    public static
Map
toMap(T entity) {
        Class
bean = entity.getClass();
        Field[] fields = bean.getDeclaredFields();
        Map
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;
    }
}

Custom annotation for column names:

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

Controller example:

@RequestMapping("/createCsvFileJcTest")
public void createCsvFileJcTest() {
    List
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);
}

Main method demonstrating custom headers:

public static void main(String[] args) {
    User user = new User();
    List
nameList = MapUtils.resolveExcelTableName(user);
    String tableNames = buildCsvFileTableNamesNew(nameList);
    System.out.println(nameList);
    System.out.println(tableNames);
}
JavaReflectionSpringBootannotationUtilityCSVExcel Export
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

0 followers
Reader feedback

How this landed with the community

login 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.