C#无原生OpenDocument支持,读取纯文本可用ZipFile+XPath解析content.xml,写入必须依赖LibreOffice UNO API;ODF Toolkit+IKVM已废弃,小众NuGet包不可靠。

OpenDocument格式在C#中没有原生支持
微软.NET框架本身不提供对.odt(文字)、.ods(表格)等OpenDocument格式的读写能力。你不能直接用System.IO.Packaging或WordprocessingDocument这类API处理它们——那些是为OOXML(.docx/.xlsx)设计的。
OpenDocument是ZIP压缩包+XML结构,但内部命名空间、文档模型、样式逻辑和OOXML完全不同,强行解析会掉进元数据校验、样式继承、单元格跨行/合并、公式表达式等深坑。
推荐方案:用ODF Toolkit(Java)还是LibreOffice SDK?
目前没有成熟、活跃、纯C#实现的OpenDocument库。主流可行路径只有两条:
-
调用LibreOffice命令行(最稳定):用
soffice --headless --convert-to转格式,或通过UNO API远程控制LibreOffice进程读写.odt/.ods。适合服务端有可控环境、允许启动外部进程的场景。 -
用
ODF Toolkit+ IKVM.NET(已废弃,不推荐):曾有人用IKVM将Java版ODF Toolkit转为.NET DLL,但IKVM停止维护,.NET Core/.NET 5+完全不兼容,运行时极易抛NoClassDefFoundError或ClassNotFoundException。
别碰任何声称“纯C# ODF解析器”的小众NuGet包——它们通常只支持极简文本提取,无法处理样式、表格结构、页眉页脚、图片嵌入等基本需求,一写入就破坏文件校验签名,LibreOffice打开直接报“损坏的文件”。
如果只要读取纯文本内容,可用System.IO.Compression手动解压+XPath
OpenDocument文件本质是ZIP,核心内容在content.xml里。若你只需要提取所有段落文字(无格式、无表格结构),可走轻量路径:
- 用
ZipFile.OpenRead打开.odt文件 - 定位到
content.xml流,用XDocument.Load加载 - 用XPath
//text:p | //text:h提取文本节点(注意命名空间:xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0") - 跳过
text:tab、text:line-break等控制符,text:span内样式信息直接丢弃
示例关键代码片段:
using (var archive = ZipFile.OpenRead(filePath))
{
var entry = archive.GetEntry("content.xml");
using (var stream = entry.Open())
using (var reader = XmlReader.Create(stream))
{
var doc = XDocument.Load(reader);
XNamespace textNs = "urn:oasis:names:tc:opendocument:xmlns:text:1.0";
var texts = doc.Descendants(textNs + "p")
.Select(p => p.Nodes().OfType().Aggregate("", (a, x) => a + x.Value))
.Where(s => !string.IsNullOrWhiteSpace(s));
}
} 这方法快、无依赖,但仅限“看个大概”。一旦遇到嵌套列表、带样式的标题、表格中的单元格内容,XPath就会漏数据或错位。
写入.odt/.ods几乎必须依赖LibreOffice UNO API
生成合规的.odt文件不是拼XML再打包那么简单。需要动态生成meta.xml、styles.xml、settings.xml,还要计算manifest.xml校验和,且样式定义必须与内容引用严格匹配。手写99%会失败。
真正可行的写入方式只有一种:
- 安装LibreOffice(建议7.4+,避免老版本UNO接口变更)
- NuGet引用
Uno.Core(非官方,需自行从LibreOffice SDK提取cli_ure.dll和cli_oootypes.dll) - 用C#调用UNO接口创建
XComponentLoader,加载空模板,插入段落/表格,再用XStorable.storeAsURL保存 - 注意:UNO是跨语言COM式接口,对象生命周期管理松散,不显式
dispose会导致LibreOffice进程残留
这个路径能保功能正确,但部署复杂、启动慢、内存占用高。不适合高频、低延迟场景。
真正难的不是“怎么写”,而是“怎么确保写出的文件能在不同版本LibreOffice、OnlyOffice、甚至Google Docs导入时不变形”。OpenDocument标准本身宽松,各家实现差异大,测试成本远高于开发成本。










