Skip to content

Commit 8b9c552

Browse files
Merge pull request #487 from SyncfusionExamples/963173-Generate-invoice-for-each-customer
963173 - Add Sample for generating invoices from XML data
2 parents 4bf47a2 + 923b73d commit 8b9c552

File tree

6 files changed

+191
-0
lines changed

6 files changed

+191
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.14.36518.9 d17.14
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generate-invoices-from-xml-data", "Generate-invoices-from-xml-data\Generate-invoices-from-xml-data.csproj", "{DE5C1DFA-6686-4D2C-878A-B0BC60558FF8}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{DE5C1DFA-6686-4D2C-878A-B0BC60558FF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{DE5C1DFA-6686-4D2C-878A-B0BC60558FF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{DE5C1DFA-6686-4D2C-878A-B0BC60558FF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{DE5C1DFA-6686-4D2C-878A-B0BC60558FF8}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {74001C0A-D635-46A4-AF4A-401D245EF6AA}
24+
EndGlobalSection
25+
EndGlobal
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Invoices>
3+
<Invoice>
4+
<InvoiceNumber>12345</InvoiceNumber>
5+
<Date>2025-04-28</Date>
6+
<CustomerName>John Doe</CustomerName>
7+
<Products>
8+
<Position>1</Position>
9+
<ProductSku>SKU123</ProductSku>
10+
<ProductName>Product A</ProductName>
11+
<Price>100.00</Price>
12+
</Products>
13+
<Products>
14+
<Position>2</Position>
15+
<ProductSku>SKU124</ProductSku>
16+
<ProductName>Product B</ProductName>
17+
<Price>200.00</Price>
18+
</Products>
19+
<Products>
20+
<Position>3</Position>
21+
<ProductSku>SKU125</ProductSku>
22+
<ProductName>Product C</ProductName>
23+
<Price>200.00</Price>
24+
</Products>
25+
<TotalAmount>500.00</TotalAmount>
26+
</Invoice>
27+
<Invoice>
28+
<InvoiceNumber>10295</InvoiceNumber>
29+
<Date>2025-04-20</Date>
30+
<CustomerName>Paul Henriot</CustomerName>
31+
<Products>
32+
<Position>1</Position>
33+
<ProductSku>SKU123</ProductSku>
34+
<ProductName>Product D</ProductName>
35+
<Price>150.00</Price>
36+
</Products>
37+
<Products>
38+
<Position>2</Position>
39+
<ProductSku>SKU124</ProductSku>
40+
<ProductName>Product E</ProductName>
41+
<Price>200.00</Price>
42+
</Products>
43+
<Products>
44+
<Position>3</Position>
45+
<ProductSku>SKU125</ProductSku>
46+
<ProductName>Product F</ProductName>
47+
<Price>220.00</Price>
48+
</Products>
49+
<TotalAmount>570.00</TotalAmount>
50+
</Invoice>
51+
</Invoices>
52+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<RootNamespace>Generate_invoices_from_xml_data</RootNamespace>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<Nullable>enable</Nullable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Syncfusion.DocIO.Net.Core" Version="*" />
13+
</ItemGroup>
14+
15+
16+
<ItemGroup>
17+
<None Update="Data\InvoiceDetails.xml">
18+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
19+
</None>
20+
<None Update="Data\Template.docx">
21+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
22+
</None>
23+
<None Update="Output\.gitkeep">
24+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
25+
</None>
26+
</ItemGroup>
27+
28+
</Project>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using Syncfusion.DocIO.DLS;
2+
using System.Dynamic;
3+
using System.Xml;
4+
5+
namespace Generate_invoices_from_xml_data
6+
{
7+
class Program
8+
{
9+
public static void Main(string[] args)
10+
{
11+
// Load the template Word document
12+
WordDocument document = new WordDocument(Path.GetFullPath(@"Data/Template.docx"));
13+
// start each record at new page
14+
document.MailMerge.StartAtNewPage = true;
15+
// Perform mail merge using relational XML data
16+
document.MailMerge.ExecuteNestedGroup(GetRelationalData());
17+
// Save the result Word document.
18+
document.Save(Path.GetFullPath("../../../Output/Output.docx"));
19+
// Close the Word document
20+
document.Close();
21+
}
22+
#region Helper Method
23+
/// <summary>
24+
/// Retrieves relational invoice data from XML and converts it into a MailMergeDataTable.
25+
/// </summary>
26+
private static MailMergeDataTable GetRelationalData()
27+
{
28+
// Load the XML data file
29+
Stream xmlStream = File.OpenRead(Path.GetFullPath(@"Data/InvoiceDetails.xml"));
30+
XmlDocument xmlDocument = new XmlDocument();
31+
xmlDocument.Load(xmlStream);
32+
xmlStream.Dispose();
33+
34+
ExpandoObject customerDetails = new ExpandoObject();
35+
GetDataAsExpandoObject((xmlDocument as XmlNode).LastChild, ref customerDetails);
36+
// Convert dynamic object into dictionary
37+
IDictionary<string, object> customerDict = customerDetails as IDictionary<string, object>;
38+
// Get the "Invoices" list
39+
List<ExpandoObject> invoicesList = customerDict["Invoices"] as List<ExpandoObject>;
40+
// Take the first item in that list
41+
IDictionary<string, object> firstInvoiceGroup = invoicesList[0] as IDictionary<string, object>;
42+
// Get the "Invoice" list from that group
43+
List<ExpandoObject> invoices = firstInvoiceGroup["Invoice"] as List<ExpandoObject>;
44+
// Create MailMergeDataTable with group name "Invoices"
45+
MailMergeDataTable dataTable = new MailMergeDataTable("Invoices", invoices);
46+
return dataTable;
47+
}
48+
/// <summary>
49+
/// Gets the data as ExpandoObject.
50+
/// </summary>
51+
/// <param name="node">The current XML node being processed.</param>
52+
/// <param name="dynamicObject">The dynamic object to populate with node data.</param>
53+
/// <returns></returns>
54+
private static void GetDataAsExpandoObject(XmlNode node, ref ExpandoObject dynamicObject)
55+
{
56+
if (node.InnerText == node.InnerXml)
57+
// Leaf node: store text value
58+
dynamicObject.TryAdd(node.LocalName, node.InnerText);
59+
else
60+
{
61+
// Handle child nodes
62+
List<ExpandoObject> childObjects;
63+
// If the tag already exists, reuse the list; otherwise create a new one
64+
if ((dynamicObject as IDictionary<string, object>).ContainsKey(node.LocalName))
65+
childObjects = (dynamicObject as IDictionary<string, object>)[node.LocalName] as List<ExpandoObject>;
66+
else
67+
{
68+
childObjects = new List<ExpandoObject>();
69+
dynamicObject.TryAdd(node.LocalName, childObjects);
70+
}
71+
// Create a new child object for the current node
72+
ExpandoObject childObject = new ExpandoObject();
73+
// Recursively process all child nodes
74+
foreach (XmlNode childNode in (node as XmlNode).ChildNodes)
75+
{
76+
GetDataAsExpandoObject(childNode, ref childObject);
77+
}
78+
// Add the processed child object to the list
79+
childObjects.Add(childObject);
80+
}
81+
}
82+
#endregion
83+
}
84+
}
85+

0 commit comments

Comments
 (0)