How to Convert XML to PDF in .NET with C#
This .NET tutorial shows how a C# application can convert XML into a tagged PDF through the pdfRest Convert to PDF API Tool. The sample combines the XML upload with layout and style options, then returns a PDF resource suitable for the rest of a document workflow.
Why Convert XML to PDF in .NET with C#?
.NET applications frequently exchange XML with ERP systems, government services, and long-established line-of-business platforms. Although the data is structured, a raw XML file is inconvenient for people who need to approve, compare, or retain the information as part of a business record.
A financial operations system, for instance, might receive an XML acknowledgement after submitting a batch to an external processor. Converting that acknowledgement into a hierarchy-formatted PDF gives reviewers a clear view of batch identifiers, status fields, warnings, and nested transaction results. The PDF can then be placed in the same audit packet as the related approvals and reports.
C# developers do not have to translate every element into drawing coordinates or create a separate HTML template. Hierarchy mode emphasizes relationships for business review, while source mode keeps literal XML syntax visible when technical fidelity is more important. Both can include tags, metadata, and consistent page formatting.
C# Code Example for Converting XML to PDF
/* * What this sample does: * - Converts structured XML 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-xml-multipart*/ using Newtonsoft.Json.Linq; using System.Net.Http.Headers; using System.Text; namespace Samples.EndpointExamples.MultipartPayload; public static class PdfFromXml { public static async Task Execute(string[] args) { var inputPath = args.Length > 0 ? args[0] : "/path/to/sample.xml"; 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# XML 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 XML 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 .xml source into StreamContent and preserves its filename in the multipart part. That extension directs the shared /pdf endpoint to the XML converter.
{
var inputPath = args.Length > 0 ? args[0] : "/path/to/sample.xml";
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 XML-related style properties used by the renderer.
{
"data_presentation": "hierarchy"
}
source is the default. It first validates the XML and then displays the user’s original source text, including its markup, whitespace, and declared encoding text. hierarchy instead creates a nested list: local element names become bold labels, attributes appear with an @ prefix, and leaf values follow their element names. Namespace prefixes are not displayed in hierarchy labels. Invalid XML and documents without a root element are rejected in either mode.
The shared style.table member does not affect XML hierarchy or source output. It can be removed from an XML-only profile without changing the resulting document.
For XML, 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 XML 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
Possible uses include documenting serialized application state, distributing configuration baselines, and creating human-readable copies of machine-to-machine messages. Teams can choose hierarchy or source presentation according to whether readability or syntax fidelity is more important.
Enabling tags contributes document structure for assistive technology and automated workflows without certifying the result against a named standard. Developers can prototype the same request in API Lab and find the authoritative field definitions in the Convert to PDF reference.