Hagemeister Conception
← Zurück zum Blog

Warum man E‑Rechnungs‑XML niemals mit Textfunktionen zusammenbauen sollte

Seit dem 1. Januar 2025 müssen Unternehmen in Deutschland, soweit sie unter die gesetzlichen Regeln für inländische B2B-Umsätze fallen, E‑Rechnungen empfangen können. Für die Ausstellung gelten Übergangsfristen: Bis Ende 2026 dürfen grundsätzlich noch andere Rechnungsformen verwendet werden; für Unternehmen mit höchstens 800.000 Euro Vorjahresumsatz und für bestimmte EDI-Verfahren reicht die Übergangsregelung bis Ende 2027. Die Einzelheiten und Ausnahmen ergeben sich aus dem Umsatzsteuerrecht und den jeweils aktuellen Hinweisen des Bundesfinanzministeriums. (BMF-FAQ)

Damit wird die Erzeugung von E‑Rechnungen für immer mehr FileMaker-Lösungen zu einer produktiven Kernfunktion. Genau hier liegt eine gefährliche Versuchung: Weil XML in einem Texteditor lesbar ist, scheint es naheliegend, die Datei mit Textfunktionen zusammenzusetzen.

"<ram:Name>" & Kunden::Firmenname & "</ram:Name>"

Das Ergebnis sieht nach XML aus. Technisch ist es zunächst jedoch nur eine Zeichenfolge. Ob daraus eine korrekte E‑Rechnung entsteht, hängt nicht allein von den sichtbaren Tags ab, sondern von der gewählten Syntax, der exakten Elementreihenfolge, der Verschachtelung, den Namespaces, den Datentypen, den Kardinalitäten, den Codelisten, den Summenregeln und den Regeln des jeweiligen Rechnungsprofils.

Präzise formuliert: XML ist textbasiert, aber kein frei formulierbarer Text. Eine E‑Rechnung sollte deshalb aus einem strukturierten Datenmodell als XML-Baum erzeugt und anschließend durch einen XML-Serializer in Bytes umgewandelt werden. Textfunktionen dürfen Werte vorbereiten. Sie sollten nicht das XML-Markup erzeugen.

Die JSON-Analogie: Was bei JSON selbstverständlich ist, gilt auch für XML

Kaum ein erfahrener FileMaker-Entwickler würde heute JSON so erzeugen:

"{\"name\":\"" & Kunden::Firmenname & "\"}"

Stattdessen verwendet man JSONSetElement. Die Funktion kennt den Pfad, unterscheidet Zeichenketten, Zahlen, Boolesche Werte und null und übernimmt die notwendige JSON-Serialisierung. Ein Anführungszeichen oder ein Zeilenumbruch im Firmennamen zerstört dadurch nicht die gesamte Datenstruktur. (Claris-JSONSetElement)

Genau dieselbe Einsicht muss auf XML übertragen werden:

Ein Datenformat wird nicht dadurch sicher erzeugt, dass man seine sichtbare Schreibweise mit Textfunktionen nachbaut.

FileMaker besitzt allerdings kein eingebautes, allgemeines und namespace-fähiges Gegenstück zu JSONSetElement, mit dem sich beliebige XML-Bäume wie UBL oder CII in einer Berechnung aufbauen lassen. Die integrierten XML-Import- und Exportmechanismen verwenden FileMaker-eigene XML-Grammatiken und können diese über eine kontrollierte XSLT-Transformation umformen. Sie sind aber kein allgemeiner XMLSetElement-Baustein für eine EN‑16931-Syntax. (Claris-XML)

Daraus folgt nicht, dass XML mit FileMaker unmöglich wäre. Die Struktur muss nur außerhalb einer verketteten Textberechnung entstehen, beispielsweise

  • in einem spezialisierten Webdienst,
  • in einer Bibliothek oder einem Plugin mit XML-API,
  • in einer kontrollierten XSLT-Transformation oder
  • in einem anderen namespace-fähigen XML-Builder.

FileMaker kann dafür ein strukturiertes, neutrales Datenobjekt liefern – etwa JSON, das mit JSONSetElement aufgebaut wurde. Der XML-Generator ordnet diese Daten anschließend der richtigen UBL- oder CII-Struktur zu.

Die EN 16931 ist kein einzelnes XML-Format

Die EN 16931-1 definiert das semantische Datenmodell der europäischen E‑Rechnung. Sie beschreibt Geschäftsbegriffe wie Rechnungsnummer, Verkäufername, Rechnungsdatum oder Umsatzsteuerbetrag und legt fachliche Beziehungen und Geschäftsregeln fest.

Die Norm definiert aber nicht eine einzige technische XML-Struktur. Das semantische Modell wird insbesondere auf zwei XML-Syntaxen abgebildet:

  • UBL 2.1,
  • UN/CEFACT Cross Industry Invoice, kurz CII.

Die konkrete XML-Struktur, die zulässigen Elemente und ihre Reihenfolge ergeben sich aus der jeweiligen Syntaxbindung und den zugehörigen XML-Schemata. (EU-EN16931)

Wie unterschiedlich diese Abbildung ist, zeigt bereits der Verkäufername, also BT‑27. In UBL befindet er sich beispielsweise unter folgendem Pfad:

Invoice
└── cac:AccountingSupplierParty
    └── cac:Party
        └── cac:PartyLegalEntity
            └── cbc:RegistrationName

In CII wird derselbe Geschäftsbegriff an einer anderen Stelle abgebildet:

rsm:CrossIndustryInvoice
└── rsm:SupplyChainTradeTransaction
    └── ram:ApplicableHeaderTradeAgreement
        └── ram:SellerTradeParty
            └── ram:Name

