Java POI Interoperability
dotnet-poi maintains bidirectional compatibility with Apache POI (Java). Files written by one can be read by the other, and vice versa.
Test Framework
The interop test suite lives in tests/DotnetPoi.Interop.Tests/:
tests/DotnetPoi.Interop.Tests/
├── java/ # Maven project with Apache POI dependency
│ ├── pom.xml
│ └── src/test/java/
│ ├── WriteForDotnetTest.java # POI writes → C# reads
│ └── ReadFromDotnetTest.java # C# writes → POI reads
├── cs/
│ ├── ReadPoiGeneratedTests.cs # C# reads POI's output
│ └── WriteForPoiTests.cs # C# writes for POI to read
└── fixtures/
├── from-poi/ # Files generated by Java POI
└── from-dotnet-poi/ # Files generated by dotnet-poi
Bidirectional Verification
Direction A: Java POI writes an xlsx/xls/docx/pptx fixture → dotnet-poi reads it and asserts the supported values, styles, and structural elements match.
Direction B: dotnet-poi writes or no-op saves a fixture → Java POI reads it and asserts the supported content matches.
Both directions run in CI on every push.
Verified Formats
| Format | Direction A (POI → C#) | Direction B (C# → POI) |
|---|---|---|
| xlsx (XSSF) | ✅ Comprehensive | ✅ Comprehensive |
| xls (HSSF) | ⚠️ Basic + styles/layout/unicode/comprehensive fixtures | ⚠️ Basic + styles/layout/unicode/comprehensive fixtures |
| docx (XWPF) | ✅ Comprehensive | ✅ Comprehensive |
| doc (HWPF) | — | ⚠️ No-op saved .doc smoke |
| pptx (XSLF) | ✅ Comprehensive | ✅ Comprehensive |
| ppt (HSLF) | ⚠️ Fixture survey / text extraction | ⚠️ C# no-op fixture generation; Java assertion pending |
| xlsm (macro) | N/A | ✅ |
| docm (macro) | N/A | ✅ |
| pptm (macro) | N/A | ✅ |
XML Byte-Level Parity
For most formats, semantic compatibility is the goal: values, styles, and structure survive the round-trip. Byte-level XML parity is only pursued when a concrete interop failure cannot be fixed by semantic means.
Minor XML divergences that do not affect interop (Excel/LibreOffice/POI ignore them):
fileVersion,workbookPrmetadata attributesworkbookViewwindow sizesspansattribute on rowss="0"style attribute on cells
Running Interop Tests Locally
# Build .NET side
dotnet build DotnetPOI.sln
# Direction A: POI writes → C# reads
mvn test -f tests/DotnetPoi.Interop.Tests/java/pom.xml -Dtest=WriteForDotnetTest
dotnet test tests/DotnetPoi.Interop.Tests/ --filter "Category=ReadFromPoi"
# Direction B: C# writes → POI reads
dotnet test tests/DotnetPoi.Interop.Tests/ --filter "Category=WriteForPoi"
mvn test -f tests/DotnetPoi.Interop.Tests/java/pom.xml -Dtest=ReadFromDotnetTest
Requirements: .NET SDK, Java 17+, Maven.
Full Runnable Example
See examples/Phase1InteropExample/ for a worked example: