使用OpenXML SDK简单地替换Word文档中的标记

时间:2015-03-03 05:41:40

标签: c# openxml

我有一个要求,我希望用户在Word文档中键入一些字符串标记,以便可以通过带有一些值的C#应用​​程序替换它们。所以说我有一个文件根据图像

enter image description here

现在使用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>&lt;:</w:t></w:r><w:r w:rsidR="002E53FF" w:rsidRPr="000A794A"><w:t>Person.Meta.Age

(显然不是我可以控制的东西)而不是我希望的那样:

<w:t>&lt;: Person.Meta.Age

OR

<w:t><: Person.Meta.Age

所以我的问题是我如何实际处理字符串本身

<: Person.Meta.Age :>

仍然保留格式化等,以便当我用我拥有的值替换标记时:

enter image description here

注意:第二个标记值的值的粗体

我是否需要迭代文档元素或使用其他方法?所有指针都非常赞赏。

3 个答案:

答案 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();
                                    }
                                }
                            }
                        }