How to Convert JSON to PDF in .NET with C#
This .NET tutorial shows how C# can turn JSON into a tagged PDF with the pdfRest Convert to PDF API Tool. The sample combines a JSON upload with metadata, layout, and style options, then reads the generated PDF resource response.
Why Convert JSON to PDF in .NET with C#?
.NET workflow systems often persist approvals, automation outcomes, and external-service responses as JSON. While that representation works well in storage and code, reviewers may need a document that fits naturally into an audit packet or case-management process.
For example, an approval engine may record a completed request with nested approvers, timestamps, conditions, and decision values. C# can generate a hierarchy-formatted PDF that keeps those relationships clear and place it with the associated forms and correspondence. The source JSON remains the system record, while the PDF serves human review.
This approach avoids mapping every possible payload shape into report classes or drawing instructions. The application can use hierarchy mode for business users and source mode for technical reviewers, with consistent page formatting and tags supplied by the same conversion profile.
C# Code Example for Converting JSON to PDF
/* * What this sample does: * - Converts structured JSON input to PDF through the /pdf endpoint. * * Setup (environment): * - Set PDFREST_API_KEY=your_api_key_here * - Optional: set PDFREST_URL to override the API region. * * Usage: * dotnet run -- pdf-from-json-multipart*/ using Newtonsoft.Json.Linq; using System.Net.Http.Headers; using System.Text; namespace Samples.EndpointExamples.MultipartPayload; public static class PdfFromJson { public static async Task Execute(string[] args) { var inputPath = args.Length > 0 ? args[0] : "/path/to/sample.json"; var apiKey = Environment.GetEnvironmentVariable("PDFREST_API_KEY"); if (string.IsNullOrWhiteSpace(apiKey)) throw new InvalidOperationException("Missing PDFREST_API_KEY"); var baseUrl = Environment.GetEnvironmentVariable("PDFREST_URL") ?? "https://api.pdfrest.com"; using var client = new HttpClient { BaseAddress = new Uri(baseUrl) }; client.DefaultRequestHeaders.TryAddWithoutValidation("Api-Key", apiKey); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var options = JObject.Parse(@"{""title"":""Structured Content Sample"",""language"":""en-US"",""enable_tagging"":true,""page_setup"":{""size"":""Letter"",""orientation"":""portrait"",""margin"":{""top"":36,""right"":42,""bottom"":36,""left"":42}},""style"":{""font"":""Arial"",""heading_font"":""Arial"",""code_font"":""Courier"",""text_size"":11,""text_color_rgb"":[34,34,34],""heading_scale"":1.35,""table"":{""column_width_weights"":[2,3,2],""keep_header_with_first_row"":true,""repeat_headers_on_overflow"":true,""show_borders"":true,""border_width"":0.75,""border_color_rgb"":[180,188,200],""header_fill_color_rgb"":[33,64,98],""header_text_color_rgb"":[255,255,255],""row_fill_color_rgb"":[250,250,252],""alternate_row_fill_color_rgb"":[235,240,246],""cell_padding"":{""top"":6,""right"":8,""bottom"":6,""left"":8}}},""data_presentation"":""hierarchy""}"); using var form = new MultipartFormDataContent(); var inputContent = new ByteArrayContent(await File.ReadAllBytesAsync(inputPath)); inputContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); form.Add(inputContent, "file", Path.GetFileName(inputPath)); form.Add(new StringContent(options.ToString()), "structured_text_options"); var response = await client.PostAsync("pdf", form); var result = await response.Content.ReadAsStringAsync(); Console.WriteLine(result); if (!response.IsSuccessStatusCode) Environment.ExitCode = 1; } }
Source: View the sample on GitHub
Breaking Down the Code
The C# JSON sample uses the framework HttpClient APIs and Newtonsoft JSON types for structured_text_options. A project adopting this code must retain the Newtonsoft.Json package reference used by the samples repository.
using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;
using System.Text;
using var client = new HttpClient { BaseAddress = new Uri(baseUrl) };
using var form = new MultipartFormDataContent();
The .NET endpoint block shows which regional service receives this JSON request:
if (string.IsNullOrWhiteSpace(apiKey)) throw new InvalidOperationException("Missing PDFREST_API_KEY");
var baseUrl = Environment.GetEnvironmentVariable("PDFREST_URL") ?? "https://api.pdfrest.com";
using var client = new HttpClient { BaseAddress = new Uri(baseUrl) };
client.DefaultRequestHeaders.TryAddWithoutValidation("Api-Key", apiKey);
C# turns the .json source into StreamContent and preserves its filename in the multipart part. That extension directs the shared /pdf endpoint to the JSON converter.
{
var inputPath = args.Length > 0 ? args[0] : "/path/to/sample.json";
var apiKey = Environment.GetEnvironmentVariable("PDFREST_API_KEY");
The Newtonsoft JObject represents pdfRest’s structured conversion settings. It defines the output title and language, explicitly enables tags, configures Letter portrait pages and margins, and carries the typography and JSON-related style properties used by the renderer.
{
"data_presentation": "hierarchy"
}
source is the default. It validates and parses the input, then pretty-prints normalized JSON syntax in a code-style block; original indentation and insignificant whitespace are not preserved. hierarchy removes the syntax and creates a nested list in which property names are bold and primitive values appear as key-value items.
Scalar arrays become ordinary nested values. Arrays containing objects or other arrays label their members Item 1, Item 2, and so on. The converter preserves JSON structure but intentionally does not infer application-specific report meaning. Malformed JSON is rejected in either presentation mode.
The sample’s shared style.table object does not affect JSON source or hierarchy output. It can be omitted when the profile is used only for JSON conversion.
For JSON, MultipartFormDataContent owns the source stream, serialized settings, and output field. HttpClient generates the matching multipart boundary and content type when it sends the container.
var inputContent = new ByteArrayContent(await File.ReadAllBytesAsync(inputPath));
inputContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
form.Add(inputContent, "file", Path.GetFileName(inputPath));
form.Add(new StringContent(options.ToString()), "structured_text_options");
var response = await client.PostAsync("pdf", form);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
if (!response.IsSuccessStatusCode) Environment.ExitCode = 1;
}
}
The C# client authenticates and posts the JSON form, then reads the response as text. Reporting both status and payload lets an integration distinguish a generated PDF resource from validation or processing errors.
var inputContent = new ByteArrayContent(await File.ReadAllBytesAsync(inputPath));
inputContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
form.Add(inputContent, "file", Path.GetFileName(inputPath));
form.Add(new StringContent(options.ToString()), "structured_text_options");
var response = await client.PostAsync("pdf", form);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
if (!response.IsSuccessStatusCode) Environment.ExitCode = 1;
}
}
Beyond the Tutorial
Potential .NET use cases include workflow histories, configuration baselines, automation results, and integration acknowledgements. Objects and arrays retain their nesting without the converter inventing business-specific labels or summaries.
Tagging supplies logical organization for compatible consumers but does not itself prove accessibility conformance. Build a representative request in API Lab and verify optional fields against the Convert to PDF reference.