我有一个要求,我希望用户在Word文档中键入一些字符串标记,以便可以通过带有一些值的C#应用程序替换它们。所以说我有一个文件根据图像
现在使用SDK我可以按如下方式阅读文档:
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
(显然不是我可以控制的东西)而不是我希望的那样:
<w:t><: Person.Meta.Age
OR
<w:t><: Person.Meta.Age
所以我的问题是我如何实际处理字符串本身
<: Person.Meta.Age :>
仍然保留格式化等,以便当我用我拥有的值替换标记时:
注意:第二个标记值的值的粗体
我是否需要迭代文档元素或使用其他方法?所有指针都非常赞赏。
答案 0 :(得分:0)
这对OpenXML来说是一个棘手的问题。我在这里解释了最好的解决方案: http://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2011/06/13/open-xml-presentation-generation-using-a-template-presentation.aspx
基本上,Eric会扩展内容,使每个角色独立运行,然后查找启动'&lt ;:'序列然后结束序列的运行。然后他进行替换并重新组合具有相同属性的所有运行。
示例适用于PowerPoint,其内容通常要少得多,因此性能可能是Word中的一个因素;我希望有一些方法可以缩小段落的范围或者你必须要炸毁的东西。
例如,您可以提取段落的文本以查看它是否包含任何占位符,并且只对这些段落执行展开/替换/压缩操作。
答案 1 :(得分:0)
使用OpenXML,您可以使用一些基于OpenXML的第三方模板,而不是直接查找/替换令牌,这些模板很容易使用,并且可以很快收回成本。
正如Scanny所指出的,OpenXML充满了令人讨厌的细节,人们必须逐一掌握这些细节。学习曲线漫长而陡峭。如果你想成为OpenXML大师,那就去吧,然后开始攀登。如果你想有时间享受一些体面的社交生活,还有其他选择:选择一个基于OpenXML的第三方工具包。我评估了Docentric Toolkit。它提供了基于模板的方法,您可以在其中准备模板,该模板是Word格式的文件,其中包含在运行时从应用程序合并的数据的占位符。它们都支持MS Word支持的任何格式,您可以使用条件内容,表格等。
您还可以使用DOM方法创建或更改文档。最终文件可以是.docx或.pdf。
Docentric是许可产品,但您很快就可以使用这些工具之一来节省成本。
如果您要在服务器上运行应用程序,请不要使用互操作 - 请参阅此链接以获取更多详细信息:(http://support2.microsoft.com/kb/257757)。
答案 2 :(得分:0)
这是一些代码,我很快就把它们拼凑在一起,以便考虑在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;
}
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();
}
}
}
}