Die fachliche Aussage ist dieselbe, die technische Darstellung aber grundverschieden. Ein Programm erzeugt deshalb nicht einfach „ein EN‑16931‑XML“, sondern immer ein XML in einer konkreten Syntax und einem konkreten Profil, etwa XRechnung, Peppol BIS Billing oder einem ZUGFeRD-/Factur‑X-Profil.

Die Reihenfolge der Elemente ist festgelegt

In einem normalen Textdokument ist die Reihenfolge einzelner Angaben häufig frei. In einem XML-Dokument, das gegen ein XSD-Schema geprüft wird, ist sie dagegen oft Bestandteil des Datenmodells.

XML Schema verwendet dafür unter anderem xs:sequence. Die darin aufgeführten Elemente müssen in der vorgegebenen Reihenfolge erscheinen. Über minOccurs und maxOccurs wird zusätzlich festgelegt, ob ein Element fehlen darf, genau einmal oder mehrfach vorkommen kann. (W3C-XSD-Structures)

Ein stark verkürzter Ausschnitt einer UBL-Rechnung kann so aussehen:

<cbc:CustomizationID>...</cbc:CustomizationID>
<cbc:ProfileID>...</cbc:ProfileID>
<cbc:ID>R-2026-4711</cbc:ID>
<cbc:IssueDate>2026-08-22</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>

Wird DocumentCurrencyCode vor IssueDate eingefügt, kann das Dokument weiterhin vollkommen wohlgeformtes XML sein. Es entspricht dann aber nicht mehr der vom UBL-Schema erwarteten Reihenfolge und ist XSD-invalide. Die offizielle Peppol-Dokumentation zeigt den vollständigen UBL-Baum einschließlich Reihenfolge und Kardinalitäten. (Peppol-UBL-Tree)

Eine wichtige Feinheit: Die Reihenfolge von Attributen ist in XML dagegen nicht bedeutungstragend. Wer XML als Text vergleicht, kann daher Unterschiede sehen, obwohl Parser beide Varianten als gleichwertig behandeln. Auch das zeigt, warum Zeichenketten nicht das richtige Datenmodell für XML sind.

Bei einer Textverkettung muss der Entwickler die Elementreihenfolge über zahlreiche optionale Gruppen und Untergruppen hinweg selbst sichern. Sobald später ein neues optionales Element ergänzt wird, muss es nicht nur erzeugt, sondern exakt an der im Schema vorgesehenen Position eingefügt werden.

XML ist ein Baum, keine Sammlung von Tags

Ein wohlgeformtes XML-Dokument besitzt genau ein Wurzelelement. Alle weiteren Elemente müssen vollständig und korrekt ineinander verschachtelt sein. Der Name eines schließenden Tags muss zum zugehörigen öffnenden Tag passen. (W3C-XML)

Folgende Struktur ist nicht wohlgeformt:

<ram:SellerTradeParty>
    <ram:Name>Muster GmbH
</ram:SellerTradeParty>
    </ram:Name>

Die Elemente überschneiden sich. Ein XML-Parser muss das Dokument bereits vor jeder fachlichen Prüfung ablehnen.

Schwieriger zu erkennen ist eine formal geschlossene, aber strukturell falsche Verschachtelung:

<ram:BuyerTradeParty>
    <ram:SellerTradeParty>
        ...
    </ram:SellerTradeParty>
</ram:BuyerTradeParty>

Dieses Fragment kann als XML wohlgeformt sein. SellerTradeParty steht aber unter einem Elternknoten, unter dem das CII-Schema dieses Element nicht erlaubt. Das Dokument scheitert deshalb erst bei der XSD-Validierung.

Falsche Verschachtelung kann somit zwei verschiedene Fehlerarten erzeugen:

  1. Überschneidende, falsch geschlossene oder fehlende Tags ergeben nicht wohlgeformtes XML.
  2. Formal korrekt geschlossene Elemente am falschen Ort ergeben möglicherweise wohlgeformtes, aber schema-invalide XML.

Sonderzeichen müssen im richtigen Kontext serialisiert werden

Ein Firmenname wie

Müller & Söhne <Nord>

kann nicht unverändert zwischen XML-Tags eingesetzt werden:

<ram:Name>Müller & Söhne <Nord></ram:Name>

Das & wird als Beginn einer Entity-Referenz interpretiert; <Nord> sieht für den Parser wie ein Element aus. Richtig serialisiert lautet der Text beispielsweise:

<ram:Name>Müller &amp; Söhne &lt;Nord&gt;</ram:Name>

In normalen Textknoten müssen insbesondere & und < geschützt werden. In Attributwerten kommen abhängig vom verwendeten Anführungszeichen weitere Regeln hinzu. (W3C-XML)

Ad-hoc aufgebaute oder über mehrere Berechnungen verteilte Ketten aus Austauschen() – englisch Substitute() – sind kein vollständiger XML-Serializer. Typische Fehler sind:

  • Ein Feld wird versehentlich gar nicht behandelt.
  • Ein bereits behandelter Wert wird ein zweites Mal escaped.
  • Ein logischer Wert & wird vorzeitig zu &amp; umgeschrieben und bei der späteren Serialisierung fälschlich zu &amp;amp;. Soll der fachliche Text dagegen tatsächlich die Zeichenfolge &amp; enthalten, ist &amp;amp; die korrekte XML-Schreibweise.
  • Textknoten und Attributwerte werden gleichbehandelt, obwohl ihr Kontext unterschiedlich ist.
  • Ein Benutzerwert wird versehentlich als Markup interpretiert.

