Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to find Merge Fields in word using open xml

am having trouble in finding the merge fields using the below syntax

foreach (var field in docx.MainDocumentPart.Document.Descendants<SimpleField>())
{
}

sometimes the above code works and finds out the mergefields and sometimes it just shows null

like image 546
Harish Avatar asked Nov 20 '25 05:11

Harish


2 Answers

The issue is that word can implement the merge field as SimpleField or FieldChar. The SimpleField element contains all the details of the merge fields, as for the FieldChar it has a begin element followed by the field code, paragraph with a run and text to store the field display value and finally the merge field end element. All the elements between the begin and finish belong to the merge field.

Merge field implemented as FieldChar

<w:r>
  <w:fldChar w:fldCharType="begin" />
</w:r>
<w:r>
  <w:instrText xml:space="preserve"> MERGEFIELD  AnotherBodyField  \* MERGEFORMAT </w:instrText>
</w:r>
<w:r>
  <w:fldChar w:fldCharType="separate" />
</w:r>
<w:r w:rsidR="003A6EEC">
  <w:rPr>
    <w:noProof />
  </w:rPr>
  <w:t>«AnotherBodyField»</w:t>
</w:r>
<w:r>
  <w:rPr>
    <w:noProof />
  </w:rPr>
  <w:fldChar w:fldCharType="end" />
</w:r>

Merge field implemented as SimpleField

<w:fldSimple w:instr=" MERGEFIELD  TestMergField  \* MERGEFORMAT ">
    <w:r w:rsidR="00C44AC1">
      <w:rPr>
        <w:noProof />
      </w:rPr>
      <w:t>«TestMergField»</w:t>
    </w:r>
</w:fldSimple>

The SimpleField object contains all the merge field content, so this code would do. var simpleMergeField = documentProcessor.MainDocumentPart.Document.Body.Descendants();

For the FieldChar, you need need to watch for the FieldCharValues.Begin, FieldCharValues.Separate and FieldCharValues.End. every element in between is included in the merge field content.

like image 50
Mahmoud Abdou Avatar answered Nov 21 '25 18:11

Mahmoud Abdou


Years later, summarising the 2 answers above, the code solution to create a lookup of all the mergefields by name:

var splitter = new[] { ' ', '"' };
const string mergefield = "MERGEFIELD";
var mergeFields = mainPart.HeaderParts.Cast<OpenXmlPart>()
    .Concat(mainPart.FooterParts.Cast<OpenXmlPart>())
    .Concat(new OpenXmlPart[] { mainPart })
    .SelectMany(x => x.RootElement.Descendants<SimpleField>().Select(sf => new { text = sf.Instruction.Value, el = (OpenXmlElement)sf })
                    .Concat(x.RootElement.Descendants<FieldCode>().Select(fc=> new { text = fc.Text, el = (OpenXmlElement)fc})))
    .Select(a => new { words = a.text.Split(splitter, StringSplitOptions.RemoveEmptyEntries), el = a.el })
    .Where(a => mergefield.Equals(a.words.FirstOrDefault(), StringComparison.OrdinalIgnoreCase))
    .ToLookup(k => string.Join(" ",k.words.Skip(1).TakeWhile(i=>i!= "\\*")), v => v.el);
like image 34
Brent Avatar answered Nov 21 '25 17:11

Brent