Skip to content

Commit a2d8ecd

Browse files
Added support for repeaters
Designed to handle nested repeaters. Did not Test with nested repeaters.
1 parent 6915d29 commit a2d8ecd

File tree

4 files changed

+193
-24
lines changed

4 files changed

+193
-24
lines changed

.vscode/launch.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"preLaunchTask": "dotnet: build",
1212
"program": "${workspaceFolder}/CSVReportGenerator/bin/Debug/net9.0/CSVReportGenerator.dll",
1313
// "args": ["-outputSchema", "${workspaceFolder}/Examples/Single/SingleTestSchema.xml", "-input", "${workspaceFolder}/Examples/Single/xmlFile1.xml"],
14-
"args": ["-outputSchema", "${workspaceFolder}/Examples/Basic/BasicTestSchema.xml", "-input", "${workspaceFolder}/Examples/Basic/xmlFile1.xml"],
14+
// "args": ["-outputSchema", "${workspaceFolder}/Examples/Basic/BasicTestSchema.xml", "-input", "${workspaceFolder}/Examples/Basic/xmlFile1.xml"],
15+
"args": ["-outputSchema", "${workspaceFolder}/Examples/Intermediate/IntermediateTestSchema.xml", "-input", "${workspaceFolder}/Examples/Intermediate/xmlFile.xml"],
1516
"cwd": "${workspaceFolder}",
1617
"stopAtEntry": false,
1718
"console": "integratedTerminal"

CSVReportGenerator/ReportGenerator.cs

Lines changed: 165 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,59 @@
66
public class RepeaterData
77
{
88
private string Path { get; set; }
9-
private XmlNode? Node { get; set; }
10-
private int iterationCount = 0;
9+
private List<XmlNode>? Nodes { get; set; }
10+
private int iterationCount = 1;
1111

12-
public RepeaterData(string name, XmlNode? node)
12+
private int iterationMax = 0;
13+
14+
public RepeaterData(string name, XmlNode node, int fileLocalMax)
1315
{
1416
Path = name;
15-
if (node != null)
17+
iterationMax = fileLocalMax;
18+
19+
Nodes = new List<XmlNode>();
20+
if (node.HasChildNodes)
1621
{
17-
Node = node;
22+
foreach (XmlNode childNode in node.ChildNodes)
23+
{
24+
Nodes.Add(childNode);
25+
}
1826
}
1927
else
2028
{
21-
Node = null;
2229
}
2330
}
2431

25-
public RepeaterData(string path)
32+
33+
34+
35+
public RepeaterData(string path, int fileCount)
2636
{
2737
Path = path;
28-
Node = null;
29-
38+
Nodes = null;
39+
iterationMax = fileCount;
3040
}
3141

3242
public string GetPath()
3343
{
3444
return Path;
3545
}
36-
public XmlNode? GetNode()
46+
public List<XmlNode> GetNodes()
3747
{
38-
return Node;
48+
return Nodes;
3949
}
40-
public int getIterationCount()
50+
public int GetIterationCount()
4151
{
4252
return iterationCount;
4353
}
4454
public int Iterate()
4555
{
56+
if (iterationCount >= iterationMax)
57+
{
58+
return -1;
59+
}
4660
iterationCount++;
47-
return iterationCount - 1;
61+
return iterationCount;
4862
}
4963
}
5064

@@ -76,19 +90,100 @@ public void CreateReport(List<XMLFile> processedXmlFiles, XMLFile schemaFile)
7690
HandleTag(node, processedXmlFiles);
7791
}
7892

79-
output.DumpToFile("outputBasic.csv");
93+
output.DumpToFile("outputIntermediate.csv");
94+
}
95+
96+
private int GetFirstNonNodeRepeaterIteration()
97+
{
98+
foreach (var repeater in repeaterStack)
99+
{
100+
if (repeater.GetNodes() == null)
101+
{
102+
return repeater.GetIterationCount();
103+
}
104+
}
105+
return -1; // Meaningful value indicating no non-node repeater found
80106
}
81107

82108
private string getFieldValue(string location, List<XMLFile> processedXmlFiles)
83109
{
84-
foreach (var xmlFile in processedXmlFiles)
110+
if (repeaterStack.Count > 0)
111+
{
112+
int fileIterationIndex = -1;
113+
if (repeaterStack.Peek().GetNodes() != null)
114+
{ //Checking if current repeater is on files or nodes
115+
116+
fileIterationIndex = GetFirstNonNodeRepeaterIteration();
117+
if (repeaterStack.Peek().GetIterationCount() != -1)
118+
{
119+
// Build a new location string by splicing in repeater iteration indices
120+
string updatedLocation = location;
121+
foreach (var repeater in repeaterStack)
122+
{
123+
string path = repeater.GetPath();
124+
int idx = updatedLocation.IndexOf(path, StringComparison.Ordinal);
125+
if (idx != -1)
126+
{
127+
// Find the end of the path in the location string
128+
int pathEnd = idx + path.Length;
129+
// Insert [iteration] after the path
130+
updatedLocation = updatedLocation.Substring(0, pathEnd) +
131+
$"[{repeater.GetIterationCount()}]" +
132+
updatedLocation.Substring(pathEnd);
133+
}
134+
}
135+
XmlNodeList? nodes = null;
136+
137+
138+
if (fileIterationIndex != -1)
139+
{
140+
nodes = processedXmlFiles[fileIterationIndex].GetDocument().SelectNodes(updatedLocation);
141+
}
142+
else
143+
{
144+
nodes = processedXmlFiles[0].GetDocument().SelectNodes(updatedLocation);
145+
}
146+
147+
if (nodes != null && nodes.Count > 0)
148+
{
149+
150+
string returnValue = "";
151+
if (updatedLocation == location)
152+
{
153+
returnValue = nodes[0].InnerText;
154+
} else {
155+
156+
returnValue = nodes[0].InnerText; //We spliced the location, therefore nodes[0] is the correct value
157+
}
158+
return returnValue; // Return the first matching node's text
159+
}
160+
else
161+
{
162+
return "";
163+
}
164+
}
165+
}
166+
else //Repeater it is on Files.
167+
{
168+
var nodes = processedXmlFiles[repeaterStack.Peek().GetIterationCount()].GetDocument().SelectNodes(location);
169+
if (nodes != null && nodes.Count > 0)
170+
{
171+
return nodes[0].InnerText; // Return the first matching node's text
172+
}
173+
}
174+
}
175+
else
85176
{
86-
var nodes = xmlFile.GetDocument().SelectNodes(location);
87-
if (nodes != null && nodes.Count > 0)
177+
foreach (var xmlFile in processedXmlFiles)
88178
{
89-
return nodes[0].InnerText; // Return the first matching node's text
179+
var nodes = xmlFile.GetDocument().SelectNodes(location);
180+
if (nodes != null && nodes.Count > 0)
181+
{
182+
return nodes[0].InnerText; // Return the first matching node's text
183+
}
90184
}
91185
}
186+
92187
return ""; // Return empty if no match found
93188
}
94189

@@ -109,16 +204,53 @@ private void HandleTag(XmlNode node, List<XMLFile> processedXmlFiles)
109204

110205
if (node.HasChildNodes)
111206
{
112-
foreach (XmlNode childNode in node.ChildNodes)
207+
if (node.Name == "Repeater" && repeaterStack.Count > 0)
208+
{
209+
var currentRepeater = repeaterStack.Peek();
210+
do
211+
{
212+
foreach (XmlNode childNode in node.ChildNodes)
213+
{
214+
Serilog.Log.Information($"Processing child of repeater node: {childNode.Name}");
215+
HandleTag(childNode, processedXmlFiles);
216+
}
217+
output.OnEnterNewLine();
218+
output.OnExitNewLine();
219+
} while (currentRepeater.Iterate() != -1);
220+
}
221+
else
113222
{
114-
Serilog.Log.Information($"Processing child node: {childNode.Name}");
115-
// Here you would implement the logic to handle child nodes
116-
// For example, you might want to recursively call a method to handle nested tags
117-
HandleTag(childNode, processedXmlFiles);
223+
foreach (XmlNode childNode in node.ChildNodes){
224+
Serilog.Log.Information($"Processing child node: {childNode.Name}");
225+
HandleTag(childNode, processedXmlFiles);
226+
}
118227
}
228+
119229
}
120230
OnExitTag(node, processedXmlFiles);
121231
}
232+
233+
private int GetFileLocalMax(string location, List<XMLFile> processedXmlFiles)
234+
{
235+
int fileVal = 0;
236+
if (repeaterStack.Count > 0)
237+
{
238+
foreach (RepeaterData repeater in repeaterStack)
239+
{
240+
if (repeater.GetNodes() == null) //This is our file Value
241+
{
242+
fileVal = repeater.GetIterationCount();
243+
}
244+
}
245+
}
246+
247+
var nodes = processedXmlFiles[fileVal].GetDocument().SelectNodes(location);
248+
if (nodes != null)
249+
{
250+
return nodes.Count;
251+
}
252+
return 0;
253+
}
122254

123255
private void OnEnterTag(XmlNode node, List<XMLFile> processedXmlFiles)
124256
{
@@ -128,6 +260,16 @@ private void OnEnterTag(XmlNode node, List<XMLFile> processedXmlFiles)
128260
if (node.Name == "Repeater")
129261
{
130262
Serilog.Log.Information("Processing 'Repeater' tag logic here.");
263+
if (node.Attributes != null && node.Attributes["location"] != null && node.Attributes["location"]?.Value != null)
264+
{
265+
repeaterStack.Push(new RepeaterData(node.Attributes["location"].Value, node, GetFileLocalMax(node.Attributes["location"].Value, processedXmlFiles)));
266+
}
267+
else if (node.Attributes != null && node.Attributes["special"] != null && node.Attributes["special"]?.Value != null)
268+
{
269+
repeaterStack.Push(new RepeaterData(node.Attributes["special"].Value, processedXmlFiles.Count));
270+
}
271+
272+
131273
}
132274
else if (node.Name == "Field")
133275
{
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Document>
2+
<Header>Name,Date,PageCount</Header>
3+
<Repeater location="root/Documents/Document">
4+
<Field special="_FileName"/>,<Field location="root/ClientInfo/ShippedDate"/>,<Field location="root/Documents/Document/PageCount"/>
5+
</Repeater>
6+
</Document>

Examples/Intermediate/xmlFile.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<root>
2+
<ClientInfo>
3+
<Name>ACME Inc.</Name>
4+
<ShippedDate>01/01/2025</ShippedDate>
5+
</ClientInfo>
6+
<Documents>
7+
<Document number="1">
8+
<PageCount>6</PageCount>
9+
</Document>
10+
<Document number="2">
11+
<PageCount>11</PageCount>
12+
</Document>
13+
<Document number="3">
14+
<PageCount>5</PageCount>
15+
</Document>
16+
<Document number="4">
17+
<PageCount>700</PageCount>
18+
</Document>
19+
</Documents>
20+
</root>

0 commit comments

Comments
 (0)