Office Open XML SDK替换

时间:2021-08-01 16:52:00

For creating word documents based on data from an SQL database, I'm using Office Open XML SDK to avoid using interop. This speeds up the process and it eliminates the requirement for a Microsoft office suite installed on the client system.

为了基于来自SQL数据库的数据创建word文档,我使用Office Open XML SDK来避免使用互操作。这加快了流程,并且消除了对客户端系统上安装的Microsoft Office套件的要求。

While this works very well, there is a problem I'm having when replacing certain text in the document. To keep customization of the final document an option, I've created a document with some tags in it as a template. This template contains tags such as [TagHere]. Since the tag names should be easy readable, they could be used throughout the document, which is why I've surrounded the tag with braces [].

虽然这很有效,但在替换文档中的某些文本时我遇到了一个问题。为了保持最终文档的自定义选项,我创建了一个文档,其中包含一些标记作为模板。此模板包含[TagHere]等标签。由于标签名称应该易于阅读,因此可以在整个文档中使用它们,这就是我用大括号[]包围标签的原因。

This works quite well, but sometimes, an issue comes up. When you're typing in a docx document, the text can be split up into multiple tags, even in the same word. A tag like [TagHere] can be split up into

这很有效,但有时会出现问题。当您输入docx文档时,即使在同一个单词中,也可以将文本拆分为多个标记。像[TagHere]这样的标签可以拆分成

<tag>[</tag><tag>TagHere</tag><tag>]</tag>

When this happens, the replacement won't work.

发生这种情况时,更换将无法正常工作。

Now the docx format has some alternative options to do this kind of operations, such as Content Controls, but these make the process of creating the template more complex. Furtermore, it is not uncommon in these documents to get one row of a table with tags and copy it multiple of times, which would probably break the content tag principle. Hence I've chosen to not use this option.

现在,docx格式有一些替代选项来执行此类操作,例如内容控件,但这些使得创建模板的过程更加复杂。更进一步,在这些文档中获取带有标记的一行表并多次复制它并不罕见,这可能会破坏内容标记原则。因此我选择不使用此选项。

It would be great if someone has a solution to this problem.

如果有人能解决这个问题,那将会很棒。

1 个解决方案

#1


4  

instead of typing plain text "taghere", insert a merge field. (in word, click insert > quick parts > field. choose "mergefield" and type "TagHere" in the "Field name" field.)

而不是键入纯文本“taghere”,插入合并字段。 (单击“插入”>“快速部件”字段。选择“合并域”并在“字段名称”字段中键入“TagHere”。)

then instead of doing a text find-replace, scan the document for merge fields and set the inner texts.

然后,不是进行文本查找 - 替换,而是扫描文档以查找合并字段并设置内部文本。

class Program
{
    static void Main(string[] args)
    {
        string document = args[0];
        using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, true))
        {
            Dictionary<string, string> replaceOperations = new Dictionary<string, string>();

            replaceOperations.Add("company", "alex's applications");
            replaceOperations.Add("first_name", "alexander");
            replaceOperations.Add("last_name", "taylor");
            //etc

            Replace(wordDoc, replaceOperations);
        }
    }

    public static char[] splitChar = new char[] {' '};
    public static void Replace(WordprocessingDocument document, Dictionary<string, string> replaceOperations)
    {
        //find all the fields
        foreach (var field in document.MainDocumentPart.Document.Body.Descendants<SimpleField>())
        {
            //parse the instruction
            string[] instruction = field.Instruction.Value.Split(splitChar, StringSplitOptions.RemoveEmptyEntries);

            //check if it's a merge field, and if so...
            if (instruction[0].ToLower().Equals("mergefield"))
            {
                //get the field name
                string fieldname = instruction[1];

                //find the text inside (there will only be one)
                foreach (var fieldtext in field.Descendants<Text>())
                {
                    //see if we know what to set this value to
                    string value = replaceOperations.ContainsKey(fieldname) ? replaceOperations[fieldname] : null;

                    //if we found the replace value, set the text to this value
                    if (value != null)
                        fieldtext.Text = value;

                    //should only be one text inside
                    break;
                }
            }
        }
    }
}

#1


4  

instead of typing plain text "taghere", insert a merge field. (in word, click insert > quick parts > field. choose "mergefield" and type "TagHere" in the "Field name" field.)

而不是键入纯文本“taghere”,插入合并字段。 (单击“插入”>“快速部件”字段。选择“合并域”并在“字段名称”字段中键入“TagHere”。)

then instead of doing a text find-replace, scan the document for merge fields and set the inner texts.

然后,不是进行文本查找 - 替换,而是扫描文档以查找合并字段并设置内部文本。

class Program
{
    static void Main(string[] args)
    {
        string document = args[0];
        using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, true))
        {
            Dictionary<string, string> replaceOperations = new Dictionary<string, string>();

            replaceOperations.Add("company", "alex's applications");
            replaceOperations.Add("first_name", "alexander");
            replaceOperations.Add("last_name", "taylor");
            //etc

            Replace(wordDoc, replaceOperations);
        }
    }

    public static char[] splitChar = new char[] {' '};
    public static void Replace(WordprocessingDocument document, Dictionary<string, string> replaceOperations)
    {
        //find all the fields
        foreach (var field in document.MainDocumentPart.Document.Body.Descendants<SimpleField>())
        {
            //parse the instruction
            string[] instruction = field.Instruction.Value.Split(splitChar, StringSplitOptions.RemoveEmptyEntries);

            //check if it's a merge field, and if so...
            if (instruction[0].ToLower().Equals("mergefield"))
            {
                //get the field name
                string fieldname = instruction[1];

                //find the text inside (there will only be one)
                foreach (var fieldtext in field.Descendants<Text>())
                {
                    //see if we know what to set this value to
                    string value = replaceOperations.ContainsKey(fieldname) ? replaceOperations[fieldname] : null;

                    //if we found the replace value, set the text to this value
                    if (value != null)
                        fieldtext.Text = value;

                    //should only be one text inside
                    break;
                }
            }
        }
    }
}