C# 中的 Openxml 仅更新段落中的第一个 MERGEFIELD

Openxml in C# updating only the first MERGEFIELD in a paragraph

我在文档中有大约 10 个 MERGEFIELD 试图用一些值替换文本。这是代码。

using (WordprocessingDocument document = WordprocessingDocument.Open(destinationFileName, true))
            {
                document.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
                MainDocumentPart docPart = document.MainDocumentPart;
                docPart.AddExternalRelationship("http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate", new Uri(destinationFileName, UriKind.RelativeOrAbsolute));
                docPart.Document.Save();

                IEnumerable<FieldChar> fldChars = document.MainDocumentPart.RootElement.Descendants<FieldChar>();
                if (fldChars == null) { return; }

                string fieldList = string.Empty;
                FieldChar fldCharStart = null;
                FieldChar fldCharEnd = null;
                FieldChar fldCharSep = null;
                FieldCode fldCode = null;
                string fldContent = String.Empty;
                int i = 1;
                foreach(var fldChar in fldChars)
                {

                    System.Diagnostics.Debug.WriteLine(i + ": " + fldChar);
                    i++;

                    string fldCharPart = fldChar.FieldCharType.ToString();
                    System.Diagnostics.Debug.WriteLine("Field Char Length: " + fldChar.Count());
                    System.Diagnostics.Debug.WriteLine("Field Char part: " + fldCharPart);

                    switch(fldCharPart)
                    {    
                        case "begin": // start of the field
                            fldCharStart = fldChar;
                            System.Diagnostics.Debug.WriteLine("Field Char Start: " + fldCharStart);
                            // get the field code, which will be an instrText element
                            // either as sibling or as a child of the parent sibling

                            fldCode = fldCharStart.Parent.Descendants<FieldCode>().FirstOrDefault();
                            System.Diagnostics.Debug.WriteLine("Field Code: " + fldCode);

                            if (fldCode == null)
                            {
                                fldCode = fldCharStart.Parent.NextSibling<Run>().Descendants<FieldCode>().FirstOrDefault();
                                System.Diagnostics.Debug.WriteLine("New Field Code: " + fldCode);
                            }

                            if (fldCode != null && fldCode.InnerText.Contains("MERGEFIELD"))
                            {
                                fldContent = getFieldValue(query, prescriber, fldCode.InnerText);
                                fieldList += fldContent + "\n";
                                System.Diagnostics.Debug.WriteLine("Field content: " + fldContent);

                            }
                            break;

                        case "end": // end of the field
                            fldCharEnd = fldChar;
                            System.Diagnostics.Debug.WriteLine("Field char end: " + fldCharEnd);
                            break;

                        case "separate": // complex field with text result
                            fldCharSep = fldChar;
                            break;

                        default:
                            break;  

                    }

                    if((fldCharStart != null) && (fldCharEnd != null))
                    {
                        if(fldCharSep != null)
                        {
                            Text elemText = (Text)fldCharSep.Parent.NextSibling().Descendants<Text>().FirstOrDefault();
                            elemText.Text = fldContent;
                            System.Diagnostics.Debug.WriteLine("Element text: " + elemText);

                            // Delete all field chas with their runs
                            DeleteFieldChar(fldCharStart);
                            DeleteFieldChar(fldCharEnd);
                            DeleteFieldChar(fldCharSep);
                            fldCode.Remove();
                        }
                        else
                        {
                            Text elemText = new Text(fldContent);
                            fldCode.Parent.Append(elemText);
                            fldCode.Remove();
                            System.Diagnostics.Debug.WriteLine("Element Text !sep: " + elemText);

                            DeleteFieldChar(fldCharStart);
                            DeleteFieldChar(fldCharEnd);
                            DeleteFieldChar(fldCharSep);
                        }

                        fldCharStart = null;
                        fldCharEnd = null;
                        fldCharSep = null;
                        fldCode = null;
                        fldContent = string.Empty;
                    }
                }
                System.Diagnostics.Debug.WriteLine("Field list: " + fieldList);
            }

在某种程度上是有效的。问题是当段落中有多个字段时。我在本文档的一个段落中有大约 4 个合并字段,之后的每个段落中有一个字段。只有段落中的第一个合并字段正在更新,段落中的其余字段未受影响。然后,它移动到下一段并查找该字段。我怎样才能解决这个问题?

看起来您将简单的 Mailmerge 替换复杂化了。与其循环遍历段落,不如获取文档中的所有邮件合并字段并替换它们。

    private const string FieldDelimeter = " MERGEFIELD ";

    foreach (FieldCode field in doc.MainDocumentPart.RootElement.Descendants<FieldCode>())
    {
         var fieldNameStart = field.Text.LastIndexOf(FieldDelimeter, System.StringComparison.Ordinal);
         var fieldName = field.Text.Substring(fieldNameStart + FieldDelimeter.Length).Trim();

         foreach (Run run in doc.MainDocumentPart.Document.Descendants<Run>())
         {
               foreach (Text txtFromRun in run.Descendants<Text>().Where(a => a.Text == "«" + fieldName + "»"))
               {                            
                     txtFromRun.Text = "Replace what the merge field here";
               }
         }
     } 


    doc.MainDocumentPart.Document.Save();

doc 是 WordprocessingDocument 类型。

这将替换所有合并字段,无论段落中的字段数量如何。