Generating Hierarchical Tables from JSON Data in .NET C#
Using TX Text Control, you can generate complex hierarchical tables directly from JSON data. This article explains the code and logic behind it.

Hierarchical tables can be easily merged using repeating merge blocks with the Mail
In this case, tables can be dynamically generated using the TX Text Control API by adding nested tables. This method can also be mixed with the MailMerge process by adding a placeholder in your template that will be replaced with the dynamically generated table.
JSON Data
Consider the following JSON data that should be used to generate the table.
[
{
"Row": 1,
"UniqueID": "900-0315918-000",
"Name": "Tim Typer",
"Address": "1 Text Control Dr.",
"City": "Charlotte",
"Zip": "28209",
"State": "NC",
"Country": "US",
"line_items": [
{
"Name": "TX Text Control",
"Description": "Awesome Document Processor",
"Price": 2998.00,
"Quantity": 1,
"additional_items": [
{
"Name": "TX Barcode .NET for Windows Forms",
"Description": "Barcode Generator for Windows Forms",
"Price": 499.00,
"Quantity": 1
},
{
"Name": "TX Spell .NET for Windows Forms",
"Description": "Spell Checking for Windows Forms",
"Price": 499.00,
"Quantity": 1
}
]
}
]
},
{
"Row": 2,
"UniqueID": "800-0315918-000",
"Name": "Kathrina Keyboard",
"Address": "1 Text Control Dr.",
"City": "Charlotte",
"Zip": "28209",
"State": "NC",
"Country": "US",
"line_items": [
{
"Name": "TX Text Control",
"Description": "Awesome Document Processor",
"Price": 2998.00,
"Quantity": 1,
"additional_items": [
{
"Name": "TX Barcode .NET for Windows Forms",
"Description": "Barcode Generator for Windows Forms",
"Price": 499.00,
"Quantity": 1
}
]
}
]
}
]
The data contains three hierarchical levels with two data rows on the first level. The JSON string is loaded and converted into a list of JObject elements returned by the DeserializeObject method of the JsonConvert class (Newtonsoft.Json). The CreateTable method is then called with the created list object.
string json = File.ReadAllText(@"data.json");
var data = JsonConvert.DeserializeObject<List<JObject>>(json);
CreateTable(data);
Recursively Adding Tables
The CreateTable method takes the hierarchical data and creates the tables. It is called recursively based on the current hierarchy level. A new table is added based on the number of columns in the current hierarchy level. For each row of data, a new table row is added and the values are added to the table cell text.
In the case where the data value is an array, a new nested table will be added by making a recursive call to the CreateTable method.
// Create a hierarchical table from a list of JSON objects
private void CreateTable(List<JObject> dataObjects) {
// Get the number of columns of the first object
int columns = dataObjects[0].Values().Count();
// Check if the first object contains an array and reduce the number of columns
columns -= dataObjects[0].Values().Count(value => value.Type == JTokenType.Array);
// Add a table with the number of columns
Table table = AddTableAtInputPosition(1, columns);
int row = 1;
// Loop through all objects
foreach (JObject dataObject in dataObjects) {
// Check if the row is not the first row and add a new row
if (row != 1) {
table.Cells.GetItem(row - 1, 1).Select();
textControl1.Selection.Start += textControl1.Selection.Length;
textControl1.Selection.Length = 0;
textControl1.Selection.Start -= 1;
// Add a new row after the current row
table.Rows.Add(TXTextControl.TableAddPosition.After, 1);
// Set the left text distance to 0 to remove the indent
table.Rows.GetItem().CellFormat.LeftTextDistance = 0;
// Split all cells
table.SplitCells();
}
int col = 1;
// Loop through all properties of the object
foreach (var info in dataObject) {
// Check if the property is an array
if (dataObject[info.Key].Type == JTokenType.Array) {
textControl1.Selection.Start = table.Cells.GetItem(row, 1).Start;
table.Rows.Add(TXTextControl.TableAddPosition.After, 1);
// Set the left text distance to indent the nested table
table.Rows.GetItem().CellFormat.LeftTextDistance = 600;
// Select the new row and merge all cells
table.Select(row + 1, 1, row + 1, table.Columns.Count);
table.MergeCells();
// Create a new table for the array
List<JObject> subObject =
dataObject[info.Key]?.Select(x => x as JObject).ToList();
// recursively call the method to create a new nested table
if (subObject != null) {
CreateTable(subObject);
row++;
}
}
else { // Add the value to the table cell
table.Cells.GetItem(row, col).Text = info.Value?.ToString();
col++;
}
}
row++;
}
}
// Add a table at the current input position and return the table object
private Table AddTableAtInputPosition(int rows, int columns) {
textControl1.Tables.Add(rows, columns);
textControl1.Selection.Start -= 1;
return textControl1.Tables.GetItem();
}
Sample Data Results
When this method is called with the sample data, the following table structure will be created.
Related Posts
Inserting MergeBlocks with the DataSourceManager and Applying Table Styles in C#
This article shows how to insert MergeBlocks with the DataSourceManager and how to apply table styles to those tables. The article uses the DocumentServer class to insert MergeBlocks with the…
Useful Tricks for Working with Tables
When you are working with complex and long tables, it can be useful to know if a table is broken up across multiple pages. These code snippets will help you with various table related tasks.
Extension Method: Converting Tables to Tabs in C#
When you export documents to formats that don't support tables, such as ANSI text, you can convert those tables to tabs. This article describes how to convert a table object to tabs using a C#…
Formatting Numbers in Table Cells
Table cell text can be interpreted as plain text or as a number with an associated number format. This article explains how to use this format and gives examples for various string formatters.
Mail Merge MS Word Office Open XML (DOCX) Templates in C#
TX Text Control's MailMerge engine allows you to perform MS Word compatible mail merge processes in .NET based applications. This article gives an overview of the basic functionality and shows how…