Ein XML-Serializer erhält dagegen den ursprünglichen Wert Müller & Söhne <Nord> als Textknoten und erzeugt die passende Schreibweise erst beim Serialisieren.

Nicht jedes Zeichen ist in XML 1.0 darstellbar

Escaping kann nicht jedes Zeichen retten. Die Char-Produktion von XML 1.0 erlaubt aus dem Bereich der klassischen C0-Steuerzeichen ausschließlich

  • Tabulator: U+0009,
  • Line Feed: U+000A,
  • Carriage Return: U+000D.

U+0000 bis U+0008, U+000B, U+000C sowie U+000E bis U+001F sind in XML 1.0 unzulässig. Auch eine numerische Zeichenreferenz macht sie nicht legal:

<ram:Name>&#x0B;</ram:Name>

ist ebenfalls kein zulässiges XML 1.0. (W3C-XML-Characters)

Solche Zeichen sind keineswegs rein theoretisch. Sie können über Excel-Copy-and-paste, Altdaten, Fremdimporte oder fehlerhafte Schnittstellen in FileMaker-Feldern landen. Ein normgerechter Serializer sollte die Serialisierung dann mit einem Fehler abbrechen; er kann aus einem in XML 1.0 verbotenen Zeichen kein gültiges XML machen. Eine Textverkettung schreibt das Zeichen dagegen unkontrolliert in den Ausgabestrom und erzeugt damit sicher ein fehlerhaftes Dokument.

Das Prüfen dieser Werte gehört tatsächlich zur Datenvorbereitung in FileMaker oder unmittelbar an die Grenze des XML-Generators. Dabei sollte es eine bewusste Regel geben:

  • unzulässige Zeichen zurückweisen und den betroffenen Datensatz melden oder
  • nur nach einer ausdrücklich dokumentierten Fachregel ersetzen beziehungsweise entfernen.

Ein stilles Löschen ist gefährlich, weil dadurch Rechnungsinhalte unbemerkt verändert werden können.

Der Sonderfall ]]>

Ein einzelnes > ist in einem Textknoten grundsätzlich erlaubt. Die exakte Zeichenfolge ]]> darf dort jedoch nicht vorkommen, weil sie das Ende eines CDATA-Abschnitts markiert. Ein Serializer kann den Wert beispielsweise so ausgeben:

<ram:Description>Vergleich: ]]&gt;</ram:Description>

Die Zeichenfolge ist im geparsten Text weiterhin ]]>, die XML-Schreibweise bleibt aber zulässig. Wer XML mit Textfunktionen zusammensetzt, muss auch diesen kaum bekannten Sonderfall selbst erkennen. (W3C-XML)

CDATA ist keine Lösung für das Escaping

Der Reflex, alle Texte in CDATA-Abschnitte einzuschließen, verschiebt das Problem nur:

<ram:Description><![CDATA[Text mit & und <]]></ram:Description>

Ein CDATA-Abschnitt kann selbst kein ]]> enthalten. Ein korrekter Generator müsste ihn an dieser Stelle in mehrere Abschnitte aufteilen. Für den fachlichen Zeichenwert sind CDATA und normaler, korrekt escapeter Text gleichwertig. CDATA schafft daher keinen fachlichen Mehrwert und keine zusätzliche Sicherheit; es führt nur einen weiteren Sonderfall ein. (W3C-Serialization)

Whitespace ist Inhalt – und wird je nach Kontext unterschiedlich behandelt

Whitespace gehört zu den am häufigsten unterschätzten XML-Problemen. Ein Parser entfernt Zeilenumbrüche und Einrückungen nicht pauschal. Ob Leerzeichen erhalten, ersetzt oder zusammengefasst werden, hängt vom XML-Kontext und gegebenenfalls vom XSD-Datentyp ab.

Zeilenenden werden vor der Verarbeitung normalisiert

Nach XML 1.0 normalisiert der Parser rohe Zeilenenden, bevor die Anwendung den Text erhält:

  • CRLF wird zu LF,
  • ein einzelnes CR wird ebenfalls zu LF.

(W3C-XML-LineEnds)

Das ist für FileMaker besonders relevant. Die Return-Taste erzeugt nach der Claris-Dokumentation den Zeichencode 13, also CR; die Enter-Taste kann den Zeichencode 10, also LF, liefern. (Claris-Code)

Ein mehrzeiliger Freitext kann daher nach dem XML-Roundtrip andere Zeilenendzeichen enthalten als im ursprünglichen FileMaker-Feld. Soll ein tatsächliches CR als Zeichen erhalten bleiben, darf es nicht als rohes Byte in der XML-Datei stehen. Ein geeigneter Serializer muss es als Zeichenreferenz wie &#xD; ausgeben. Ob eine konkrete Bibliothek das bei konstruierten Textknoten automatisch tut, sollte getestet werden; auf eine Textverkettung darf man sich dabei keinesfalls verlassen.

Für die meisten Rechnungsfreitexte ist eine bewusste Vereinheitlichung auf LF sinnvoller als der Erhalt verschiedener historischer Zeilenendtypen. Entscheidend ist, dass dies eine dokumentierte Datenregel und kein zufälliger Nebeneffekt des Parsers ist.

Hübsche Einrückung kann Werte verändern

Folgende Schreibweise wirkt gut lesbar:

<cbc:ID>
    R-2026-4711
</cbc:ID>

Der Textknoten besteht aber nicht nur aus R-2026-4711. Er enthält auch Zeilenumbrüche und führende Leerzeichen. XML selbst trimmt sie nicht automatisch.

