Reputation: 291
I am trying to replace mergefields in word file with my data using OpenXML.
I think there is no problem in the code as sometimes it works fine in some templates and sometimes the mergefields isn't replaced.
Is there a problem in inserting Mergefields inside word template ? I don't know what is the problem.
Below is the code I use :
public void Fill_Document_Fields(Dictionary<string, string> Fields, string FilePath)
{
try
{
using (WordprocessingDocument Doc = WordprocessingDocument.Open(FilePath, true))
{
foreach (FieldCode field in Doc.MainDocumentPart.RootElement.Descendants<FieldCode>())
{
string FieldName =string.Empty;
try
{
FieldName = field.Text.Trim().Split(' ')[2];
}
catch
{
FieldName = field.Text.Trim();
}
foreach (Run run in Doc.MainDocumentPart.Document.Descendants<Run>())
{
foreach (Text txtFromRun in run.Descendants<Text>().Where(a => a.Text == "«" + FieldName + "»"))
{
string itemValue;
if (Fields.TryGetValue(txtFromRun.Text, out itemValue))
{
txtFromRun.Text = itemValue;
}
}
}
}
}
}
catch
{
}
}
Upvotes: 2
Views: 7093
Reputation: 107
You should save the word document after you edit it so before you get out of the using of the WordprocessingDocument.Open just close the document (see example below).
You can find a nice implementation of the GetMergeFields()
here: https://github.com/frankfajardo/OpenXmlWordHelper
This is how I do it:
// get a memory stream for the bytes
using MemoryStream templateFileStream = this.GetMemoryStream(templateFileBytes);
// open template file from Memory Stream
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(templateFileStream, true))
{
var mergeFields = wordDoc.GetMergeFields().ToList();
foreach (var mergeField in mergeFields)
{
mergeField.ReplaceWithText(*replacementText*);
}
// This is required to save and flush the merged word document to the memory stream
wordDoc.Close();
// get the new merged document stream before it is closed and disposed
mergedDocumentBytes = templateFileStream.ToArray();
}
Upvotes: 5
Reputation: 916
your code works only with 'SimpleField', in fact you code is not correct at all, because replacing «FieldName» with your custom value, the value you are replacing will also be a mergefield.
If you look the xml behind the document, you can see that is similar to this:
<w:fldSimple w:instr=" MERGEFIELD TestFoo \* MERGEFORMAT ">
<w:r w:rsidR="00C44AC1">
<w:rPr>
<w:noProof />
</w:rPr>
<w:t>«TestFoo»</w:t>
</w:r>
</w:fldSimple>
At this moment, you replace element 'w:t' («TestFoo») with your value (Foo) and the result is something like:
<w:fldSimple w:instr=" MERGEFIELD TestFoo \* MERGEFORMAT ">
<w:r w:rsidR="00C44AC1">
<w:rPr>
<w:noProof />
</w:rPr>
<w:t>Foo</w:t>
</w:r>
</w:fldSimple>
As you can see, your value is still inside the mergefield element.
In this case (SimpleField) you need to find 'fldSimple' element and read instr attribute maybe with a regex to extract mergefield value. When you want to replace mergefield with your value, you must replace fldSimple element with text element inside run element.
The second one implementation of mergefield is the FieldChar.
<w:r>
<w:fldChar w:fldCharType="begin" />
</w:r>
<w:r>
<w:instrText xml:space="preserve"> MERGEFIELD TestFoo \* MERGEFORMAT </w:instrText>
</w:r>
<w:r>
<w:fldChar w:fldCharType="separate" />
</w:r>
<w:r w:rsidR="00FA6E12">
<w:rPr>
<w:noProof />
</w:rPr>
<w:t>«TestFoo»</w:t>
</w:r>
<w:r>
<w:rPr>
<w:noProof />
</w:rPr>
<w:fldChar w:fldCharType="end" />
</w:r>
In this case you need to find fldChar element with fldCharType attr equal to begin, you have to iterate all nodes until you find fldCharType attr value equal to end.
Between 'begin' and 'end' values you need to find and read value inside instrText element.
When you want replace your value you must replace all elements found between 'begin' and 'end' values with text element inside run element.
I think that with docx mergefield are written as FieldChar and with dotx are written as SimpleField.
Now I can not write an example of code, I hope that I have helped you.
Sorry for my english, Bye
Upvotes: 6