C# 使用OpenXMLSDK在Word文档中简单地替换令牌
我有一个要求,我希望用户在Word文档中键入一些字符串标记,以便可以通过C#应用程序用一些值替换它们。假设我有一个与图片一致的文档 现在使用SDK,我可以阅读以下文档:C# 使用OpenXMLSDK在Word文档中简单地替换令牌,c#,openxml,C#,Openxml,我有一个要求,我希望用户在Word文档中键入一些字符串标记,以便可以通过C#应用程序用一些值替换它们。假设我有一个与图片一致的文档 现在使用SDK,我可以阅读以下文档: private void InternalParseTags(WordprocessingDocument aDocumentToManipulate) { StringBuilder sbDocumentText = new StringBuilder(); using (Stre
private void InternalParseTags(WordprocessingDocument aDocumentToManipulate)
{
StringBuilder sbDocumentText = new StringBuilder();
using (StreamReader sr = new StreamReader(aDocumentToManipulate.MainDocumentPart.GetStream()))
{
sbDocumentText.Append(sr.ReadToEnd());
}
但是,由于这是原始XML,我无法轻松搜索标记,因为底层XML如下所示:
<w:t><:</w:t></w:r><w:r w:rsidR="002E53FF" w:rsidRPr="000A794A"><w:t>Person.Meta.Age
:Person.Meta.Age
(显然不是我能控制的)而不是我所希望的:
<w:t><: Person.Meta.Age
:Person.Meta.Age
或
这是OpenXML的一个棘手问题。我遇到的最佳解决方案如下所述:
基本上,Eric会扩展内容,使每个角色都独立运行,然后查找启动“的运行,而不是直接查找/替换令牌,使用OpenXML,您可以使用一些基于第三方OpenXML的模板,使用起来很简单,而且很快就能收回成本 正如Scanny指出的,OpenXML充满了令人讨厌的细节,人们必须逐一掌握这些细节。学习曲线又长又陡。如果你想成为OpenXML的大师,那么就去努力吧,开始攀登。如果您想有时间享受体面的社交生活,还有其他选择:只需选择一个基于OpenXML的第三方工具包。我已经评估过了。它提供了基于模板的方法,您可以准备一个模板,该模板是Word格式的文件,其中包含用于在运行时从应用程序合并的数据的占位符。它们都支持MS Word支持的任何格式,您可以使用条件内容、表格等 您还可以使用DOM方法创建或更改文档。最终文件可以是.docx或.pdf Docentric是许可产品,但您很快就会通过使用这些工具节省的时间来补偿成本
如果要在服务器上运行应用程序,请不要使用interop-有关详细信息,请参阅此链接:()。以下是我快速拼凑的一些代码,用于解释xml中跨运行的令牌。我对图书馆了解不多,但我能把它用起来。由于所有的循环,这也可能需要一些性能增强
/// <summary>
/// Iterates through texts, concatenates them and looks for tokens to replace
/// </summary>
/// <param name="texts"></param>
/// <param name="tokenNameValuePairs"></param>
/// <returns>T/F whether a token was replaced. Should loop this call until it returns false.</returns>
private bool IterateTextsAndTokenReplace(IEnumerable<Text> texts, IDictionary<string, object> tokenNameValuePairs)
{
List<Text> tokenRuns = new List<Text>();
string runAggregate = String.Empty;
bool replacedAToken = false;
foreach (var run in texts)
{
if (run.Text.Contains(prefixTokenString) || runAggregate.Contains(prefixTokenString))
{
runAggregate += run.Text;
tokenRuns.Add(run);
if (run.Text.Contains(suffixTokenString))
{
if (possibleTokenRegex.IsMatch(runAggregate))
{
string possibleToken = possibleTokenRegex.Match(runAggregate).Value;
string innerToken = possibleToken.Replace(prefixTokenString, String.Empty).Replace(suffixTokenString, String.Empty);
if (tokenNameValuePairs.ContainsKey(innerToken))
{
//found token!!!
string replacementText = runAggregate.Replace(prefixTokenString + innerToken + suffixTokenString, Convert.ToString(tokenNameValuePairs[innerToken]));
Text newRun = new Text(replacementText);
run.InsertAfterSelf(newRun);
foreach (Text runToDelete in tokenRuns)
{
runToDelete.Remove();
}
replacedAToken = true;
}
}
runAggregate = String.Empty;
tokenRuns.Clear();
}
}
}
return replacedAToken;
}
以及调用函数的一些示例:
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(memoryStream, true))
{
bool replacedAToken = true;
//continue to loop document until token's have not bee replaced. This is because some tokens are spread across 'runs' and may need a second iteration of processing to catch them.
while (replacedAToken)
{
//get all the text elements
IEnumerable<Text> texts = wordDoc.MainDocumentPart.Document.Body.Descendants<Text>();
replacedAToken = this.IterateTextsAndTokenReplace(texts, tokenNameValuePairs);
}
wordDoc.MainDocumentPart.Document.Save();
foreach (FooterPart footerPart in wordDoc.MainDocumentPart.FooterParts)
{
if (footerPart != null)
{
Footer footer = footerPart.Footer;
if (footer != null)
{
replacedAToken = true;
while (replacedAToken)
{
IEnumerable<Text> footerTexts = footer.Descendants<Text>();
replacedAToken = this.IterateTextsAndTokenReplace(footerTexts, tokenNameValuePairs);
}
footer.Save();
}
}
}
foreach (HeaderPart headerPart in wordDoc.MainDocumentPart.HeaderParts)
{
if (headerPart != null)
{
Header header = headerPart.Header;
if (header != null)
{
replacedAToken = true;
while (replacedAToken)
{
IEnumerable<Text> headerTexts = header.Descendants<Text>();
replacedAToken = this.IterateTextsAndTokenReplace(headerTexts, tokenNameValuePairs);
}
header.Save();
}
}
}
}
使用(WordprocessingDocument wordDoc=WordprocessingDocument.Open(memoryStream,true))
{
bool replacedAToken=true;
//继续循环文档,直到未替换令牌。这是因为一些令牌分布在“运行”中,可能需要第二次处理迭代才能捕获它们。
while(replacedAToken)
{
//获取所有文本元素
IEnumerable text=wordDoc.MainDocumentPart.Document.Body.subjects();
replacedAToken=this.IterateTextsAndTokenReplace(文本,tokenNameValuePairs);
}
wordDoc.MainDocumentPart.Document.Save();
foreach(wordDoc.MainDocumentPart.FooterPart中的FooterPart FooterPart)
{
if(footerPart!=null)
{
Footer-Footer=footerPart.Footer;
如果(页脚!=null)
{
replacedAToken=true;
while(replacedAToken)
{
IEnumerable footerText=footer.subjects();
replacedAToken=this.IterateTextsAndTokenReplace(footerText,tokenNameValuePairs);
}
footer.Save();
}
}
}
foreach(HeaderPart HeaderPart在wordDoc.main DocumentPart.HeaderParts中)
{
if(headerPart!=null)
{
页眉页眉=页眉部分页眉;
if(标题!=null)
{
replacedAToken=true;
while(replacedAToken)
{
IEnumerable headerTexts=header.subjections();
replacedAToken=this.IterateTextsAndTokenReplace(headerText,tokenNameValuePairs);
}
header.Save();
}
}
}
}
类似的问题:您可以尝试Eric White的Openxml powertools。在这里检查我的答案-
/// <summary>
/// Iterates through texts, concatenates them and looks for tokens to replace
/// </summary>
/// <param name="texts"></param>
/// <param name="tokenNameValuePairs"></param>
/// <returns>T/F whether a token was replaced. Should loop this call until it returns false.</returns>
private bool IterateTextsAndTokenReplace(IEnumerable<Text> texts, IDictionary<string, object> tokenNameValuePairs)
{
List<Text> tokenRuns = new List<Text>();
string runAggregate = String.Empty;
bool replacedAToken = false;
foreach (var run in texts)
{
if (run.Text.Contains(prefixTokenString) || runAggregate.Contains(prefixTokenString))
{
runAggregate += run.Text;
tokenRuns.Add(run);
if (run.Text.Contains(suffixTokenString))
{
if (possibleTokenRegex.IsMatch(runAggregate))
{
string possibleToken = possibleTokenRegex.Match(runAggregate).Value;
string innerToken = possibleToken.Replace(prefixTokenString, String.Empty).Replace(suffixTokenString, String.Empty);
if (tokenNameValuePairs.ContainsKey(innerToken))
{
//found token!!!
string replacementText = runAggregate.Replace(prefixTokenString + innerToken + suffixTokenString, Convert.ToString(tokenNameValuePairs[innerToken]));
Text newRun = new Text(replacementText);
run.InsertAfterSelf(newRun);
foreach (Text runToDelete in tokenRuns)
{
runToDelete.Remove();
}
replacedAToken = true;
}
}
runAggregate = String.Empty;
tokenRuns.Clear();
}
}
}
return replacedAToken;
}
string prefixTokenString = "{";
string suffixTokenString = "}";
Regex possibleTokenRegex = new Regex(prefixTokenString + "[a-zA-Z0-9-_]+" + suffixTokenString);
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(memoryStream, true))
{
bool replacedAToken = true;
//continue to loop document until token's have not bee replaced. This is because some tokens are spread across 'runs' and may need a second iteration of processing to catch them.
while (replacedAToken)
{
//get all the text elements
IEnumerable<Text> texts = wordDoc.MainDocumentPart.Document.Body.Descendants<Text>();
replacedAToken = this.IterateTextsAndTokenReplace(texts, tokenNameValuePairs);
}
wordDoc.MainDocumentPart.Document.Save();
foreach (FooterPart footerPart in wordDoc.MainDocumentPart.FooterParts)
{
if (footerPart != null)
{
Footer footer = footerPart.Footer;
if (footer != null)
{
replacedAToken = true;
while (replacedAToken)
{
IEnumerable<Text> footerTexts = footer.Descendants<Text>();
replacedAToken = this.IterateTextsAndTokenReplace(footerTexts, tokenNameValuePairs);
}
footer.Save();
}
}
}
foreach (HeaderPart headerPart in wordDoc.MainDocumentPart.HeaderParts)
{
if (headerPart != null)
{
Header header = headerPart.Header;
if (header != null)
{
replacedAToken = true;
while (replacedAToken)
{
IEnumerable<Text> headerTexts = header.Descendants<Text>();
replacedAToken = this.IterateTextsAndTokenReplace(headerTexts, tokenNameValuePairs);
}
header.Save();
}
}
}
}