Bei einer XSD-Validierung kommt zusätzlich die whiteSpace-Facette des Datentyps ins Spiel:

  • xs:string verwendet preserve: Whitespace bleibt erhalten.
  • xs:normalizedString verwendet replace: Tab, LF und CR werden zu Leerzeichen; führende Leerzeichen bleiben grundsätzlich bestehen.
  • xs:token verwendet collapse: Folgen von Whitespace werden zu einem Leerzeichen zusammengefasst und führender beziehungsweise abschließender Whitespace entfernt.
  • Zahlen-, Boolean- und Datumsdatentypen verwenden ebenfalls eine kollabierende Whitespace-Behandlung.

(W3C-XSD-Datatypes)

Das erklärt einen tückischen Praxiseffekt: Bei Beträgen oder Datumswerten scheint eingerückter Textbau oft zu funktionieren, weil der Datentyp den Whitespace kollabiert. Bei Bezeichnern, Namen oder anderen Zeichenketten kann dieselbe Einrückung dagegen als Teil des Werts erhalten bleiben oder nur teilweise normalisiert werden. Zudem sieht eine nicht schema-aware empfangende Anwendung möglicherweise den unveränderten Textknoten.

Ein XML-Generator sollte deshalb niemals Zeilenumbrüche und Einrückungen innerhalb eines Elements mit einfachem Inhalt erzeugen. Pretty Printing darf nur zwischen Elementen stattfinden, wo es keinen fachlichen Textwert verändert.

Attributwerte werden ebenfalls normalisiert

In Attributwerten ersetzt der XML-Parser rohe Tabulatoren, LF und CR durch Leerzeichen. Bei bestimmten deklarierten Attributtypen kommen weitere Kollapsregeln hinzu. Soll etwa ein LF tatsächlich Teil des Attributwerts sein, muss es als Zeichenreferenz wie &#xA; serialisiert werden. (W3C-XML-Attributes)

Auch hier reicht eine allgemeine Funktion „Sonderzeichen ersetzen“ nicht aus. Textknoten und Attribute brauchen eine kontextabhängige Serialisierung.

Zeichenkodierung und BOM: Gültige Zeichen müssen auch als richtige Bytes ankommen

Selbst ein korrekt aufgebauter XML-Baum kann als Datei unlesbar werden, wenn die Zeichenkodierung falsch behandelt wird. FileMaker kann beim Schreiben in eine Datendatei unter anderem UTF‑8 und UTF‑16LE ausgeben. (Claris-WriteDataFile)

Die XML-Deklaration muss zu den tatsächlichen Bytes passen:

<?xml version="1.0" encoding="UTF-8"?>

Steht dort UTF-8, während die Datei tatsächlich UTF‑16LE enthält, ist nicht nur ein Umlaut falsch. Der Parser kann bereits die XML-Deklaration und die Tags nicht zuverlässig lesen. Umgekehrt darf ein UTF‑8-Datenstrom nicht als UTF‑16 deklariert werden.

Der Byte Order Mark, kurz BOM, wird häufig pauschal als Fehler bezeichnet. Das ist zu grob:

  • Bei UTF‑16 gehört eine Encoding-Signatur am Anfang des Datenstroms zur zuverlässigen Erkennung.
  • Bei UTF‑8 ist ein BOM zulässig, aber nicht erforderlich.
  • Ein BOM am Anfang des Byte-Datenstroms steht nicht „vor dem XML-Inhalt“, sondern dient der Kodierungserkennung.

(W3C-XML-Encoding)

Die tatsächlichen Praxisfehler sind andere:

  • Die deklarierte Kodierung passt nicht zu den geschriebenen Bytes.
  • Mehrere Text- oder Containeroperationen setzen zusätzliche Bytes vor den XML-Anfang.
  • U+FEFF wird aus einem importierten Feld als normales Zeichen in den Inhalt übernommen.
  • Eine bereits codierte Bytefolge wird nochmals als Text interpretiert und erneut codiert.
  • Ein BOM wird bei Stringoperationen wie gewöhnlicher Inhalt behandelt.

Die letzte Validierung sollte daher nicht nur auf einer internen Zeichenkette, sondern auf genau der Bytefolge erfolgen, die anschließend übertragen oder archiviert wird.

Namespaces sind Bestandteil des Namens

Bei Elementen wie

<rsm:CrossIndustryInvoice>
<ram:Name>
<udt:DateTimeString>
<cbc:ID>
<cac:Party>

sind rsm, ram, udt, cbc und cac keine optischen Dekorationen:

  • rsm wird in CII für den Nachrichten- beziehungsweise Dokumentbereich verwendet,
  • ram für wiederverwendbare fachliche Aggregatstrukturen,
  • udt für unqualifizierte Datentypen,
  • cbc in UBL für grundlegende Komponenten,
  • cac in UBL für zusammengesetzte Komponenten.

Der eigentliche XML-Name ist die Kombination aus Namespace-URI und lokalem Namen. Das sichtbare Präfix kann grundsätzlich anders heißen, solange es an dieselbe URI gebunden ist. Umgekehrt bezeichnet ram:Name bei einer falschen Namespace-Bindung ein anderes Element, obwohl es im Editor richtig aussieht. (W3C-Namespaces)

Ein XML-Builder erzeugt deshalb nicht bloß die Zeichenfolge ram:Name, sondern ein Element mit einer bestimmten Namespace-URI und dem lokalen Namen Name.

Der Default-Namespace gilt nicht für unpräfixierte Attribute

Ein Default-Namespace gilt für unpräfixierte Elemente, aber nie für unpräfixierte Attribute. Das Attribut ID="123" liegt also nicht automatisch im Namespace seines Elements. Soll ein Attribut in einem Namespace liegen, braucht es ein eigenes Präfix. (W3C-Namespaces)

