MergeFields with Brackets

Hello,

I am using Mergefield and ReplaceEvaluator functions with Aspose.Words to replace text in Words File. It works fine for almost every MergeFields but not for the ones which contains brackets.
For example this kind of mergefield doesn’t work: “~TESTE(E)~” but this one works “~TESTE/E~”
Do you have any idea ?
If necessary I can send you sample file ormy code.

Thanks for your help.
Laure

Hello

Thanks for your inquiry. Could you please provide me your template document and simple code, those will allow me to reproduce the problem on my side. I will check the issue and provide you more information.
Best regards,

Hello,

Thanks for your answer.
I have attached a sample file with severals of my mergefields (there all between “~~”) including the one which doesn’t works : “~TESTE(E)”.
Sometimes I also have a strange behavior when I replace my mergefields : there all replace by this text : "MERGEFIELD " & myfield & “* MERGEFORMAT” instead of their value (?!). In this case I killed WinWorde.exe process and retry and then it works. Any Idea ?

Here is a sample code :

Public Sub Test()
Dim _oDocWord As New Aspose.Words.Document("TEST.DOC")
Dim _oBuilderDocWord As New Aspose.Words.DocumentBuilder(_oDocWord)
_oDocWord.Range.Replace(New System.Text.RegularExpressions.Regex("~TESTE(E)~"), New Aspose.Words.ReplaceEvaluator(AddressOf ReplaceFieldHandler), False)
_oDocWord.Save("TEST_OUTPUT.DOC")
End Sub

Private Function ReplaceFieldHandler(ByVal sender As Object, ByVal e As Aspose.Words.ReplaceEvaluatorArgs) As Aspose.Words.ReplaceAction

'Get MatchNode
Dim run1 As Aspose.Words.Run = DirectCast(e.MatchNode, Aspose.Words.Run)
'Create Run
Dim run2 As Aspose.Words.Run = DirectCast(run1.Clone(True), Aspose.Words.Run)
'Get index of match value
Dim index As Integer = run1.Text.IndexOf(e.Match.Value)
'split run that contains matched text
run2.Text = run1.Text.Substring(index + e.Match.Value.Length)
run1.Text = run1.Text.Substring(0, index)
run1.ParentParagraph.InsertAfter(run2, run1)
'Move to run2 in document builder
_oBuilderDocWord.MoveTo(run2)

'_oBuilderDocWord.InsertField("MERGEFIELD myField * MERGEFORMAT", sFieldValue)
_oBuilderDocWord.InsertField("MERGEFIELD " & "~TESTE(E)~" & "* MERGEFORMAT", "testée")

Return Aspose.Words.ReplaceAction.Skip 
End Function

Hi

Thank you for additional information. The problem occurs because brackets are special characters in regular expressions, so you need to escape them. For example please try using the following code:

_oDocWord.Range.Replace(New System.Text.RegularExpressions.Regex(System.Text.RegularExpressions.Regex.Escape("~TESTE(E)~")), New ReplaceFieldHandler(), False)

Hope this helps.
Best regards.

Yes it works great !

Thanks a lot Alexey !

hello,

I am still using Mergefield and ReplaceEvaluator functions with Aspose.Words
to replace text in Words File. It still works fine for almost every
MergeFields, even for the ones which contains brackets ! But sometimes, a strange error occurs on a regular merge field and the following message pop out : “An exception of type ‘System.ArgumentOutOfRangeException’ occurred and was caught.”
The error is throw in the “IReplacingCallback_Replacing” procedure
I join a sample file “example.doc” in which the 2 following mergefields don’t work and I don’t understand why : <span style=“font-size: 8pt; font-family: “Courier New”; color: red;”>~CELSITESMVT - LISTE DEROULANTE COTE~ and <span style=“font-size: 8pt; font-family: “Courier New”; color: red;”>~CELSITESMVT - LISTE DEROULANTE VERIF
AMP~

Here is a sample code that I am using :

Private Sub ReplaceField()
Try

_oDocWord.Range.Replace(New System.Text.RegularExpressions.Regex(System.Text.RegularExpressions.Regex.Escape(_oItemFusion.sFieldCode)), New cls_ReplaceFieldEvaluator(_oItemFusion, _oBuilderDocWord), False)

Catch ex As Exception
CoRa.Outils.ExceptionManager.AddInfoToEx(ex, "Méthode", "cls_fusion.ReplaceField")
If Outils.ExceptionManager.ExceptionAlert(ex) Then Throw
End Try
End Sub

