diff --git a/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data.sln b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data.sln new file mode 100644 index 000000000..2184ee2fe --- /dev/null +++ b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35527.113 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MailMerge-XML-with-HTML-data", "MailMerge-XML-with-HTML-data\MailMerge-XML-with-HTML-data.csproj", "{5B0540C8-1A4D-4BB4-A0B9-10028D140E00}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5B0540C8-1A4D-4BB4-A0B9-10028D140E00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B0540C8-1A4D-4BB4-A0B9-10028D140E00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B0540C8-1A4D-4BB4-A0B9-10028D140E00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B0540C8-1A4D-4BB4-A0B9-10028D140E00}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Data/Data.xml b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Data/Data.xml new file mode 100644 index 000000000..1eb899821 --- /dev/null +++ b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Data/Data.xml @@ -0,0 +1,12 @@ + + + + Login + This component handles <strong> user authentication <strong> + + + Dashboard + The dashboard <em>summarizes<em> key metrics + + + diff --git a/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Data/Template.docx b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Data/Template.docx new file mode 100644 index 000000000..8c9f91955 Binary files /dev/null and b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Data/Template.docx differ diff --git a/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/MailMerge-XML-with-HTML-data.csproj b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/MailMerge-XML-with-HTML-data.csproj new file mode 100644 index 000000000..8d8c2df15 --- /dev/null +++ b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/MailMerge-XML-with-HTML-data.csproj @@ -0,0 +1,27 @@ + + + + Exe + net8.0 + MailMerge_XML_with_HTML_data + enable + enable + + + + + + + + + Always + + + Always + + + Always + + + + diff --git a/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Output/.gitkeep b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Output/.gitkeep new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Output/.gitkeep @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Program.cs b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Program.cs new file mode 100644 index 000000000..45938c4ed --- /dev/null +++ b/Mail-Merge/MailMerge-XML-with-HTML-data/.NET/MailMerge-XML-with-HTML-data/Program.cs @@ -0,0 +1,155 @@ +using Syncfusion.DocIO; +using Syncfusion.DocIO.DLS; +using System.Dynamic; +using System.Xml; + + +// Dictionary to temporarily hold merge field HTML values and their positions. +Dictionary> paraToInsertHTML = new Dictionary>(); + +// Load the Word document template +using (WordDocument document = new WordDocument(Path.GetFullPath(@"Data/Template.docx"))) +{ + // Load the XML file that contains data to be merged + Stream xmlStream = System.IO.File.OpenRead(Path.GetFullPath(@"Data/Data.xml")); + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.Load(xmlStream); + xmlStream.Dispose(); // Close and release the file stream + + // Convert the XML data into a dynamic ExpandoObject structure + ExpandoObject allDataObject = new ExpandoObject(); + GetDataAsExpandoObject(xmlDocument.LastChild, ref allDataObject); + + // Traverse the dynamic data to extract and map mail merge records + if (allDataObject is IDictionary allObjects && + allObjects.ContainsKey("Root")) + { + var rootObjects = (allObjects["Root"] as List)[0] as IDictionary; + if (rootObjects.ContainsKey("Validator")) + { + var validatorObjects = (rootObjects["Validator"] as List)[0] as IDictionary; + + // Iterate over each component inside Validator (e.g., Login, Dashboard) + foreach (var validatorObject in validatorObjects) + { + var componentMainTag = validatorObject.Key; // Typically "Component" + var componentData = validatorObject.Value as List; + + if (componentData != null) + { + // Create a mail merge data table for each component + MailMergeDataTable componentDataTable = new MailMergeDataTable(componentMainTag, componentData); + + // Attach event handler to handle HTML merge fields + document.MailMerge.MergeField += new MergeFieldEventHandler(MergeFieldEvent); + + // Execute group mail merge using nested data table + document.MailMerge.ExecuteGroup(componentDataTable); + } + } + } + } + + // After mail merge is complete, insert HTML into placeholders + InsertHtml(); + + // Detach mail merge event handlers + document.MailMerge.MergeField -= new MergeFieldEventHandler(MergeFieldEvent); + + // Save the updated document + document.Save(Path.GetFullPath(@"Output/Result.docx"), FormatType.Docx); +} + +/// +/// Event handler that intercepts the mail merge process to handle HTML content. +/// +void MergeFieldEvent(object sender, MergeFieldEventArgs args) +{ + if (args.FieldName.Equals("Description")) + { + // Get the paragraph containing the current merge field + WParagraph paragraph = args.CurrentMergeField.OwnerParagraph; + + // Get the index of the merge field inside the paragraph + int mergeFieldIndex = paragraph.ChildEntities.IndexOf(args.CurrentMergeField); + + // Store the HTML field value with its position + Dictionary fieldValues = new Dictionary(); + fieldValues.Add(mergeFieldIndex, args.FieldValue.ToString()); + + // Associate the paragraph with its corresponding HTML values + paraToInsertHTML.Add(paragraph, fieldValues); + + // Remove the default text replacement; HTML will be inserted later + args.Text = string.Empty; + } +} + +/// +/// Recursively parses XML nodes into ExpandoObject structure for easy access. +/// Supports handling of HTML content inside text nodes. +/// +void GetDataAsExpandoObject(XmlNode node, ref ExpandoObject dynamicObject) +{ + try + { + // Check if node is simple text (possibly with HTML tags) + if (node.InnerText == node.InnerXml || (node.ChildNodes.Count == 1 && node.FirstChild.NodeType == XmlNodeType.Text)) + { + if (!(dynamicObject as IDictionary).ContainsKey(node.LocalName)) + (dynamicObject as IDictionary).Add(node.LocalName, node.InnerText); + } + else + { + List childObjects; + + // Reuse existing list if it already exists for this node + if ((dynamicObject as IDictionary).ContainsKey(node.LocalName)) + childObjects = (dynamicObject as IDictionary)[node.LocalName] as List; + else + { + childObjects = new List(); + (dynamicObject as IDictionary).Add(node.LocalName, childObjects); + } + + ExpandoObject childObject = new ExpandoObject(); + + // Recursively parse each child node + foreach (XmlNode childNode in node.ChildNodes) + { + GetDataAsExpandoObject(childNode, ref childObject); + } + + childObjects.Add(childObject); + } + } + catch (Exception e) + { + Console.WriteLine("Error in XML reading: " + e.ToString()); + } +} + +/// +/// Replaces merge fields with actual HTML content using collected placeholders. +/// +void InsertHtml() +{ + // Iterate each paragraph and its associated HTML values + foreach (KeyValuePair> dictionaryItems in paraToInsertHTML) + { + WParagraph paragraph = dictionaryItems.Key; + Dictionary values = dictionaryItems.Value; + + foreach (KeyValuePair valuePair in values) + { + int index = valuePair.Key; + string fieldValue = valuePair.Value; + + // Insert HTML at the specified location in the paragraph + paragraph.OwnerTextBody.InsertXHTML(fieldValue, paragraph.OwnerTextBody.ChildEntities.IndexOf(paragraph), index); + } + } + + // Clear dictionary after inserting all HTML + paraToInsertHTML.Clear(); +}