Diese Regel wird bei kopierten Textfragmenten leicht übersehen, weil Element und Attribut optisch zusammengehören.

xmlns="" setzt den Default-Namespace zurück

Eine weitere klassische Falle ist eine mitgeschleppte leere Namespace-Deklaration:

<root xmlns="urn:example:invoice">
    <child xmlns="">Inhalt</child>
</root>

child sieht wie ein normales Kindelement aus, gehört aber zu keinem Namespace. Es ist damit ein anderes Element als {urn:example:invoice}child. Das Dokument bleibt wohlgeformt; ein Schema für den erwarteten Namespace erkennt den Knoten jedoch nicht.

Solche Fehler entstehen besonders leicht, wenn XML-Fragmente aus unterschiedlichen Quellen kopiert oder durch Textfunktionen ineinandergeschoben werden.

xsi:schemaLocation ist nur ein Hinweis

Ein Eintrag wie

xsi:schemaLocation="urn:example:invoice https://example.invalid/invoice.xsd"

zwingt einen Validator nicht, genau dieses Schema zu laden. xsi:schemaLocation ist ein Hinweis. Ein Validator kann ihn ignorieren, über einen lokalen Katalog auflösen oder bei ungeeigneter Konfiguration sogar gar keine vollständige Schema-Prüfung durchführen. (W3C-XSD-Structures)

Für E‑Rechnungen sollten die zulässigen Schemata und Schematron-Regeln aus einer vertrauenswürdigen, lokal konfigurierten Validator-Distribution stammen. Die Validierung darf nicht davon abhängen, welche URL der Rechnungsaussteller in das Dokument geschrieben hat.

Datentypen haben eine feste lexikalische Darstellung

Ein fachlich richtiger Wert kann im XML trotzdem falsch geschrieben sein. Das XSD legt nicht nur fest, dass ein Element eine Zahl oder ein Datum enthält, sondern auch, welche Zeichenfolge dafür zulässig ist.

Dezimalzahlen und Integer

Die deutsche Anzeige

1.234,56

ist kein gültiger lexikalischer Wert für xs:decimal. XML Schema verwendet den Punkt als Dezimaltrennzeichen und keine Tausendertrennzeichen:

1234.56

Auch Exponentialschreibweise ist bei xs:decimal nicht zulässig:

1.5E3

ist als xs:double möglich, aber nicht als xs:decimal. FileMaker kann Zahlen abhängig von Größenordnung und Anzeigeformat wissenschaftlich darstellen. Deshalb darf nicht die zufällige Bildschirmdarstellung in das XML übernommen werden. (W3C-XSD-Datatypes) (Claris-NumberFormat)

Für einen Integer sind ebenfalls keine Gruppierungszeichen zulässig. 1.000 ist nicht der XML-Integer eintausend, sondern ein ungültiger Integerwert. Korrekt ist:

1000

Boolean

xs:boolean akzeptiert ausschließlich

true
false
1
0

Nicht zulässig sind beispielsweise TRUE, False, Ja oder Nein. (W3C-XSD-Datatypes)

Das betrifft unter anderem Zu- und Abschlagsindikatoren wie cbc:ChargeIndicator. Hinzu kommt eine weitere Validierungsebene: Während XSD sowohl true/false als auch 1/0 erlaubt, verlangt die Peppol-Regel PEPPOL-EN16931-R043 für ChargeIndicator ausdrücklich die Literale true oder false. Ein Wert 1 kann also XSD-valide und trotzdem profilwidrig sein. (Peppol-Rules)

Datum

Ein XSD-Datum darf grundsätzlich eine Zeitzone tragen:

2026-08-22+02:00

Das ist ein gültiger Wert für xs:date. Ein Rechnungsprofil kann strenger sein. Peppol verlangt für die betreffenden Datumsfelder mit der Regel F001 die Form YYYY-MM-DD; eine angehängte Zeitzone wird dort verworfen. (Peppol-Rules)

In UBL erscheint das Rechnungsdatum typischerweise so:

<cbc:IssueDate>2026-08-22</cbc:IssueDate>

In CII wird es üblicherweise mit einem Formatqualifizierer dargestellt:

<ram:IssueDateTime>
    <udt:DateTimeString format="102">20260822</udt:DateTimeString>
</ram:IssueDateTime>

Bei format="102" wird YYYYMMDD ohne Trennzeichen erwartet. Die deutsche Anzeige 22.08.2026 ist in beiden Beispielen falsch. Offizielle CII-Testinstanzen der KoSIT verwenden diese Darstellung. (KoSIT-CII-Example)

Damit zeigt sich erneut die Hierarchie der Prüfungen:

  • Eine Zeichenfolge kann XML-technisch problemlos sein.
  • Sie kann sogar zum allgemeinen XSD-Datentyp passen.
  • Das konkrete E‑Rechnungsprofil kann sie dennoch verbieten.

Derselbe Elementname kann an vielen Stellen etwas anderes bedeuten

E‑Rechnungsformate sind nicht nur komplex, weil sie viele unterschiedliche Elemente besitzen. Dieselben lokalen Namen kommen an zahlreichen Stellen vor.

In einer UBL-Rechnung kann cbc:ID beispielsweise die

  • Rechnungsnummer,
  • Bestellreferenz,
  • Kennung eines Dokuments,
  • Projektkennung,
  • Positionsnummer oder
  • Artikelkennung

enthalten. Entscheidend ist nicht cbc:ID allein, sondern der vollständige Pfad mit allen Elternknoten. (Peppol-UBL-Tree)

Bei CII gilt dasselbe für ram:ID, ram:Name, ram:TypeCode, ram:CategoryCode oder ram:ApplicableTradeTax. Ein Textbaustein wie

