Как сгенерировать отчет в формате PDF из данных JSON на C#


    In this short article, we'll show you how to dynamically generate PDF report with pictures and table based on JSON data and .docx template under .NET platform.

    We'll see the latest C# 9.0 language and newest standard "System.Text.Json" assembly, and also our SDK - "SautinSoft.Document" which will perform the whole work by us.
So, we'll generate report in PDF format with list of cat breeds, their description and picture.
Assume that in the real app we've a JSON object containing all data regarding cat breeds: title, description, link to the picture and a weight range.

    In our app we'll create the JSON object manually and populate it by the testing data.

Let's start:

  1. First of all, we've to create a class that describe the cat breed:

    class CatBreed
        {
            public string Title { get; set; }
            public string Description { get; set; }
            public string PictUrl { get; set; }
            /// <summary>
            /// Weight in lb. (Fields in template: WeightFrom, WeightTo). Here we are using a tuple.
            /// </summary>
            public (int, int) Weight { get; set; }        
        }
    
  2. Create a method that populates the collection by testing data (various cat breeds), serializes this collection to JSON object and returns it as string:

    public static string CreateJsonObject()
            {
                string json = String.Empty;
                List<CatBreed> cats = new List<CatBreed>
                {
                    new CatBreed() {Title = "Australian Mist",
                        Description = "The Australian Mist (formerly known as the Spotted Mist) is a breed of cat developed in Australia.",
                        PictUrl = "australian-mist.jpg",
                        Weight = (8, 15)},                    
                    new CatBreed() {Title = "Maine Coon",
                        Description = "The Maine Coon is a large domesticated cat breed. It has a distinctive physical appearance and valuable hunting skills.",
                        PictUrl = "maine-coon.png",
                        Weight = (13, 18)},                    
                    new CatBreed() {Title = "Scottish Fold",
                        Description = "The original Scottish Fold was a white barn cat named Susie, who was found at a farm near Coupar Angus in Perthshire, Scotland, in 1961.",
                        PictUrl = "scottish-fold.jpg",
                        Weight = (9, 13)},
                    new CatBreed() {Title = "Oriental Shorthair",
                        Description = "The Oriental Shorthair is a breed of domestic cat that is developed from and closely related to the Siamese cat.",
                        PictUrl = "oriental-shorthair.jpg",
                        Weight = (8, 12)},                    
                    new CatBreed() {Title = "Bengal cat",
                        Description = "The earliest mention of an Asian leopard cat × domestic cross was in 1889, when Harrison Weir wrote of them in Our Cats and ...",
                        PictUrl = "bengal-cat.jpg",
                        Weight = (10, 15)},                    
                    new CatBreed() {Title = "Russian Blue",
                        Description = "The Russian Blue is a naturally occurring breed that may have originated in the port of Arkhangelsk in Russia.",
                        PictUrl = "russian-blue.jpg",
                        Weight = (8, 15)},
                    new CatBreed() {Title = "Mongrel cat",
                        Description = "A mongrel, mutt or mixed-breed cat is a cat that does not belong to one officially recognized breed, but he's cool and gentle!",
                        PictUrl = "mongrel-cat.jpg",
                        Weight = (8, 16)}                    
                };
    
                // Generate full path for the cat's pictures.
                string pictDirectory = Path.GetFullPath(@"..\..\picts\");
                foreach (var cb in cats)
                {
                    cb.PictUrl = Path.Combine(pictDirectory, cb.PictUrl);
                }
    
                // Make serialization to JSON format.
                JsonSerializerOptions options = new() {IncludeFields = true };
                json = JsonSerializer.Serialize(cats, options);
                return json;
            }
    
  3. We've to prepare the .docx template for the report using MS Word or any other .docs app, here you'll find how to create DOCX template

  4. The final method which we'll create has to accept JSON as string and generate PDF, for example as "PDF/A1". Actually the SDK - "SautinSoft.Document" allows you to choose any output format for the report from: PDF, DOCX, HTML or even RTF.
    Here in this method we'll also add the event handler to decorate our by pictures. The links to these pictures are stored in the JSON object in the field "PictUrl".
    The list of this method:

    public static void GeneratePdfReport(string json)
            {
                // Get data from json.
                JsonSerializerOptions options = new() { IncludeFields = true };
                var cats = JsonSerializer.Deserialize<List<CatBreed>>(json, options);            
    
                // Load the template document.
                string templatePath = @"..\..\cats-template.docx";
    
                DocumentCore dc = DocumentCore.Load(templatePath);
    
                // To be able to mail merge from your own data source, it must be wrapped into an object that implements the IMailMergeDataSource interface.
                CustomMailMergeDataSource customDataSource = new CustomMailMergeDataSource(cats);
    
                // Decorate each cat beed by by appropriate picture.
                // Set picture width to 80 mm, height to Auto.
                dc.MailMerge.FieldMerging += (senderFM, eFM) =>
                {
                    // Insert an icon before the product name
                    if (eFM.RangeName == "CatBreed" && eFM.FieldName == "PictUrl")
                    {
                        eFM.Inlines.Clear();
                        string pictPath = eFM.Value.ToString();
                        Picture pict = new Picture(dc, pictPath);
                        double kWH = 1f;
                        double desiredWidthMm = 80;
    
                        if (pict.Layout.Size.Width > 0 && pict.Layout.Size.Height > 0)
                            kWH = pict.Layout.Size.Width / pict.Layout.Size.Height;
    
                        pict.Layout = new InlineLayout(new Size(desiredWidthMm, desiredWidthMm / kWH, LengthUnit.Millimeter));
    
                        eFM.Inlines.Add(pict);                    
                        eFM.Cancel = false;
                    }
                };
    
                // Execute the mail merge.
                dc.MailMerge.Execute(customDataSource);
    
                string resultPath = "CatBreeds.pdf";
    
                // Save the output to file
                PdfSaveOptions so = new PdfSaveOptions()
                {
                    Compliance = PdfCompliance.PDF_A1a
                };
    
                dc.Save(resultPath, so);
    
                // Open the result for demonstration purposes.
                System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(resultPath) { UseShellExecute = true });
            }
    
  5. Here you will find the complete source code of our report application:

Download the resulting file: CatBreeds.pdf

Полный код

using System;
using System.IO;
using System.Collections.Generic;
using SautinSoft.Document;
using SautinSoft.Document.Drawing;
using SautinSoft.Document.MailMerging;
using Newtonsoft.Json;

/// <summary>
/// Generates a report in PDF format (PDF/A) based on JSON data and .docx template.
/// </summary>
/// <remarks>
/// See details at: https://www.sautinsoft.com/products/document/help/net/developer-guide/mail-merge-generate-pdf-report-from-json-data-net-csharp-vb.php
/// </remarks>
namespace CatBreedReportApp
{
    class CatBreed
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public string PictUrl { get; set; }
        /// <summary>
        /// Weight in lb. (Fields in template: WeightFrom, WeightTo). Here we are using a tuple.
        /// </summary>
        public (int, int) Weight { get; set; }        
    }
    class Program
    {
        static void Main(string[] args)
        {
            // Get your free 30-day key here:   
            // https://sautinsoft.com/start-for-free/

            // 1. Get json data
            string json = CreateJsonObject();

            // 2. Show json to Console.
            Console.WriteLine(json);

            // 3. Generate report based on .docx template and json.
            GeneratePdfReport(json);
        }

        public static void GeneratePdfReport(string json)
        {
            // Get data from json.            
            var cats = JsonConvert.DeserializeObject<List<CatBreed>>(json);

            // Load the template document.
            string templatePath = @"..\..\..\cats-template.docx";

            DocumentCore dc = DocumentCore.Load(templatePath);

            // To be able to mail merge from your own data source, it must be wrapped into an object that implements the IMailMergeDataSource interface.
            CustomMailMergeDataSource customDataSource = new CustomMailMergeDataSource(cats);

            // Decorate each cat beed by by appropriate picture.
            // Set picture width to 80 mm, height to Auto.
            dc.MailMerge.FieldMerging += (senderFM, eFM) =>
            {
                // Insert an icon before the product name
                if (eFM.RangeName == "CatBreed" && eFM.FieldName == "PictUrl")
                {
                    eFM.Inlines.Clear();
                    string pictPath = eFM.Value.ToString();
                    Picture pict = new Picture(dc, pictPath);
                    double kWH = 1f;
                    double desiredWidthMm = 80;

                    if (pict.Layout.Size.Width > 0 && pict.Layout.Size.Height > 0)
                        kWH = pict.Layout.Size.Width / pict.Layout.Size.Height;

                    pict.Layout = new InlineLayout(new Size(desiredWidthMm, desiredWidthMm / kWH, LengthUnit.Millimeter));

                    eFM.Inlines.Add(pict);                    
                    eFM.Cancel = false;
                }
            };

            // Execute the mail merge.
            dc.MailMerge.Execute(customDataSource);

            string resultPath = "CatBreeds.pdf";

            // Save the output to file
            PdfSaveOptions so = new PdfSaveOptions()
            {
                Compliance = PdfCompliance.PDF_A1a
            };

            dc.Save(resultPath, so);

            // Open the result for demonstration purposes.
            System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(resultPath) { UseShellExecute = true });
        }
        public static string CreateJsonObject()
        {
            string json = String.Empty;
            List<CatBreed> cats = new List<CatBreed>
            {
                new CatBreed() {Title = "Australian Mist",
                    Description = "The Australian Mist (formerly known as the Spotted Mist) is a breed of cat developed in Australia.",
                    PictUrl = "australian-mist.jpg",
                    Weight = (8, 15)},                    
                new CatBreed() {Title = "Maine Coon",
                    Description = "The Maine Coon is a large domesticated cat breed. It has a distinctive physical appearance and valuable hunting skills.",
                    PictUrl = "maine-coon.png",
                    Weight = (13, 18)},                    
                new CatBreed() {Title = "Scottish Fold",
                    Description = "The original Scottish Fold was a white barn cat named Susie, who was found at a farm near Coupar Angus in Perthshire, Scotland, in 1961.",
                    PictUrl = "scottish-fold.jpg",
                    Weight = (9, 13)},
                new CatBreed() {Title = "Oriental Shorthair",
                    Description = "The Oriental Shorthair is a breed of domestic cat that is developed from and closely related to the Siamese cat.",
                    PictUrl = "oriental-shorthair.jpg",
                    Weight = (8, 12)},                    
                new CatBreed() {Title = "Bengal cat",
                    Description = "The earliest mention of an Asian leopard cat × domestic cross was in 1889, when Harrison Weir wrote of them in Our Cats and ...",
                    PictUrl = "bengal-cat.jpg",
                    Weight = (10, 15)},                    
                new CatBreed() {Title = "Russian Blue",
                    Description = "The Russian Blue is a naturally occurring breed that may have originated in the port of Arkhangelsk in Russia.",
                    PictUrl = "russian-blue.jpg",
                    Weight = (8, 15)},
                new CatBreed() {Title = "Mongrel cat",
                    Description = "A mongrel, mutt or mixed-breed cat is a cat that does not belong to one officially recognized breed, but he's cool and gentle!",
                    PictUrl = "mongrel-cat.jpg",
                    Weight = (8, 16)}                    
            };

            // Generate full path for the cat's pictures.
            string pictDirectory = Path.GetFullPath(@"..\..\..\picts\");
            foreach (var cb in cats)
            {
                cb.PictUrl = Path.Combine(pictDirectory, cb.PictUrl);
            }

            // Make serialization to JSON format.            
            json = JsonConvert.SerializeObject(cats);
            return json;
        }

        /// <summary>
        /// A custom mail merge data source that allows SautinSoft.Document to retrieve data from CatBeeds objects.
        /// </summary>
        public class CustomMailMergeDataSource : IMailMergeDataSource
        {
            private readonly List<CatBreed> _cats;
            private int _recordIndex;

            /// <summary>
            /// The name of the data source. 
            /// </summary>
            public string Name
            {
                get { return "CatBreed"; }
            }

            /// <summary>
            /// SautinSoft.Document calls this method to get a value for every data field.
            /// </summary>
            public bool TryGetValue(string valueName, out object value)
            {
                switch (valueName)
                {
                    case "Title":
                        value = _cats[_recordIndex].Title;
                        return true;
                    case "Description":
                        value = _cats[_recordIndex].Description;
                        return true;
                    case "PictUrl":
                        value = _cats[_recordIndex].PictUrl;
                        return true;
                    case "WeightFrom":
                        value = _cats[_recordIndex].Weight.Item1;
                        return true;
                    case "WeightTo":
                        value = _cats[_recordIndex].Weight.Item2;
                        return true;
                    default:
                        // A field with this name was not found
                        value = null;
                        return false;
                }
            }

            /// <summary>
            /// A standard implementation for moving to a next record in a collection.
            /// </summary>
            public bool MoveNext()
            {
                return (++_recordIndex < _cats.Count);
            }

            public IMailMergeDataSource GetChildDataSource(string sourceName)
            {
                return null;
            }
            public CustomMailMergeDataSource(List<CatBreed> cats)
            {
                _cats = cats;
                // When the data source is initialized, it must be positioned before the first record.
                _recordIndex = -1;
            }
        }
    }
}

