We're sorry Aspose doesn't work properply without JavaScript enabled.

Free Support Forum - aspose.com

Build a table and fill a hierarchical object model in it

Hi,
Can’t seem to manage to arrange hierarchical data into a tabular format (Table).
In here we attach a folder containing all the necessary data to have a clear view of what we’re trying to achieve.
.Samples.zip (25.6 KB)
The data to build the report comes from 2 text files (see the 2 text files attached (name should start with sched…))
The CurrentOutput.odt is what i achieved so far. As you’ll see the data is coming as per our requirements, only is on a simple lines displayed. We want the exact structure to be in tables, “schedule” column being the base hierarchy and each schedule to be on separate page (hence separate table)
The snippet of code up to here:

string[] feedFiles;
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);
feedFiles = ReadDocPathDetails();

List<ModelData2> lstMTRO = new List<ModelData2>();
for (int i = 0; i < feedFiles.Length - 1; i++)
{
    var feed = $"{pathToTemp}{feedFiles[i]}.txt";
    lstMTRO = LoadPMFeedDataInClass(feed);

    var schedules = from sched in lstMTRO
                    orderby sched.schedule
                    group sched by sched.schedule into newGroup1
                    from newGroup2 in (
                        from sched in newGroup1
                        orderby sched.ItemNo
                        group sched by sched.ItemNo
                    )
                    group newGroup2 by newGroup1.Key;
}

where schedules is of type: IEnumerable<IGrouping<string, IGrouping<string, ModelData2>>> -> and represent the hierarchical structure, of string and ModelData2 (just a simple POCO class).
The main point is how can we translate this algo:

foreach (var mtroSched in schedules)
{
    builder.Writeln(mtroSched.Key);
    foreach (var itemGroup in mtroSched)
    {
        builder.Writeln(itemGroup.Key);
        foreach (var details in itemGroup)
        {
            builder.Write($"{details.Street_name}" +
                $"\n{details.start ?? ""}\n{details.finish ?? ""}");
        }
    }
}

that display correctly the data but in simple lines format, into a tabular form where schedules: 230, 101.001 and X32 to be each a table.
The ExpectedOutput.docx is what we target as an end result.

Thanks in advance.

@Remus87 You can achieve this using code like the following:

foreach (var mtroSched in schedules)
{
    // This is table title. Init a page brak before.
    builder.ParagraphFormat.PageBreakBefore = true;
    // Formating of title. Can be set using style.
    builder.ParagraphFormat.Alignment = ParagraphAlignment.Center;
    builder.Font.Bold = true;
    builder.Writeln("Schedule " + mtroSched.Key);
    // Reset page break before.
    builder.ParagraphFormat.PageBreakBefore = false;
    builder.Writeln(mtroSched.Last().Last().restrdesc);
    builder.Writeln();

    // Reset center
    builder.ParagraphFormat.Alignment = ParagraphAlignment.Left;

    // Build the table.
    // Header row.
    builder.StartTable();
    builder.InsertCell();
    builder.Write("Column 1");
    builder.InsertCell();
    builder.Write("Column 2");
    builder.EndRow();

    builder.InsertCell();
    builder.Write("Item No.");
    builder.InsertCell();
    builder.Write("Length of Road");
    builder.EndRow();

    // reset bold 
    builder.Font.Bold = false;
    foreach (var itemGroup in mtroSched)
    {

        bool isFirst = true;
        foreach (var details in itemGroup)
        {
            builder.InsertCell();
            if (isFirst)
            {
                // Put key only for first row in the group
                builder.Write(itemGroup.Key);
                isFirst = false;
            }

            builder.InsertCell();
            builder.Write($"{details.Street_name}" +
                $"\n{details.start ?? ""}\n{details.finish ?? ""}");

            builder.EndRow();
        }
    }
    builder.EndTable();
}

Here is output document generated on my side: out.docx (7.6 KB)

Also, I think you can use LINQ Reporting Engine to simplify the way you generate reports.

Thanks for the solution provided. That works great.
Actually we’re looking to make more automation on it of course as there will be tens of models of reports that will have the same architecture (feeding from text / csv files dynamically) but different formatting applied in all aspects. For eg. will avoid hard coding values (as per header rows from this sample), and rather create a model to extract and serialise the column titles from feeding files.
Therefore your suggestion, using the ‘LINQ Reporting Engine’ might a good and clean alternative (hence we’re still balancing with the current solution as using the suggested will need to have an additional template document (.docx or .odt) for each client))
Since we haven’t used yet the ‘LINQ Reporting Engine’ feature, will be great if you can provide a code snippet with the current sample scenario, just to make a clear picture on how can be implemented for our use cases. Also, how well does ‘LINQ Reporting Engine’ can perform on generating in .ODT format (is almost as compatible as generating .DOCX or has many limitations in this sense) ?

@Remus87

Please check Template.docx (13.0 KB) and the following code:

Document partDoc = new Document($"{pathToTemp}Template.docx");

ReportingEngine engine = new ReportingEngine();
engine.BuildReport(partDoc, schedules, "schedules");

doc.AppendDocument(partDoc, ImportFormatMode.UseDestinationStyles);

Please also change the following line of your code:

DocumentBuilder builder = new DocumentBuilder(doc);

To this line:

doc.RemoveAllChildren();

Please note that a final document is built by parts. This is due to your data is loaded by parts (i.e. from a single data file at a time). If it is suitable for you to combine data loaded from several files into a single object then it is possible to build a final document upon a single ReportingEngine.BuildReport call and adjusting the template.

LINQ Reporting Engine does not impose any additional restrictions on ODT. If a particular ODT feature is supported by Aspose.Words then it is also supported by LINQ Reporting Engine.

Thank you for the solution provided. And great explanation!

1 Like