<ram:ID>12345</ram:ID>

sagt für sich genommen fast nichts darüber aus, welcher Business Term damit gemeint ist. Erst seine Position im XML-Baum stellt die fachliche Verbindung her.

Genau deshalb ist das Einsetzen vermeintlich passender XML-Schnipsel so riskant: Das Element kann richtig heißen und einen plausiblen Wert enthalten, aber im falschen fachlichen Kontext stehen.

Nicht vorhanden, leer und nil sind drei verschiedene Zustände

Textvorlagen enthalten häufig für jedes denkbare Feld bereits ein Element:

<ram:TelephoneUniversalCommunication>
    <ram:CompleteNumber></ram:CompleteNumber>
</ram:TelephoneUniversalCommunication>

Ist keine Telefonnummer vorhanden, bleibt der Inhalt leer. Das ist aber nicht dasselbe wie ein fehlendes optionales Element.

Diese beiden Schreibweisen sind für den XML-Parser gleichwertige leere Elemente:

<ram:CompleteNumber></ram:CompleteNumber>
<ram:CompleteNumber/>

Die selbstschließende Schreibweise löst das Problem daher nicht. Beide Varianten bedeuten: Das Element ist vorhanden und hat keinen Textinhalt.

Eine dritte Variante ist

<ram:CompleteNumber xsi:nil="true"/>

Sie ist nur zulässig, wenn die Elementdeklaration im Schema nillable="true" erlaubt. xsi:nil ist kein allgemeiner Ersatz für das Weglassen optionaler Angaben und wird an den typischen Stellen der Rechnungsprofile nicht als universelle Lösung akzeptiert.

Peppol verbietet mit PEPPOL-EN16931-R008 leere Elemente ausdrücklich. Wenn für eine optionale Gruppe keine Daten vorliegen, muss häufig nicht nur das Wertelement, sondern der gesamte übergeordnete Block entfallen. (Peppol-Rules)

Bei einer Textvorlage entstehen dafür verschachtelte Fallunterscheidungen. Ein strukturierter Builder erzeugt die Gruppe dagegen nur dann, wenn das zugrunde liegende Datenobjekt tatsächlich vorhanden ist.

Codelisten sind keine freien Texte

Viele Werte dürfen nicht frei formuliert werden. Sie müssen aus festgelegten Codelisten stammen. Beispiele sind:

  • EUR statt „Euro“,
  • DE statt „Deutschland“,
  • C62 für eine Einheit,
  • 380 für den Rechnungsdokumenttyp Rechnung.

Ein Wert wie Euro enthält kein problematisches Sonderzeichen und ergibt wohlgeformtes XML. An einer Stelle, an der ein ISO-Währungscode verlangt wird, ist er trotzdem fachlich ungültig.

Ein XML-Serializer kann das nicht allein verhindern. Er weiß, wie ein Textknoten korrekt geschrieben wird, aber nicht, ob der Wert aus der richtigen fachlichen Codeliste stammt. Dafür braucht es ein typisiertes Domänenmodell, kontrollierte Wertemengen und die anschließende Prüfung mit den Regeln des jeweiligen Profils. (XRechnung)

Rundung und Steueraggregation sind Geschäftslogik, keine Formatierung

Ein besonders folgenreicher Fehler entsteht, wenn die XML-Erzeugung als reine Darstellungsschicht behandelt wird. Die EN‑16931-Regeln prüfen nicht nur die Schreibweise einzelner Werte, sondern auch rechnerische Beziehungen.

Zu den zentralen Regeln gehören beispielsweise:

  • BR-CO-10 bis BR-CO-17 für Summen und Beziehungen zwischen Netto-, Steuer- und Zahlbeträgen,
  • BR-CO-14: Der gesamte Umsatzsteuerbetrag muss der Summe der Steuerbeträge der einzelnen Umsatzsteueraufschlüsselungen entsprechen,
  • BR-CO-17: Der Steuerbetrag einer Kategorie ergibt sich aus Bemessungsgrundlage × Steuersatz ÷ 100, auf zwei Nachkommastellen gerundet,
  • BR-S-08: Die Bemessungsgrundlage einer Standardsteuer-Kategorie muss aus den zugehörigen Rechnungszeilen zuzüglich Zuschlägen und abzüglich Nachlässen mit derselben Kategorie und demselben Satz aggregiert werden,
  • BR-S-09: Der Steuerbetrag dieser Kategorie muss aus der aggregierten Bemessungsgrundlage und dem Steuersatz berechnet werden.

(Peppol-EN16931-Rules)

In CII ist ram:ApplicableTradeTax ein gutes Beispiel für die Gefahr identischer Knotennamen an unterschiedlichen Stellen:

  • Auf Zeilenebene beschreibt ein ram:ApplicableTradeTax die steuerliche Einordnung der einzelnen Rechnungsposition.
  • Im Header unter ram:ApplicableHeaderTradeSettlement bilden ram:ApplicableTradeTax-Blöcke die aggregierte Umsatzsteueraufschlüsselung ab.

Die Zeilenblöcke dürfen nicht einfach eins zu eins in den Header kopiert werden. Zehn Positionen mit derselben Steuerkategorie und demselben Steuersatz ergeben grundsätzlich eine zusammengefasste Steueraufschlüsselung – gegebenenfalls weiter differenziert nach den Merkmalen, die das Profil verlangt. Ein Header-Steuerblock pro Rechnungszeile führt schnell zu doppelten oder widersprüchlichen Aufschlüsselungen und verletzt die Aggregations- und Summenregeln.

