dotnet OpenXML 已知问题 设置 0x0001 等 XML 不合法字符给到标题将在保存时抛出异常
此问题报告给了官方: https://github.com/dotnet/Open-XML-SDK/issues/1874
最简复现代码:
using System;
using (var presentationDocument =
DocumentFormat.OpenXml.Packaging.PresentationDocument.Open("Test.pptx", true))
{
presentationDocument.PackageProperties.Title = "\u0001";
}
以上的 Test.pptx 是任意一份正常的 PPTX 文件
运行以上代码,可以看到如下异常
System.ArgumentException:“'', hexadecimal value 0x01, is an invalid character.”
System.Private.Xml.dll!System.Xml.XmlUtf8RawTextWriter.InvalidXmlChar(int ch, byte* pDst, bool entitize)
System.Private.Xml.dll!System.Xml.XmlUtf8RawTextWriter.WriteElementTextBlock(char* pSrc, char* pSrcEnd)
System.Private.Xml.dll!System.Xml.XmlUtf8RawTextWriter.WriteString(string text)
System.Private.Xml.dll!System.Xml.XmlWellFormedWriter.WriteString(string text)
System.IO.Packaging.dll!System.IO.Packaging.PartBasedPackageProperties.SerializeDirtyProperties()
System.IO.Packaging.dll!System.IO.Packaging.PartBasedPackageProperties.Flush()
System.IO.Packaging.dll!System.IO.Packaging.PartBasedPackageProperties.Close()
System.IO.Packaging.dll!System.IO.Packaging.Package.System.IDisposable.Dispose()
System.IO.Packaging.dll!System.IO.Packaging.Package.Close()
DocumentFormat.OpenXml.Framework.dll!DocumentFormat.OpenXml.Features.StreamPackageFeature.Dispose(bool disposing)
DocumentFormat.OpenXml.Framework.dll!DocumentFormat.OpenXml.Features.StreamPackageFeature.Dispose()
DocumentFormat.OpenXml.Framework.dll!DocumentFormat.OpenXml.Packaging.PackageFeatureCollection.DocumentFormat.OpenXml.Features.IContainerDisposableFeature.Dispose()
DocumentFormat.OpenXml.Framework.dll!DocumentFormat.OpenXml.Packaging.OpenXmlPackage.Dispose(bool disposing)
DocumentFormat.OpenXml.Framework.dll!DocumentFormat.OpenXml.Packaging.OpenXmlPackage.Dispose()
> FukemqibairLaylalljerowhem.dll!Program.<Main>$(string[] args)
本文的测试代码放在 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 6baba8b5407c1d23119d3ac150b1ab5af4cd810c
以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 6baba8b5407c1d23119d3ac150b1ab5af4cd810c
获取代码之后,进入 Pptx/FukemqibairLaylalljerowhem 文件夹,即可获取到源代码
这个异常是在更底层的 dotnet runtime 里面抛出的,详细请看 https://github.com/dotnet/runtime/blob/c1fe87ad88532f0e80de3739fe7b215e6e1f8b90/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriter.cs#L932
可选规避的方法是写一个过滤方法,过滤掉 XML 不合法字符,以下是我给出的过滤代码
internal static class XmlSafeTextContentHelper
{
public static string ToSafeXmlText(string text, char replaceChar = '_')
{
// 第一遍是跑一下,因为正常是不会有奇怪的字符的,那就进入快速分支
// 如果能够找到首个奇怪的字符,则进入慢分支,重新拼装替代字符
var index = -1;
for (var i = 0; i < text.Length; i++)
{
var c = text[i];
if (!XmlConvert.IsXmlChar(c))
{
index = i;
break;
}
}
var canFindInvalidChar = index >= 0;
if (canFindInvalidChar)
{
// 慢分支,开始重新拼装字符串
var stringBuilder = new StringBuilder(text.Length);
stringBuilder.Append(text, 0, index);
for (var i = index; i < text.Length; i++)
{
var c = text[i];
if (XmlConvert.IsXmlChar(c))
{
stringBuilder.Append(c);
}
else
{
stringBuilder.Append(replaceChar);
}
}
return stringBuilder.ToString();
}
else
{
return text;
}
}
}
确保给到 PackageProperties 等属性的字符串都是经过过滤的
更多技术博客,请参阅 Office 使用 OpenXML SDK 解析文档博客目录
博客园博客只做备份,博客发布就不再更新,如果想看最新博客,请访问 https://blog.lindexi.com/
如图片看不见,请在浏览器开启不安全http内容兼容

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名[林德熙](https://www.cnblogs.com/lindexi)(包含链接:https://www.cnblogs.com/lindexi ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我[联系](mailto:lindexi_gd@163.com)。