Find Plain Text Markers in Word Document & Replace with Real Merge Fields using C# or Java | Duplicate Merge Fields

In My Case

Input file doc contains
$StartTag{ }EndTag$

Create Copy of document and builder.
Consider Start Tag as $StartTag{ }EndTag$
Trying to Replace Start Tag with MergeFields
Example instead of this $StartTag{ Replaceas merge fields <<$StartTag{>>

Expected Output
<<$StartTag{ >> << }EndTag$>>

Please Suggest With Sample Code.

@saranyasrinivasan92

Yes, you can achieve your requirement using Aspose.Words. We suggest you following solution.

  1. Please implement IReplacingCallback interface.
  2. In IReplacingCallback.Replacing, move the cursor to the matched node.
  3. Insert mail merge field with desired name.
  4. Remove the matched text.

Please read the following articles.
Find and Replace
Navigation with Cursor
Inserting Merge Field into a Document using DOM

@saranyasrinivasan92,

Sure, you can replace plain text markers in Word document with real merge fields by using following C# code of Aspose.Words for .NET.

Document doc = new Document("E:\\Temp\\Source (3)\\Input File as RegaxOutputFile.docx");

FindReplaceOptions opts = new FindReplaceOptions();
opts.ReplacingCallback = new ReplaceWithMergefield();
opts.Direction = FindReplaceDirection.Backward;
opts.MatchCase = false;

doc.Range.Replace("$abcstart{", "", opts);
doc.Range.Replace("}abcend$", "", opts);
doc.Range.Replace("$xyzstart{", "", opts);
doc.Range.Replace("}xyzend$", "", opts);

doc.Save("E:\\Temp\\Source (3)\\20.5.docx");

private class ReplaceWithMergefield : IReplacingCallback
{
    public ReplaceAction Replacing(ReplacingArgs e)
    {
        // This is a Run node that contains either the beginning or the complete match.
        Node currentNode = e.MatchNode;

        // The first (and may be the only) run can contain text before the match,
        // in this case it is necessary to split the run.
        if (e.MatchOffset > 0)
            currentNode = SplitRun((Run)currentNode, e.MatchOffset);

        // This array is used to store all nodes of the match for further removing.
        ArrayList runs = new ArrayList();

        // Find all runs that contain parts of the match string.
        int remainingLength = e.Match.Value.Length;
        while (
        (remainingLength > 0) &&
        (currentNode != null) &&
        (currentNode.GetText().Length <= remainingLength))
        {
            runs.Add(currentNode);
            remainingLength = remainingLength - currentNode.GetText().Length;

            // Select the next Run node.
            // Have to loop because there could be other nodes such as BookmarkStart etc.
            do
            {
                currentNode = currentNode.NextSibling;
            }
            while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
        }

        // Split the last run that contains the match if there is any text left.
        if ((currentNode != null) && (remainingLength > 0))
        {
            SplitRun((Run)currentNode, remainingLength);
            runs.Add(currentNode);
        }

        DocumentBuilder builder = new DocumentBuilder((Document)e.MatchNode.Document);
        builder.MoveTo((Run)runs[runs.Count - 1]);

        Field field = builder.InsertField("MERGEFIELD \"" + e.Match.Groups[0].Value.Trim() + "\"", null);
        field.Update();

        //Now remove all runs in the sequence.
        foreach (Run run in runs)
            run.Remove();

        return ReplaceAction.Skip;
    }

    private static Run SplitRun(Run run, int position)
    {
        Run afterRun = (Run)run.Clone(true);
        afterRun.Text = run.Text.Substring(position);
        run.Text = run.Text.Substring(0, position);
        run.ParentNode.InsertAfter(afterRun, run);
        return afterRun;
    }
}

Hope, this helps in achieving what you are looking for.

Thanks a lot, its working fine .
I have one more doubt ,In My case have possibilty to have same start tag in two place
example:
Input file doc contains 2 times $abcstart{ }abcend$
<<$abcstart{>> << }abcend$>>

<<$abcstart{>> << }abcend$>>

Currently able to create merge fields as suggested in previous post.
After creating merge fields trying to find to merge fields and insert table after start tag.

expected output

In both <<$abcstart{>> << }abcend$>> table should be inserted after start tag but currently only in one merge fields table is getting inserted.

Sample code to insert table in

builder.MoveToMergeField(StartTag, true, false);
DataTable dtable = GetTable(false);
builder.InsertParagraph();
Table table = ImportTableFromDataTable(builder, dtable, true);

My Query :
1.is that possible to Create Merge Field start tags with same name?
2.is that possible to Insert table in all findings of Merge Field start tags. consider in input document having xyz duplicate two merge fields

<< xyz>>

<< xyz>>

how to find and insert table.

@saranyasrinivasan92,

Yes, it is possible to create more than one merge fields in Word documents with same names. Secondly, please check the following code if you would like to move cursor to all merge fields (with same name) and insert document elements there:

DocumentBuilder builder = new DocumentBuilder(doc);

string targetMergeFieldName = "mf";
foreach (string mergeField in doc.MailMerge.GetFieldNames())
{
    if (mergeField.Equals(targetMergeFieldName)) {
        builder.MoveToMergeField(targetMergeFieldName);
        builder.Writeln("multiple times");
        // or insert table here
    }
}

Hope, this helps.

Thank you , its working fine.

one more doubt Im trying to duplicate merge fields based on condition like
example
check for start n end tag found and duplicate tag count =3
then i should replace start and end tag with three duplicate merege fields in sequences like

Input document

$Starttag{ }Endtag$

Output Document

<<$Starttag{>> <<}Endtag$>>
<<$Starttag{>> <<}Endtag$>>
<<$Starttag{>> <<}Endtag$>>

Is that possible ? and currently for inserting starttag as merge fileds sample code.

tag= tags[0];
FindReplaceOptions options = new FindReplaceOptions();
options.ReplacingCallback = new ReplaceWithMergefield();
options.Direction = FindReplaceDirection.Backward;
options.MatchCase = false;
doc.Range.Replace(Tag, “”, options);

If possible, Please share sample code.

@saranyasrinivasan92,

Yes, it is possible. But, to ensure a timely and accurate response, please ZIP and attach the following resources here for testing:

  • Your simplified input Word document
  • Your expected DOCX file showing the desired output. You can create this document by using MS Word.

As soon as you get these pieces of information ready, we will start further investigation into your scenario and provide you code to achieve the same by using Aspose.Words. Thanks for your cooperation.

Source.zip (57.7 KB)
Please check attachment for input and output document.

@saranyasrinivasan92,

One way to achieve this is to clone the Paragraph containing the string multiple times and then use the same code from my previous post to replace them with real merge fields:

string str = “$abcstart{}abcend$”;

Document doc = new Document("E:\\Temp\\Source (5)\\InputFile.docx");

Paragraph targetPara = null;
foreach (Paragraph para in doc.GetChildNodes(NodeType.Paragraph, true))
{
    if (para.ToString(SaveFormat.Text).Contains(str))
    {
        targetPara = para;
        break;
    }
}

if (targetPara != null)
{
    targetPara.ParentNode.InsertAfter(targetPara.Clone(true), targetPara);
    targetPara.ParentNode.InsertAfter(targetPara.Clone(true), targetPara);

    FindReplaceOptions opts = new FindReplaceOptions();
    opts.ReplacingCallback = new ReplaceWithMergefield();
    opts.Direction = FindReplaceDirection.Backward;
    opts.MatchCase = false;

    doc.Range.Replace("$abcstart{", "", opts);
    doc.Range.Replace("}abcend$", "", opts);
}

doc.Save("E:\\Temp\\Source (5)\\20.5.docx");

Hope, this helps.