Reputation: 160
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
Upvotes: 3
Views: 4278
Reputation: 4876
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);
Upvotes: 2
Reputation: 106
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.
Upvotes: 3
Reputation: 187
Please verify once whether your merge field present which part of document.Itmay present in header or footer parts.You can find out this by making xml package.check each header,footer xmls.
you can loop through header parts and footerparts using below statements
foreach (var header in docx.MainDocumentPart.Headerparts)
{
foreach (var field in header.RootElement.Descendants<SimpleField>())
{
}
}
foreach (var footerin docx.MainDocumentPart.FooterParts)
{
foreach (var field in footer.RootElement.Descendants<SimpleField>())
{
}
}
Upvotes: 0