Private Function IReplacingCallback_Replacing(ByVal e As Aspose.Words.ReplacingArgs) As Aspose.Words.ReplaceAction Implements Aspose.Words.IReplacingCallback.Replacing
Try
If TypeOf e.MatchNode Is Aspose.Words.Run Then

'Get MatchNode
Dim run1 As Aspose.Words.Run = DirectCast(e.MatchNode, Aspose.Words.Run)
'Create Run
Dim run2 As Aspose.Words.Run = DirectCast(run1.Clone(True), Aspose.Words.Run)
'Get index of match value
Dim index As Integer = run1.Text.IndexOf(e.Match.Value)
'split run that contains matched text
run2.Text = run1.Text.Substring(index + e.Match.Value.Length)
run1.Text = run1.Text.Substring(0, index)
run1.ParentParagraph.InsertAfter(run2, run1)
'Move to run2 in document builder
_oBuilderDocWord.MoveTo(run2)

'replace 
_oBuilderDocWord.InsertField("MERGEFIELD " & _oItemFusion.sFieldCode & "* MERGEFORMAT", _oItemFusion.sFieldValue)

End If

Return Aspose.Words.ReplaceAction.Skip

Catch ex As Exception
CoRa.Outils.ExceptionManager.AddInfoToEx(ex, "Méthode", "cls_ReplaceFieldEvaluator.IReplacingCallback_Replacing : champ de fusion = " & _oItemFusion.sFieldCode)
If Outils.ExceptionManager.ExceptionAlert(ex) Then Throw
End Try
End Function

Do you have any Idea ?
Thanks for your help.
Laure

Hi Laure,
Thanks for your inquiry.
The reason for the exception is because you are trying to get the index of the entire match string from the text in the one run where as the match in those two cases actually spans over more than one run. This means the string cannot be found and the index returns -1 which is the cause of the exception later on.
You should be using Dim index As Integer = e.MatchOffset instead. However for a similar reason the next two lines of code will also not work properly. I think from the code you have implemented you are trying to split the text around the match runs so you can insert the merge field. This can be more easily achieve by splitting the runs around the match text. Please see the code on the article page here which gives a good example of this.
If you need any further help please feel free to ask.
Thanks,

Hi Adam,

Thanks for your answer ! I tried the sample code in the article and you were right the mergefields which didn’t work span on 3 runs. So as in the example, I managed to highlight all my mergefields but I didn’t manage to replace the ones which span on 3 runs since I still don’t understand very well how to use the runs (I am not a pro…) I am stuck here : In my runs array I have the 3 runs corresponding to my mergefield but how do I do to replace them by the corresponding text ??
Could you help me on this last one please ?

Thanks a lot !!

Laure

Hi

Thank you for additional information. Please see the following link to learn how to use this code:
https://forum.aspose.com/t/merge-field-formatting/56930/4
Please let me know if you need more information, I will be glad to help you.
Best regards,

Hello,

Thank you very much for your help ! now it works just fine !

Laure

Hello,

I am trying to use Mergefield and ReplaceEvaluator functions with Aspose.Words to replace text in Open Office File 3.2.1. I am using the same code describe in the previous post. The “IReplacingCallback_Replacing” procedure find all the mergefields but when it comes to replace the mergefield by its value it doesn’t work.
I join a sample file “input.odt” and the “ouput.odt” obtained.
Do you have any idea ?
Thank you for your help.
Laure

Hello

Thanks for your request. I managed to reproduce the problem on my side. Your request has been linked to the appropriate issue. You will be notified as soon as it is fixed.
It seems the problem occurs because you are using white spaces inside the MergField names. So as a workaround you can try avoiding using white spaces, replaced them, for example, with underline.
~NOM_PATIENT~
Best regards,

Hello,

Thanksyou for your help. Problem is I can’t modify the mergefields because they have been defined and used by many clients and products for a long time now. I hope this issue will find a quick answer… However I am gonna try (just for a test) to replace white spaces by underline just to check if this works on my side too and I’ll keep you inform.

Thanks again.
Laure

Hello

Thanks for your inquiry. I think in your case the information provided in this thread will be very useful for you:
https://forum.aspose.com/t/aspose-words-truncate-long-mail-merge-fields/57366/2
You can get list of fields in the template and list of columns in your data source and map corresponding fields with the corresponding columns in your data source.
Best regards,

