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
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.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With