Excel is useful for organizing and analyzing data, while Markdown tables work well in README files, technical documentation, and project reports.
Sometimes you need to move tabular data between these formats. For example, you might want to publish an Excel report as Markdown or import tables from a Markdown document into Excel for further editing.
In this article, we'll cover how to convert Excel to Markdown and back using Java, customize image and hyperlink output, and extract multiple Markdown tables into separate Excel worksheets.
1. Set Up the Java Project
First, add a spreadsheet processing library to your Java project.
Excel–Markdown conversion is supported starting from version 15.11.3. The examples below use version 16.10.1.
If you're using Maven, add the following repository and dependency to your pom.xml file:
<repositories>
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>https://repo.e-iceblue.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.xls</artifactId>
<version>16.10.1</version>
</dependency>
</dependencies>
The examples work with local XLSX and Markdown files. You don't need Microsoft Excel installed to run the conversion code.
2. Convert Excel to Markdown in Java
Suppose you have an Excel workbook containing a project status table that you want to include in a README or Markdown report.
You can load the workbook using Workbook.loadFromFile() and export it by specifying FileFormat.Markdown in saveToFile().
Here's the complete code:
import com.spire.xls.Workbook;
import com.spire.xls.FileFormat;
public class ExcelToMarkdown {
public static void main(String[] args) {
Workbook workbook = new Workbook();
try {
// Load the Excel workbook
workbook.loadFromFile("report.xlsx");
// Export the workbook to Markdown
workbook.saveToFile(
"report.md", FileFormat.Markdown);
} finally {
workbook.dispose();
}
}
}
The program generates report.md.
For example, if the Excel file contains this table:
| Department | Owner | Completion |
|---|---|---|
| Engineering | Alex | 92% |
| Marketing | Emma | 88% |
| Support | Daniel | 95% |
The exported Markdown will look similar to this:
| Department | Owner | Completion |
| --- | --- | --- |
| Engineering | Alex | 92% |
| Marketing | Emma | 88% |
| Support | Daniel | 95% |
The resulting table can be used in Markdown-supported platforms such as GitHub or documentation websites.
There is one limitation to keep in mind: Markdown tables don't support Excel formulas, merged cells, conditional formatting, or complex cell styles. If the workbook contains these features, review the converted content to make sure the table structure and displayed values are correct.
3. Customize Images and Hyperlinks When Exporting Excel to Markdown
When an Excel workbook contains images or hyperlinks, the conversion involves more than just cell values.
For example, if you're publishing the generated Markdown to GitHub, relative image paths can make it easier to keep the Markdown file and its images together. You may also prefer inline links or reference-style links, depending on how the document is maintained.
These export settings can be controlled through MarkdownOptions.
Two useful options are:
| Option | Purpose |
|---|---|
isSavePicInRelativePath(true) |
Uses relative paths for exported image references |
isSaveHyperlinkAsRef(true) |
Exports hyperlinks as Markdown reference-style links |
For example, an inline Markdown link looks like this:
[Project Website](https://www.example.com)
A reference-style link separates the visible link text from its URL:
[Project Website][ref1]
[ref1]: https://www.example.com
Both formats are valid Markdown. Inline links are straightforward, while reference-style links can be easier to maintain when the same URL appears multiple times.
Java Example: Export with Custom Markdown Options
The following example uses relative image paths and keeps hyperlinks in inline format.
import com.spire.xls.Workbook;
import com.spire.xls.MarkdownOptions;
public class ExcelToMarkdownWithOptions {
public static void main(String[] args) {
Workbook workbook = new Workbook();
try {
// Load the Excel workbook
workbook.loadFromFile("report.xlsx");
// Configure Markdown export options
MarkdownOptions options =
new MarkdownOptions();
// Use relative image paths
options.isSavePicInRelativePath(true);
// Use inline hyperlinks
options.isSaveHyperlinkAsRef(false);
// Export with the specified options
workbook.saveToMarkdown(
"report_custom.md", options);
} finally {
workbook.dispose();
}
}
}
Unlike the previous example, this one uses saveToMarkdown() and passes a MarkdownOptions object.
To export reference-style hyperlinks instead, change:
options.isSaveHyperlinkAsRef(false);
to:
options.isSaveHyperlinkAsRef(true);
Relative image paths are useful when keeping Markdown files in a project repository. However, you'll still need to include the exported image files and preserve their relative directory structure when moving or publishing the document.
4. Convert Markdown to Excel in Java
The conversion also works in the other direction.
Suppose a project maintains task lists in Markdown, but you want to move the data into Excel for sorting, filtering, or further analysis.
You can load the Markdown file using Workbook.loadFromMarkdown() and save it as an XLSX workbook.
Here's the complete example:
import com.spire.xls.Workbook;
import com.spire.xls.ExcelVersion;
public class MarkdownToExcel {
public static void main(String[] args) {
Workbook workbook = new Workbook();
try {
// Load the Markdown file
workbook.loadFromMarkdown("tasks.md");
// Save as an Excel workbook
workbook.saveToFile(
"tasks.xlsx",
ExcelVersion.Version2016);
} finally {
workbook.dispose();
}
}
}
Suppose tasks.md contains:
| Task | Owner | Status |
| --- | --- | --- |
| API Development | Alex | Completed |
| UI Improvements | Emma | In Progress |
| Functional Testing | Daniel | Pending |
After running the program, you'll get tasks.xlsx, which you can open in Excel and continue editing.
One thing worth checking after conversion is the cell data type.
Markdown tables store their content as text and don't define Excel-specific data types. If a table contains numbers, dates, or percentages, check whether they're imported as the appropriate Excel values.
This matters if you plan to use formulas, calculations, charts, or date-based operations on the imported data.
5. Extract Multiple Markdown Tables into Separate Excel Worksheets
Direct conversion is useful when you want to import an entire Markdown file. But sometimes a Markdown document contains several tables mixed with headings and explanatory text, and you only need the table data.
For example, consider a regional sales report containing two tables:
# Regional Sales Report
## East Region
| Product | Units Sold | Revenue |
| --- | ---: | ---: |
| Product A | 120 | 18000 |
| Product B | 85 | 12750 |
## South Region
| Product | Units Sold | Revenue |
| --- | ---: | ---: |
| Product A | 96 | 14400 |
| Product B | 110 | 16500 |
Instead of importing the document as a whole, we can extract each table and place it in its own Excel worksheet.
The following example uses standard Java code to read the Markdown file, identify table headers and separator rows, and write each detected table to a separate worksheet.
Complete Java Example
import com.spire.xls.Workbook;
import com.spire.xls.Worksheet;
import com.spire.xls.ExcelVersion;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class MarkdownTablesToExcel {
public static void main(String[] args) throws Exception {
// Read the Markdown file
List<String> lines = Files.readAllLines(
Paths.get("report.md"),
StandardCharsets.UTF_8);
Workbook workbook = new Workbook();
int tableCount = 0;
boolean inCodeBlock = false;
char fenceChar = '\0';
int fenceLength = 0;
try {
// Remove default worksheets
workbook.getWorksheets().clear();
for (int i = 0; i < lines.size() - 1; i++) {
String line = lines.get(i).trim();
// Skip fenced code blocks
if (inCodeBlock) {
int length =
countLeading(line, fenceChar);
if (length >= fenceLength
&& line.substring(length)
.trim().isEmpty()) {
inCodeBlock = false;
}
continue;
}
int backticks = countLeading(line, '`');
int tildes = countLeading(line, '~');
if (backticks >= 3 || tildes >= 3) {
fenceChar = backticks >= 3
? '`' : '~';
fenceLength =
Math.max(backticks, tildes);
inCodeBlock = true;
continue;
}
// Check for a possible table header
if (!isTableRow(line)) {
continue;
}
List<String> headers = splitCells(line);
List<String> separators =
splitCells(lines.get(i + 1).trim());
// Validate the header and separator row
if (headers.isEmpty()
|| headers.size() != separators.size()
|| !isSeparator(separators)) {
continue;
}
// Create a worksheet for this table
Worksheet sheet = workbook.getWorksheets()
.add("Table" + (++tableCount));
// Write the headers
for (int col = 0;
col < headers.size(); col++) {
sheet.getCellRange(1, col + 1)
.setText(headers.get(col));
sheet.getCellRange(1, col + 1)
.getStyle()
.getFont()
.isBold(true);
}
// Write the table rows
int row = 2;
int j = i + 2;
while (j < lines.size()
&& isTableRow(
lines.get(j).trim())) {
List<String> cells =
splitCells(lines.get(j).trim());
if (cells.size() != headers.size()) {
break;
}
for (int col = 0;
col < cells.size(); col++) {
sheet.getCellRange(row, col + 1)
.setText(cells.get(col));
}
row++;
j++;
}
// Adjust column widths
sheet.getAllocatedRange()
.autoFitColumns();
// Continue after the current table
i = j - 1;
}
if (tableCount == 0) {
System.out.println(
"No Markdown tables found.");
return;
}
// Save the workbook
workbook.saveToFile(
"tables.xlsx",
ExcelVersion.Version2016);
System.out.println(
"Extracted " + tableCount + " tables.");
} finally {
workbook.dispose();
}
}
// Count consecutive marker characters
private static int countLeading(
String line, char marker) {
int i = 0;
while (i < line.length()
&& line.charAt(i) == marker) {
i++;
}
return i;
}
// Check whether the line is a pipe-delimited row
private static boolean isTableRow(String line) {
return line.startsWith("|")
&& line.endsWith("|");
}
// Validate the Markdown table separator
private static boolean isSeparator(
List<String> cells) {
if (cells.isEmpty()) {
return false;
}
for (String cell : cells) {
if (!cell.matches(":?-+:?")) {
return false;
}
}
return true;
}
// Split a Markdown row into cells
private static List<String> splitCells(String line) {
List<String> cells = new ArrayList<>();
if (!isTableRow(line)) {
return cells;
}
String content =
line.substring(1, line.length() - 1);
StringBuilder cell = new StringBuilder();
for (int i = 0; i < content.length(); i++) {
char ch = content.charAt(i);
// Handle escaped pipes: \|
if (ch == '\\'
&& i + 1 < content.length()
&& content.charAt(i + 1) == '|') {
cell.append('|');
i++;
} else if (ch == '|') {
cells.add(cell.toString().trim());
cell.setLength(0);
} else {
cell.append(ch);
}
}
cells.add(cell.toString().trim());
return cells;
}
}
How It Works
The code reads the Markdown file line by line and looks for table headers followed by valid separator rows, such as:
| --- | ---: | :---: |
Once a table is found, it creates a worksheet and writes the table data into cells.
A few details are worth highlighting:
- Table detection: The code checks for a header row followed by a valid Markdown separator row.
- Code blocks: Fenced code blocks using backticks or tildes are skipped, so Markdown table examples inside those blocks aren't treated as actual tables.
-
Cell parsing: The
splitCells()method separates table cells and handles simple escaped pipe characters (\|). - Excel output: Each detected table gets its own worksheet, with bold headers and automatically adjusted column widths.
For the regional sales report above, the generated tables.xlsx contains two worksheets:
| Worksheet | Content |
|---|---|
| Table1 | East Region sales data |
| Table2 | South Region sales data |
The tables are kept separate, making it easier to work with each region's data in Excel.
Limitations of This Approach
This example is intended for regular Markdown pipe tables with leading and trailing | characters.
It handles common separator rows and simple escaped pipes, but it isn't a complete Markdown parser. More complex cases, such as pipes inside inline code, nested structures, or unusual escaping rules, may require a dedicated Markdown parsing library.
Also, the example uses setText() when writing cell values:
sheet.getCellRange(row, col + 1)
.setText(cells.get(col));
This means the imported values are stored as text, including numbers such as 120 or 18000.
If you need to perform calculations on the imported data, you can detect numeric values and write them using setNumberValue() instead.
6. Common Issues When Converting Between Excel and Markdown
What Happens to Excel Formulas and Formatting?
Markdown tables don't support Excel formulas, merged cells, conditional formatting, or complex cell styles.
As a result, converting a workbook to Markdown won't preserve all of these features. If your workbook contains formulas, make sure the calculated values are up to date before exporting, and check the resulting Markdown table.
For worksheets with merged cells or multi-level headers, you may also need to reorganize the data into a simpler table before conversion.
Why Doesn't the Markdown Table Render Correctly?
Markdown table rendering depends on both the table syntax and the Markdown renderer.
A table may not display as expected if its rows have inconsistent column counts or if the original Excel layout contains structures that standard Markdown tables cannot represent.
For better compatibility, use a regular table layout with consistent columns and a valid separator row.
Why Are Numbers Imported as Text?
Markdown doesn't carry Excel cell type information.
When importing data, numeric-looking values may need to be converted explicitly before you can use them reliably in calculations.
For example, when writing values into an Excel worksheet:
// Write a text value
sheet.getCellRange("A1").setText("1200");
// Write a numeric value
sheet.getCellRange("A2").setNumberValue(1200);
The first cell contains text, while the second contains a numeric value.
If the imported workbook will be used for formulas, averages, charts, or date calculations, it's worth handling the relevant data types during the import process.
7. Conclusion
Converting between Excel and Markdown in Java can help when spreadsheet data needs to move into documentation or Markdown tables need to be brought back into Excel.
For straightforward conversions, you can load one format and save it as the other. When exporting Excel to Markdown, additional options let you control how image paths and hyperlinks are written.
If a Markdown document contains several tables and you only need their data, extracting each table into a separate Excel worksheet provides more control over the result.
The approach you choose depends on whether you need to convert an entire file or work with individual tables.
Top comments (0)