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

烂漫一生 提交于 2019-12-10 11:52:14

问题


I have approximately 10 MERGEFIELD in a document that I'm trying to replace the Text with some value. Here's the code.

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);
            }

It works to some extent. The problem is when there are more than one field in a paragraph. I have about 4 merge fields in one paragraph in this document, and one field in each paragraph after that. Only the first merge field in the paragraph is being updated and the rest fields in the paragraphs is untouched. Then, it moves to the next paragraph and looks for the field. How can I fix this?


回答1:


Looks like you are over complicating a simple Mailmerge replacement. Instead of looping through paragraphs you could rather get all mailmerge fields within a document and replace them.

    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 is of type WordprocessingDocument.

This will replace all merge fields regardless of the amount of fields in a paragraph.



来源:https://stackoverflow.com/questions/38577690/openxml-in-c-sharp-updating-only-the-first-mergefield-in-a-paragraph

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!