Download

Imports System
Imports System.IO
Imports System.Collections.Generic
Imports SautinSoft.Document
Imports SautinSoft.Document.Drawing
Imports SautinSoft.Document.MailMerging
Imports Newtonsoft.Json
''' Get your free 30-day key here:   
''' https://sautinsoft.com/start-for-free/
''' <summary>
''' Generates a report in PDF format (PDF/A) based on JSON data and .docx template.
''' </summary>
''' <remarks>
''' See details at: https://www.sautinsoft.com/products/document/help/net/developer-guide/mail-merge-generate-pdf-report-from-json-data-net-csharp-vb.php
''' </remarks>
Namespace CatBreedReportApp
    Friend Class CatBreed
        Public Property Title() As String
        Public Property Description() As String
        Public Property PictUrl() As String
        Public Property WeightMin() As Integer
        Public Property WeightMax() As Integer

    End Class

    Friend Class Program
        Shared Sub Main(ByVal args() As String)
            ' 1. Get json data
            Dim json As String = CreateJsonObject()

            ' 2. Show json to Console.
            Console.WriteLine(json)

            ' 3. Generate report based on .docx template and json.
            GeneratePdfReport(json)
        End Sub
        Public Shared Sub GeneratePdfReport(ByVal json As String)
            ' Get data from json.            
            Dim cats = JsonConvert.DeserializeObject(Of List(Of CatBreed))(json)

            ' Load the template document.
            Dim templatePath As String = "..\..\..\cats-template.docx"

            Dim dc As DocumentCore = DocumentCore.Load(templatePath)

            ' To be able to mail merge from your own data source, it must be wrapped into an object that implements the IMailMergeDataSource interface.
            Dim customDataSource As New CustomMailMergeDataSource(cats)

            ' Decorate each cat beed by by appropriate picture.
            ' Set picture width to 80 mm, height to Auto.
            AddHandler dc.MailMerge.FieldMerging, Sub(senderFM, eFM)
                                                      ' Insert an icon before the product name
                                                      If eFM.RangeName = "CatBreed" AndAlso eFM.FieldName = "PictUrl" Then
                                                          eFM.Inlines.Clear()
                                                          Dim pictPath As String = eFM.Value.ToString()
                                                          Dim pict As New Picture(dc, pictPath)
                                                          Dim kWH As Double = 1.0F
                                                          Dim desiredWidthMm As Double = 80

                                                          If pict.Layout.Size.Width > 0 AndAlso pict.Layout.Size.Height > 0 Then
                                                              kWH = pict.Layout.Size.Width \ pict.Layout.Size.Height
                                                          End If

                                                          pict.Layout = New InlineLayout(New Size(desiredWidthMm, desiredWidthMm / kWH, LengthUnit.Millimeter))

                                                          eFM.Inlines.Add(pict)
                                                          eFM.Cancel = False
                                                      End If
                                                  End Sub

            ' Execute the mail merge.
            dc.MailMerge.Execute(customDataSource)

            Dim resultPath As String = "CatBreeds.pdf"

            ' Save the output to file
            Dim so As New PdfSaveOptions() With {.Compliance = PdfCompliance.PDF_A1a}

            dc.Save(resultPath, so)

            ' Open the result for demonstration purposes.
            System.Diagnostics.Process.Start(New System.Diagnostics.ProcessStartInfo(resultPath) With {.UseShellExecute = True})
        End Sub
        Public Shared Function CreateJsonObject() As String
            Dim json As String = String.Empty
            Dim cats As New List(Of CatBreed) From {
                New CatBreed() With {
                    .Title = "Australian Mist",
                    .Description = "The Australian Mist (formerly known as the Spotted Mist) is a breed of cat developed in Australia.",
                    .PictUrl = "australian-mist.jpg",
                    .WeightMin = 8,
                    .WeightMax = 15
                },
                New CatBreed() With {
                    .Title = "Maine Coon",
                    .Description = "The Maine Coon is a large domesticated cat breed. It has a distinctive physical appearance and valuable hunting skills.",
                    .PictUrl = "maine-coon.png",
                    .WeightMin = 13,
                    .WeightMax = 18
                },
                New CatBreed() With {
                    .Title = "Scottish Fold",
                    .Description = "The original Scottish Fold was a white barn cat named Susie, who was found at a farm near Coupar Angus in Perthshire, Scotland, in 1961.",
                    .PictUrl = "scottish-fold.jpg",
                    .WeightMin = 9,
                    .WeightMax = 13
                },
                New CatBreed() With {
                    .Title = "Oriental Shorthair",
                    .Description = "The Oriental Shorthair is a breed of domestic cat that is developed from and closely related to the Siamese cat.",
                    .PictUrl = "oriental-shorthair.jpg",
                    .WeightMin = 8,
                    .WeightMax = 12
                },
                New CatBreed() With {
                    .Title = "Bengal cat",
                    .Description = "The earliest mention of an Asian leopard cat × domestic cross was in 1889, when Harrison Weir wrote of them in Our Cats and ...",
                    .PictUrl = "bengal-cat.jpg",
                    .WeightMin = 10,
                    .WeightMax = 15
                },
                New CatBreed() With {
                    .Title = "Russian Blue",
                    .Description = "The Russian Blue is a naturally occurring breed that may have originated in the port of Arkhangelsk in Russia.",
                    .PictUrl = "russian-blue.jpg",
                    .WeightMin = 8,
                    .WeightMax = 15
                },
                New CatBreed() With {
                    .Title = "Mongrel cat",
                    .Description = "A mongrel, mutt or mixed-breed cat is a cat that does not belong to one officially recognized breed, but he's cool and gentle!",
                    .PictUrl = "mongrel-cat.jpg",
                    .WeightMin = 8,
                    .WeightMax = 16
                }
            }

            ' Generate full path for the cat's pictures.
            Dim pictDirectory As String = Path.GetFullPath("..\..\..\picts\")
            For Each cb In cats
                cb.PictUrl = Path.Combine(pictDirectory, cb.PictUrl)
            Next cb

            ' Make serialization to JSON format.            
            json = JsonConvert.SerializeObject(cats)
            Return json
        End Function
        ''' <summary>
        ''' A custom mail merge data source that allows SautinSoft.Document to retrieve data from CatBeeds objects.
        ''' </summary>
        Public Class CustomMailMergeDataSource
            Implements IMailMergeDataSource

            Private ReadOnly _cats As List(Of CatBreed)
            Private _recordIndex As Integer

            ''' <summary>
            ''' The name of the data source. 
            ''' </summary>
            Public ReadOnly Property Name() As String
                Get
                    Return "CatBreed"
                End Get
            End Property

            Private ReadOnly Property IMailMergeDataSource_Name As String Implements IMailMergeDataSource.Name
                Get
                    Return "CatBreed"
                End Get
            End Property

            ''' <summary>
            ''' SautinSoft.Document calls this method to get a value for every data field.
            ''' </summary>
            Public Function TryGetValue(ByVal valueName As String, <System.Runtime.InteropServices.Out()> ByRef value As Object) As Boolean
                Select Case valueName
                    Case "Title"
                        value = _cats(_recordIndex).Title
                        Return True
                    Case "Description"
                        value = _cats(_recordIndex).Description
                        Return True
                    Case "PictUrl"
                        value = _cats(_recordIndex).PictUrl
                        Return True
                    Case "WeightFrom"
                        value = _cats(_recordIndex).WeightMin
                        Return True
                    Case "WeightTo"
                        value = _cats(_recordIndex).WeightMax
                        Return True
                    Case Else
                        ' A field with this name was not found
                        value = Nothing
                        Return False
                End Select
            End Function

            Public Sub New(ByVal cats As List(Of CatBreed))
                _cats = cats
                ' When the data source is initialized, it must be positioned before the first record.
                _recordIndex = -1
            End Sub

            Private Function IMailMergeDataSource_MoveNext() As Boolean Implements IMailMergeDataSource.MoveNext
                _recordIndex += 1
                Return (_recordIndex < _cats.Count)
            End Function

            Private Function IMailMergeDataSource_TryGetValue(valueName As String, ByRef value As Object) As Boolean Implements IMailMergeDataSource.TryGetValue
                Select Case valueName
                    Case "Title"
                        value = _cats(_recordIndex).Title
                        Return True
                    Case "Description"
                        value = _cats(_recordIndex).Description
                        Return True
                    Case "PictUrl"
                        value = _cats(_recordIndex).PictUrl
                        Return True
                    Case "WeightFrom"
                        value = _cats(_recordIndex).WeightMin
                        Return True
                    Case "WeightTo"
                        value = _cats(_recordIndex).WeightMax
                        Return True
                    Case Else
                        ' A field with this name was not found
                        value = Nothing
                        Return False
                End Select
            End Function

            Private Function IMailMergeDataSource_GetChildDataSource(sourceName As String) As IMailMergeDataSource Implements IMailMergeDataSource.GetChildDataSource
                Return Nothing
            End Function
        End Class

    End Class
End Namespace

Download


Если вам нужен пример кода или у вас есть вопрос: напишите нам по адресу [email protected] или спросите в онлайн-чате (правый нижний угол этой страницы) или используйте форму ниже:



Вопросы и предложения всегда приветствуются!

Мы разрабатываем компоненты .Net с 2002 года. Мы знаем форматы PDF, DOCX, RTF, HTML, XLSX и Images. Если вам нужна помощь в создании, изменении или преобразовании документов в различных форматах, мы можем вам помочь. Мы напишем для вас любой пример кода абсолютно бесплатно.