Auch die Rundung muss fachlich festgelegt sein. Es reicht nicht, am Ende alle sichtbaren Beträge mit zwei Nachkommastellen zu formatieren. Notwendig sind

  • eine dezimale Rechenlogik statt zufälliger Anzeige- oder Binärdarstellung,
  • klar definierte Rundungspunkte,
  • die Aggregation nach Steuerkategorie und Steuersatz,
  • der Abgleich aller Zwischen- und Endsummen mit den Geschäftsregeln.

Ein XML-Serializer kann diese Entscheidungen nicht treffen. Er schreibt nur den Betrag korrekt, den er erhält. Ist die fachliche Berechnung falsch, serialisiert er den Fehler tadellos.

Wohlgeformt ist noch lange nicht valide

Bei E‑Rechnungen müssen mehrere Prüfungsebenen getrennt werden:

PrüfungsebeneWas wird geprüft?Typischer Fehler
Byte- und ZeichenebeneKodierung, BOM, zulässige XML-ZeichenDeklariertes UTF‑8, tatsächlich UTF‑16LE
XML-ParserWurzelelement, geschlossene Tags, Verschachtelung, EscapingNicht escapetes & oder verbotenes U+000B
Namespace- und XSD-PrüfungElementnamen, Reihenfolge, Kardinalitäten, DatentypenElement am falschen Ort oder 1.5E3 als Decimal
EN‑16931-GeschäftsregelnSummen, Abhängigkeiten, SteuerlogikSteueraufschlüsselung stimmt nicht mit den Zeilen überein
Profil beziehungsweise CIUSZusätzliche Regeln von XRechnung, Peppol oder ZUGFeRD/Factur‑XXSD-Boolean 1, obwohl das Profil true verlangt

Ein Dokument kann vom XML-Parser problemlos geöffnet werden und trotzdem keine valide E‑Rechnung sein. Auch eine erfolgreiche XSD-Validierung reicht nicht aus, weil zahlreiche fachliche Regeln nicht sinnvoll im XSD ausgedrückt werden können.

Für XRechnung werden die Syntaxschemata von UBL und CII mit zusätzlichen Schematron-Regeln für EN 16931 und die deutsche CIUS kombiniert. Es gibt kein einzelnes „XRechnungs-XSD“, das alle fachlichen Anforderungen vollständig abdeckt. Die offizielle KoSIT-Validator-Konfiguration bündelt deshalb XSD-, Schematron- und Konfigurationsartefakte. (KoSIT-Validator)

Auch ZUGFeRD und Factur‑X liefern profilspezifische XSD- und Schematron-Artefakte. Verwendet werden muss immer die zur erzeugten Profil- und Versionsangabe passende Validatorfassung. (FeRD)

Gültiges XML kann unsichtbar falsch sein

Nicht jeder problematische Wert macht das XML formal ungültig. Einige Zeichen sind vollkommen legal, aber fachlich oder technisch überraschend:

  • ein geschütztes Leerzeichen U+00A0 statt eines normalen Leerzeichens,
  • Zero-Width Space U+200B oder andere unsichtbare Formatzeichen aus Copy-and-paste,
  • derselbe sichtbare Buchstabe einmal als vorgefertigtes Zeichen und einmal als Grundzeichen plus kombinierendes Zeichen,
  • ein versehentlich übernommenes U+FEFF im Feldinhalt.

Unicode erlaubt verschiedene Codepunktfolgen, die für Menschen gleich aussehen. NFC und NFD sind zwei unterschiedliche Normalisierungsformen. Daten aus macOS, Office-Dokumenten, Webformularen oder Fremdimporten können in unterschiedlichen Formen ankommen; daraus folgt nicht, dass macOS jeden Text pauschal verändert. (Unicode-Normalization)

Für die optische Darstellung ist der Unterschied oft irrelevant. Bei exakten Vergleichen, Dublettenprüfungen, Hashwerten und digitalen Signaturen sind die Bytes jedoch verschieden. XML-Kanonisierung ordnet unter anderem Namespaces und Attribute, führt aber nicht automatisch eine allgemeine Unicode-Normalisierung des Zeichenmodells durch. (W3C-C14N)

Deshalb braucht eine Anwendung eine bewusste Regel:

  • Fachliche Eingabewerte können vor der Erzeugung auf eine definierte Form, häufig NFC, normalisiert werden, soweit dies ihre Bedeutung nicht verändert.
  • Verdächtige unsichtbare Zeichen sollten erkannt, protokolliert und je nach Feld zurückgewiesen oder kontrolliert bereinigt werden.
  • Ein bereits signiertes oder gehashtes Dokument darf anschließend nicht mehr normalisiert oder anderweitig verändert werden.

Ein XML-Serializer ist notwendig, aber nicht hinreichend

Ein guter Serializer übernimmt insbesondere

  • korrekt geschlossene Elemente,
  • kontextgerechtes Escaping,
  • Namespace-Deklarationen,
  • eine definierte Zeichenkodierung,
  • die zulässige Ausgabe von Zeilenenden und Zeichenreferenzen,
  • die Zurückweisung nicht serialisierbarer XML-Zeichen.

Er weiß aber nicht automatisch,

  • welcher Business Term an welchen UBL- oder CII-Pfad gehört,
  • ob ein Pflichtfeld fehlt,
  • ob ein Ländercode oder eine Einheit zulässig ist,
  • ob Steuerkategorie und Steuersatz zusammenpassen,
  • ob Abschläge, Nachlässe und Steueraufschlüsselungen richtig aggregiert sind,
  • ob alle Rechnungssummen rechnerisch konsistent sind.

Die belastbare Erzeugung folgt daher einer Pipeline:

FileMaker-Daten
    ↓