Hello Andrey,

Thank you for your help. I have read the thread about mergefield, but unfortunately it can’t work for me because I am not using mailmerge or template document. My mergefields are made of “simple text” in a Word document or Open document, so the function WordDocument.MailMerge.GetFieldNames return an empty table and the WordDocument.MailMerge.Execute doesn’t work. You also have to know that some of my mergefields are replaced by a inserting a document (I don’t know if this is possible with mailmerge ?!). So I am still searching for a solution to make this work with Open document…

Could you give me the link to the issue related to this problem ?

Thank you.

Laure

Hello

I tried to replace white spaces by underline
in my mergefieds and it still doesn’t work with Open Document…

Laure

Hi

Oups, my mistake ! It works with underlines insteads of white spaces !

Laure

Hello

I am really sorry but in fact it doesn’t work with the underlines… I explain : After calling my replace procedure I was looking at the property WordDocument.ToTxt() to see if my mergefields have been replaced correctly and it was good !! I had the corresponding value to each mergefield but when I call WordDocument.save() I still have the same problem, my output document contains the mergefields and not the values ?! So I called WordDocument.GetText() instead of toTxt() just berfore saving to see the text and in this case my mergefields aren’t replace… so don’t understand the difference between ToTxt() and GetText() methods ?? Why do I have my mergefields replace with ToText() and not with GetText() ? And why the save() method doesn’t keep the replaced values as shown in ToTxt()??

Thanks for your help !!

Laure

Hi Laure,
Thank you for additional information. I created a simple code example that should help you to resolve the issue without changing your templates or data source. Please see my comments through the code:

public void Test001()
{
    // Supose, you have datasource with column names that contains whitespaces.
    // For testing purposes, lets use dummy data.
    DataTable data = new DataTable();
    data.Columns.Add("NOM PATIENT");
    data.Columns.Add("NOM PATIENT MIN");
    data.Columns.Add("NOM PATIENT COMPL");
    data.Columns.Add("PRENOM PATIENT");
    data.Columns.Add("DATE NAISSANCE");
    data.Columns.Add("AGE");
    data.Columns.Add("SEXE");
    data.Columns.Add("ADR_1 PAT");
    data.Columns.Add("ADR_2 PAT");
    // Add some dummy data.
    data.Rows.Add(new object[] { "Andrey", "Noskov", "V", "Mr", "12/12/12", "30", "male", "My address 1", "My Address 2" });
    // open source document with palceholders.
    Document doc = new Document(@"Test001\input.odt");
    // Replace placeholders with mergefields.
    // During replacing we will replace whitespaces in fields names with underlines.
    doc.Range.Replace(new Regex(@"\~(?.*?)\~"), new ReplaceEvaluatorFindAndInsertMergefield(), false);
    // Now we can map names in our datasource (with whitespaces) with names in the template (without whitespaces).
    foreach (DataColumn column in data.Columns)
        doc.MailMerge.MappedDataFields.Add(column.ColumnName.Replace(" ", "_"), column.ColumnName);
    // Now, we can execute mail merge.
    doc.MailMerge.Execute(data);
    // Save output.
    doc.Save(@"Test001\out.odt");
}
private class ReplaceEvaluatorFindAndInsertMergefield : IReplacingCallback
{
    /// 
    /// This method is called by the Aspose.Words find and replace engine for each match.
    /// This method highlights the match string, even if it spans multiple runs.
    /// 
    ReplaceAction IReplacingCallback.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);
        }
        // Create Document Buidler aond insert MergeField
        DocumentBuilder builder = new DocumentBuilder(e.MatchNode.Document as Document);
        builder.MoveTo((Run)runs[runs.Count - 1]);
        string fieldName = e.Match.Groups["FieldName"].Value.Replace(" ", "_");
        builder.InsertField(string.Format("MERGEFIELD {0}", fieldName), string.Format("«{0}»", fieldName));
        // Now remove all runs in the sequence.
        foreach (Run run in runs)
            run.Remove();
        // Signal to the replace engine to do nothing because we have already done all what we wanted.
        return ReplaceAction.Skip;
    }
    /// 
    /// Splits text of the specified run into two runs.
    /// Inserts the new run just after the specified run.
    /// 
    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.
Best regards,

Hi Andrey,

Thank you very much for your help and your sample code, this has been really helpful and now it works great !! I am relieved !

Laure