使用 OpenXMl 填充 MergeField

Mou*_*nir 2 .net c# asp.net openxml openxml-sdk

我正在尝试使用 OpenXML 将 Word 文件中的合并字段替换为我的数据。

\n\n

我认为代码中没有问题,因为有时它在某些模板中工作正常,有时合并字段没有被替换。

\n\n

在word模板中插入合并域有问题吗?我不知道出了什么问题。

\n\n

下面是我使用的代码:

\n\n
 public void Fill_Document_Fields(Dictionary<string, string> Fields, string FilePath)\n    {\n        try\n        {\n            using (WordprocessingDocument Doc = WordprocessingDocument.Open(FilePath, true))\n            {\n                foreach (FieldCode field in Doc.MainDocumentPart.RootElement.Descendants<FieldCode>())\n                {\n                    string FieldName =string.Empty;\n                    try\n                    {\n                        FieldName = field.Text.Trim().Split(\' \')[2];\n                    }\n                    catch\n                    {\n                        FieldName = field.Text.Trim();\n                    }\n\n\n                    foreach (Run run in Doc.MainDocumentPart.Document.Descendants<Run>())\n                    {\n                        foreach (Text txtFromRun in run.Descendants<Text>().Where(a => a.Text == "\xc2\xab" + FieldName + "\xc2\xbb"))\n                        {\n                            string itemValue;\n                            if (Fields.TryGetValue(txtFromRun.Text, out itemValue))\n                            {\n                                txtFromRun.Text = itemValue;\n                            }\n                        }\n                    }\n\n                }\n            }\n        }\n        catch \n        {\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

Ste*_*ono 6

您的代码仅适用于 \'SimpleField\',事实上您的代码根本不正确,因为用您的自定义值替换 \xc2\xabFieldName\xc2\xbb ,您要替换的值也将是合并字段。

\n\n

如果你查看文档后面的 xml,你会发现类似于以下内容:

\n\n
<w:fldSimple w:instr=" MERGEFIELD  TestFoo  \\* MERGEFORMAT ">\n  <w:r w:rsidR="00C44AC1">\n      <w:rPr>\n           <w:noProof />\n      </w:rPr>\n      <w:t>\xc2\xabTestFoo\xc2\xbb</w:t>\n   </w:r>\n</w:fldSimple>\n
Run Code Online (Sandbox Code Playgroud)\n\n

此时,您将元素 \'w:t\' (\xc2\xabTestFoo\xc2\xbb) 替换为您的值 (Foo),结果类似于:

\n\n
<w:fldSimple w:instr=" MERGEFIELD  TestFoo  \\* MERGEFORMAT ">\n  <w:r w:rsidR="00C44AC1">\n      <w:rPr>\n           <w:noProof />\n      </w:rPr>\n      <w:t>Foo</w:t>\n   </w:r>\n</w:fldSimple>\n
Run Code Online (Sandbox Code Playgroud)\n\n

如您所见,您的值仍在 mergefield 元素内。

\n\n

在这种情况下 (SimpleField),您需要找到 \'fldSimple\' 元素并读取 instr 属性,可能使用正则表达式来提取合并字段值。\n当您想用您的值替换合并字段时,您必须将 fldSimple 元素替换为 run 内的文本元素元素。

\n\n

mergefield 的第二个实现是 FieldChar。

\n\n
<w:r>\n  <w:fldChar w:fldCharType="begin" />\n</w:r>\n<w:r>\n  <w:instrText xml:space="preserve"> MERGEFIELD  TestFoo  \\* MERGEFORMAT </w:instrText>\n</w:r>\n<w:r>\n  <w:fldChar w:fldCharType="separate" />\n</w:r>\n<w:r w:rsidR="00FA6E12">\n  <w:rPr>\n    <w:noProof />\n  </w:rPr>\n  <w:t>\xc2\xabTestFoo\xc2\xbb</w:t>\n</w:r>\n<w:r>\n  <w:rPr>\n    <w:noProof />\n  </w:rPr>\n  <w:fldChar w:fldCharType="end" />\n</w:r>\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这种情况下,您需要找到 fldCharType attr 等于 begin 的 fldChar 元素,您必须迭代所有节点,直到找到 fldCharType attr 值等于 end 为止。

\n\n

在“begin”和“end”值之间,您需要查找并读取 instrText 元素内的值。

\n\n

当您想要替换值时,必须将“begin”和“end”值之间找到的所有元素替换为 run 元素内的文本元素。

\n\n

我认为使用 docx mergefield 会写成 FieldChar,使用 dotx 会写成 SimpleField。

\n\n

现在我无法编写代码示例,希望对您有所帮助。

\n\n

抱歉我的英语不好,\n再见

\n


小智 5

您应该在编辑后保存 Word 文档,以便在停止使用 WordprocessingDocument.Open 之前 关闭该文档(请参见下面的示例)。

您可以在这里找到一个很好的实现GetMergeFields(): https: //github.com/frankfajardo/OpenXmlWordHelper

我就是这样做的:

// get a memory stream for the bytes
using MemoryStream templateFileStream = this.GetMemoryStream(templateFileBytes);

// open template file from Memory Stream
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(templateFileStream, true))
{
    var mergeFields = wordDoc.GetMergeFields().ToList();

    foreach (var mergeField in mergeFields)
    {
        mergeField.ReplaceWithText(*replacementText*);
    }

    // This is required to save and flush the merged word document to the memory stream
    wordDoc.Close();

    // get the new merged document stream before it is closed and disposed
    mergedDocumentBytes = templateFileStream.ToArray();
}                
Run Code Online (Sandbox Code Playgroud)