fachliche Pflichtfeld- und Plausibilitätsprüfung
    ↓
kontrollierte Bereinigung unzulässiger Zeichen
und gegebenenfalls definierte Unicode-Normalisierung
    ↓
typisiertes Domänenmodell
    ↓
Abbildung auf UBL oder CII
    ↓
Aufbau eines namespace-fähigen XML-Baums
    ↓
Serialisierung in eine definierte Byte-Kodierung
    ↓
XSD-Validierung
    ↓
EN-16931- und Profilvalidierung mit Schematron
    ↓
Versand und Archivierung exakt der validierten Bytes

Das XML ist dabei das Ergebnis des Prozesses. Es ist nicht die Textvorlage, in die Daten eingesetzt werden.

Auch die Empfangsseite braucht einen sicheren XML-Parser

Wer fremde E‑Rechnungen einliest, verarbeitet nicht nur strukturierte Daten, sondern potenziell nicht vertrauenswürdige XML-Dateien. Die XML-Syntax kennt DTDs, interne und externe Entities sowie weitere Mechanismen, die bei ungeeigneter Parserkonfiguration missbraucht werden können.

Für einen Rechnungsimport sollten deshalb mindestens

  • DTD- und DOCTYPE-Verarbeitung abgeschaltet oder strikt zurückgewiesen,
  • externe allgemeine und Parameter-Entities deaktiviert,
  • das Laden externer DTDs und Ressourcen unterbunden,
  • XInclude deaktiviert,
  • Netzwerkzugriffe des Parsers verhindert,
  • Grenzen für Dateigröße, Tiefe, Textmenge und Entity-Expansion gesetzt,
  • xsi:schemaLocation-URLs aus dem Dokument nicht ungeprüft abgerufen

werden.

Damit werden unter anderem XXE-Angriffe, lokale Dateizugriffe, SSRF und Entity-Expansion-Angriffe wie „Billion Laughs“ verhindert. Die OWASP-Empfehlungen betonen, dass sichere Einstellungen bibliotheks- und versionsabhängig sind und ausdrücklich geprüft werden müssen. (OWASP-XXE)

Bei einem eigenen Plugin oder Webdienst muss die Parserkonfiguration bewusst gewählt und getestet werden. Sich auf vermeintlich sichere Standardwerte zu verlassen, ist bei Rechnungen aus beliebigen Quellen nicht ausreichend.

Was tatsächlich in FileMaker gehört

FileMaker bleibt ein sinnvoller Ort für die fachliche Vorbereitung. Dazu gehören beispielsweise

  • Pflichtfeld- und Plausibilitätsprüfungen,
  • die Zuordnung interner Werte zu ISO- und UN/ECE-Codes,
  • die kontrollierte Ermittlung von Datum, Menge und Dezimalwerten,
  • die Erkennung unzulässiger XML‑1.0-Steuerzeichen,
  • eine dokumentierte Unicode-Normalisierung, sofern erforderlich,
  • die Berechnung und Gruppierung von Steuerdaten im fachlichen Datenmodell,
  • die Übergabe eines strukturierten neutralen Payloads, etwa mit JSONSetElement.

Nicht in eine FileMaker-Textberechnung gehören dagegen

  • das Verketten öffnender und schließender XML-Tags,
  • das manuelle Escaping einzelner Felder,
  • die Verwaltung von Namespace-Präfixen als Text,
  • das Einrücken von Elementinhalten mit Absatzzeichen,
  • das Kopieren von Steuerblöcken aus Rechnungszeilen in den Header,
  • die Annahme, eine erfolgreiche XML-Anzeige im Browser sei bereits eine Validierung.

Eine XSLT-basierte Lösung kann ebenfalls belastbar sein, wenn FileMaker zunächst sein definiertes Export-XML erzeugt und ein kontrolliertes Stylesheet daraus UBL oder CII erstellt. Dann liegt die Struktur im Stylesheet und nicht in verstreuten Textformeln. Auch dieses Ergebnis muss anschließend gegen XSD und Schematron validiert werden.

Fazit

Eine E‑Rechnung ist keine Textdatei, die lediglich wie XML aussehen muss. Sie ist ein streng strukturiertes, semantisch definiertes und auf mehreren Ebenen maschinell prüfbares Dokument.

Die manuelle Erzeugung mit Textfunktionen muss gleichzeitig

  • die richtige Syntaxbindung,
  • die exakte Elementreihenfolge,
  • die korrekte Verschachtelung,
  • alle Namespaces,
  • kontextabhängiges Escaping,
  • unzulässige XML-Zeichen,
  • Zeilenenden und Whitespace,
  • Byte-Kodierung und BOM,
  • Datentypen und lexikalische Formate,
  • Kardinalitäten und leere Elemente,
  • Codelisten,
  • Rundungs- und Summenregeln,
  • Steueraggregation,
  • profilspezifische Geschäftsregeln und
  • eine sichere Parserkonfiguration auf der Empfangsseite

beherrschen.

Ein XML-Builder mit typisiertem Datenmodell, Serializer sowie anschließender XSD- und Schematron-Validierung trennt diese Aufgaben voneinander und macht Fehler sichtbar.

Die JSON-Erfahrung liefert dafür die einfachste Merkhilfe: Niemand baut verlässliches JSON mehr aus Klammern, Anführungszeichen und Feldinhalten zusammen. Bei XML gilt derselbe Grundsatz – nur ist die Struktur einer E‑Rechnung noch wesentlich anspruchsvoller.

Textfunktionen dürfen Rechnungswerte vorbereiten. Das E‑Rechnungs‑XML selbst sollten sie niemals zusammenbauen.

Quellen und Referenzen