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

FormatDirection 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):

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:

examples/Phase1InteropExample