Leitfaden · REST-API

Ein PDF aus XML und einer Word-Vorlage generieren

Ihre Daten verlassen das Quellsystem als XML: eine Bestellung, ein Export, eine Nachricht. Das Layout ist eine Word-Vorlage mit Standard-Seriendruckfeldern. Der Adapter, der Ihre Elemente und Attribute auf die Felder der Vorlage abbildet, wird als Teil Ihrer Integration geliefert; der Generate-Aufruf dahinter ist derselbe, der heute JSON-Aufrufer bedient, und liefert das fertige PDF oder DOCX.

Warum das PDF nicht selbst aus XML bauen

  • Eine Stylesheet-Pipeline von XML zu Formatierungsobjekten macht das Layout zum Entwicklerartefakt; wer das Dokument verantwortet, kann es nicht ändern.
  • Eine PDF-Bibliothek, die aus geparstem XML gespeist wird, bindet das Layout an eine Laufzeit; jede Textänderung kommt mit einem Release.
  • Jedes Vorlagenformat mit eigener Syntax bedeutet Umschulung und gebundene Vorlagen.

Die Anfrage

Die heutige API nimmt den Fields-Envelope entgegen, kein XML. Der XML-Adapter wird als Teil Ihrer Integration geliefert (verfolgt als xgenerateio/xgenerate#364): Er bildet Ihre Elemente und Attribute auf skalare Felder und wiederholte Elemente auf Listenzeilen ab, benannt nach den MERGEFIELD-Codes der Vorlage. Das Beispiel zeigt das XML, den Envelope, der daraus wird, und den Generate-Aufruf, den es heute gibt.

TOKEN=$(curl -sS https://auth.xgenerate.io/realms/xgenerate/protocol/openid-connect/token \
  -d grant_type=client_credentials \
  -d client_id="$XGENERATE_CLIENT_ID" \
  -d client_secret="$XGENERATE_CLIENT_SECRET" | jq -r .access_token)

# 1. Your XML as it leaves the source system.
cat > invoice.xml <<'XML'
<invoice number="2026-0042" date="2026-10-25">
  <recipient>
    <name>Camille Durand</name>
  </recipient>
  <lines>
    <line description="Consulting, October" amount="CHF 4'800.00"/>
    <line description="Travel expenses" amount="CHF 312.40"/>
  </lines>
</invoice>
XML

# 2. The fields envelope the XML adapter produces (delivered as part of your
#    integration): elements and attributes become fields, repeating elements
#    become list rows. Names match the MERGEFIELD codes in the Word template.
cat > fields.json <<'JSON'
{
  "fields": [
    { "name": "invoice.number", "value": "2026-0042" },
    { "name": "invoice.date",   "value": "2026-10-25" },
    { "name": "recipient.name", "value": "Camille Durand" }
  ],
  "lists": [
    { "name": "invoice.lines", "value": [
      { "fields": [ { "name": "description", "value": "Consulting, October" },
                    { "name": "amount",      "value": "CHF 4'800.00" } ] },
      { "fields": [ { "name": "description", "value": "Travel expenses" },
                    { "name": "amount",      "value": "CHF 312.40" } ] }
    ] }
  ]
}
JSON

# 3. The generate call as it exists today: Word template + envelope, PDF back (format=DOCX for Word).
curl -sS https://api.xgenerate.io/v2/documents/generate \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@invoice.docx" \
  -F "fields=@fields.json;type=application/json" \
  -F "format=PDF" \
  -o invoice.pdf

Aus .NET

using System.Net.Http.Json;
using System.Xml.Linq;

using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new("Bearer", token);

// The mapping step your integration adds: elements and attributes become fields,
// repeating elements become list rows. Names match the template's MERGEFIELD codes.
var xml = XDocument.Load("invoice.xml").Root!;
var fields = new
{
    fields = new[]
    {
        new { name = "invoice.number", value = xml.Attribute("number")!.Value },
        new { name = "invoice.date",   value = xml.Attribute("date")!.Value },
        new { name = "recipient.name", value = xml.Element("recipient")!.Element("name")!.Value }
    },
    lists = new[]
    {
        new
        {
            name = "invoice.lines",
            value = xml.Element("lines")!.Elements("line").Select(line => new
            {
                fields = new[]
                {
                    new { name = "description", value = line.Attribute("description")!.Value },
                    new { name = "amount",      value = line.Attribute("amount")!.Value }
                }
            }).ToArray()
        }
    }
};

// The generate call as it exists today: Word template + envelope over the JSON transport.
var request = new
{
    filename = "invoice.docx",
    content = Convert.ToBase64String(await File.ReadAllBytesAsync("invoice.docx")),
    format = "PDF",
    fields
};
var response = await http.PostAsJsonAsync(
    "https://api.xgenerate.io/v2/documents/generate", request);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadFromJsonAsync<GenerateResult>();
await File.WriteAllBytesAsync(result!.Filename, Convert.FromBase64String(result.Content));

record GenerateResult(string Filename, string Content, string Format);

Die Antwort

Der Multipart-Aufruf streamt das PDF als Rohbytes zurück; der JSON-Transport liefert filename, content, format und warnings. Warnungen nennen Bild-, QR- und Briefkopf-Platzhalter ohne Wert; Seriendruckfelder und Bereiche ohne Daten werden ohne Warnung entfernt. Die Anfrage gelingt in beiden Fällen.

Das Ergebnis

Erste Seite der gerenderten Referenzrechnung: Briefkopf, Adressblock, Positionstabelle und Summen
Die Referenzrechnung, gerendert aus einer Word-Vorlage mit Standard-Seriendruckfeldern. Vorlage, Anfrage und erwartetes PDF: bereitgestelltes Beispiel

Was XGenerate nicht tut

XML ist heute kein Eingabeformat der API: Die Generate-Endpunkte nehmen den Fields-Envelope als JSON oder einen HTML- bzw. Markdown-Body entgegen, und der XML-Adapter wird als Teil Ihrer Integration gegen Ihr Beispiel-XML gebaut (xgenerateio/xgenerate#364). XSLT-Stylesheets werden nicht ausgeführt. Bedingte Blöcke in Abhängigkeit von einem Datenwert werden nicht unterstützt; Word-IF-Felder um Text schon.

Testen Sie es mit Ihrer eigenen Vorlage

Schicken Sie uns eine Word-Datei, die Sie heute verwenden. Wir rendern sie mit Ihren Daten und senden das PDF zusammen mit der angepassten Vorlage zurück — Ihre Evaluation muss nicht auf uns warten.

Demo anfragen

Verwandte Leitfäden