Spezifikation
Dieses Dokument spezifiziert das JSON-Format, in dem das FIM Portal maschinenlesbare Übersetzungen von XDatenfelder-Regeln bereitstellt. Es ist der Vertrag für alles, was übersetzte Regeln liest – etwa die Formularvorschau des FIM Portals oder Formular-Laufzeiten Dritter.
Entstehung der Übersetzungen
Regeln in XDatenfelder (xdf2 und xdf3) sind als deutschsprachiger Freitext
hinterlegt (freitextRegel), z. B. „Das Feld 'Augenfarbe Person Freitext' ist
nur sichtbar und muss ausgefüllt werden, wenn 'Augenfarbe Person' den Wert
'98 - Sonstige' hat." Grundlage der Übersetzung ist ausschließlich dieser Freitext, es werden keine Metadaten ausgewertet.
Das FIM Portal übersetzt diese Freitexte in einem automatisierten, KI-gestützten Verfahren in das hier beschriebene Format. Der Ablauf pro regeltragendem Element (Schema, Datenfeldgruppe oder Datenfeld):
- Datenstruktur ableiten. Aus dem Element wird eine stark typisierte Beschreibung seiner Kinder erzeugt (Format siehe unten). Sie legt fest, welche Bezeichner in einer Regel überhaupt vorkommen dürfen und welchen Typ sie haben.
- Übersetzen. Ein Sprachmodell erhält die Datenstruktur, eine Zuordnung der Kind-IDs zu ihren deutschen Namen und alle Freitextregeln des Elements, jede mit der ID und Version ihrer Regel, und erzeugt daraus je Regel die Rule-Objekte. Kann eine Regel mit den verfügbaren Operationen nicht ausgedrückt werden, wird das Element als „nicht übersetzbar" markiert.
- Vollständigkeitsprüfung. Die Regel-IDs des erzeugten Dokuments müssen genau die Regeln des Elements sein: Fehlt eine Regel oder taucht eine fremde ID auf, wird die Übersetzung des gesamten Elements verworfen.
- Typprüfung. Jedes erzeugte Rule-Objekt wird gegen die Datenstruktur geprüft: Bezeichner müssen existieren, Operanden die passenden Typen haben, Bedingungen den vom Effekt geforderten Typ liefern. Schlägt die Prüfung für eine Regel fehl, wird die Übersetzung des gesamten Elements verworfen.
Nur Übersetzungen, die die Schritte 3 und 4 bestehen, werden gespeichert und ausgeliefert. Für Elemente ohne gültige Übersetzung gibt es kein Dokument.
Format der Datenstruktur (Basis der Übersetzung)
Die Datenstruktur ist ein JSON-Objekt, das die Form der Daten eines Elements
beschreibt. Jeder Knoten hat ein type-Attribut:
- Primitive Typen:
text,text_latin,date,time,datetime,bool,num,num_int,num_currency,file,objsowieenum(mit den erlaubten Codes im Attributvalues). - Komplexe Typen:
groupbzw.schema(Kinder im Attributchildren, Schlüssel = Kind-ID),array(Eintragstyp invalue, optionalmin_items/max_items) undoptional(innerer Typ invalue).
Beispiel für eine Datenfeldgruppe mit einem Pflicht-Textfeld und einem optionalen Codelistenfeld:
{
"type": "group",
"children": {
"F001": { "type": "text" },
"F002": {
"type": "optional",
"value": { "type": "enum", "values": ["01", "02", "98"] }
}
}
}
Die Datenstruktur beschreibt nur die Grundform der Daten. Regeln dürfen diese Form weiter einschränken (z. B. ein optionales Feld bedingt zur Pflicht machen), aber nie lockern.
Auslieferung
- Endpunkt:
GET /api/v0/translated-rules/{fim_id}/{fim_version}–200mit dem unten beschriebenen JSON-Dokument,404, wenn für das Element keine Übersetzung vorliegt. - Der Endpunkt gehört zur v0-API (Beta): Das Format kann sich noch inkompatibel ändern und trägt keine Versionsangabe im Dokument.
- Ein Dokument pro Element. Wer die Regeln eines ganzen Schemas benötigt, ruft das Schema selbst sowie jede Datenfeldgruppe und jedes Datenfeld ab, das Regeln definiert.
Dokument
Das Dokument ist ein JSON-Objekt mit einem Schlüssel je Regel des
Elements. Der Schlüssel ist <fim_id>:<fim_version> der Regel (bei einer
Regel ohne Version <fim_id>:), der Wert das Array der Rule-Objekte, die
diese Regel übersetzen. Das Dokument enthält genau die Regeln des Elements,
die einen Freitext haben; die Reihenfolge der Schlüssel und der Rule-Objekte
ist nicht garantiert.
Ein Freitext kann mehrere Rule-Objekte ergeben (eine Sichtbarkeitsregel mit
Pflichtanteil wird zu forbid und require). Ein leeres Array bedeutet, dass
die Regel die Datenstruktur nicht weiter einschränkt, etwa weil sie nur
verlangt, dass ohnehin verpflichtende Elemente befüllt sind.
{
"R60000012:1.0": [
{
"effect": "require",
"target": "F60000355",
"message": "Das Feld 'Augenfarbe Person Freitext' muss angegeben werden, wenn 'Augenfarbe Person' den Wert '98 - Sonstige' hat.",
"condition": { "equal": [{ "identifier": "F60000281" }, "98"] }
},
{
"effect": "forbid",
"target": "F60000355",
"message": "Das Feld 'Augenfarbe Person Freitext' darf nicht angegeben werden, wenn 'Augenfarbe Person' nicht den Wert '98 - Sonstige' hat.",
"condition": { "not_equal": [{ "identifier": "F60000281" }, "98"] }
}
],
"R60000013:1.0": []
}
Über die Regel-IDs ordnen Konsumenten jedes Rule-Objekt der Freitextregel zu, aus der es entstanden ist. Das Dokument selbst enthält keine Metadaten, keine Herkunftsangabe und keinen Freitext der Originalregel. Den Freitext liefert weiterhin das XDatenfelder-Dokument selbst; die Übersetzung ergänzt ihn, sie ersetzt ihn nicht.
Rule-Objekt
| Feld | Typ | Pflicht | Bedeutung |
|---|---|---|---|
effect | "require" | "forbid" | "value" | "valid" | ja | Was passiert, wenn die Bedingung erfüllt ist (siehe unten). |
target | Bezeichner-String (siehe Bezeichner) | require/forbid/value: ja; valid: muss fehlen | Das Kindelement, auf das der Effekt wirkt. |
condition | Expression | ja | Wird gegen die Daten des Elements ausgewertet; der geforderte Typ hängt vom effect ab. |
message | String (Deutsch) | ja | Für Nutzer lesbarer Text, der bei Verletzung der Regel angezeigt wird. |
Konsumenten müssen unbekannte zusätzliche Schlüssel ignorieren; der Erzeuger gibt derzeit keine aus.
Effekte
| Effekt | Typ der Bedingung | Semantik |
|---|---|---|
require | boolean | Ist die Bedingung true, muss target vorhanden (nicht leer) sein. target muss ein optional-Kind oder ein array-Kind mit min_items = 0 sein. |
forbid | boolean | Ist die Bedingung true, darf target nicht vorhanden sein. Gleiche Einschränkungen für das Ziel wie bei require. Formular-Laufzeiten blenden verbotene Elemente aus – so werden Sichtbarkeitsregeln („nur sichtbar, wenn …") ausgedrückt. |
value | Typ von target | Setzt target auf das Ergebnis der Bedingung. Reserviert: Die Übersetzung ist angewiesen, diesen Effekt nicht zu erzeugen, und die aktuelle Laufzeit unterstützt ihn nicht. Konsumenten dürfen ihn ablehnen oder ignorieren. |
valid | boolean | Die Bedingung muss true sein, sonst ist das Element als Ganzes ungültig. Kein target. |
Mehrere require- bzw. forbid-Regeln auf dasselbe Ziel sind zulässig: Das
Ziel ist erforderlich bzw. verboten, sobald die Bedingung mindestens einer
dieser Regeln erfüllt ist. Ist ein Ziel danach gleichzeitig erforderlich und
verboten, widersprechen sich die Regeln; die Referenzlaufzeit bricht die
Auswertung des Dokuments dann mit einem Fehler ab.
Eine Regel lockert niemals eine Einschränkung, die die Datenstruktur selbst definiert.
Bezeichner und Geltungsbereich
Alle Bezeichner eines Dokuments werden relativ zu dem Element aufgelöst, zu dem das Dokument gehört (sein Geltungsbereich): das Schema bei einer Schema-Übersetzung, die Gruppe bei einer Datenfeldgruppen-Übersetzung, das Feld bei einer Datenfeld-Übersetzung. Übersetzungen sind damit pro Element in sich geschlossen und wissen nichts über das Schema, in das sie eingebettet sind.
- Ein Bezeichner ist ein durch Punkte getrennter Pfad aus Kind-IDs, z. B.
"F001","G60000012.F60000231". Kind-IDs sind die FIM-IDs ohne Version (F…für Datenfelder,G…für Datenfeldgruppen). - Das Präfix
selfbezeichnet das Element des Geltungsbereichs und ist vor einem Pfad optional:"self.F001"≡"F001". Ein bloßes"self"verweist auf den Wert des Elements selbst (nur bei einem Datenfeld als Geltungsbereich sinnvoll). - Gespeicherte Dokumente enthalten keine numerischen (Array-Index-)Pfadsegmente;
Array-Einträge werden über
mapadressiert. - Innerhalb von
mapwerden Bezeichner relativ zum aktuellen Array-Eintrag aufgelöst. - Ein
targetist ein Bezeichner-String; innerhalb vonconditionist ein Bezeichner das Objekt{ "identifier": "<pfad>" }.
Ausdrucksgrammatik
Ausdrücke sind JSON-Werte. Jede Operation ist ein Objekt mit genau einem Schlüssel, der die Operation benennt. Operandentypen werden gegen die Datenstruktur des Elements geprüft (siehe Format der Datenstruktur oben) – ein ausgeliefertes Dokument hat diese Prüfung bestanden.
type Expression = Literal | Identifier | Operation;
// Literals. Bare JSON string/boolean/number are text/bool/num;
// the wrapped forms carry the more specific XDF data types.
// text_latin, file and obj values are compared as plain text.
// date, time and datetime are reserved: the runtime supports
// them, but the translation does not emit them yet (see below).
type Literal =
| string
| boolean
| number
| { num_int: number }
| { num_currency: number }
| { date: string } // ISO 8601 date, e.g. "2024-01-31"
| { time: string } // ISO 8601 time
| { datetime: string }; // ISO 8601 date-time
type Identifier = { identifier: string };
type Operation =
| LogicalOperation
| ArithmeticOperation
| ComparisonOperation
| StringOperation
| OptionalOperation
| ArrayOperation
| DateOperation
| DomainSpecificOperation;
type LogicalOperation =
| { and: Expression[] }
| { or: Expression[] }
| { xor: Expression[] }
| { not: Expression };
type ArithmeticOperation =
| { add: Expression[] }
| { subtract: [Expression, Expression] }
| { multiply: Expression[] }
| { divide: [Expression, Expression] };
// Ordered comparisons work on two numbers or two values of the same
// date/time type (e.g. a date field against `today`).
type ComparisonOperation =
| { equal: [Expression, Expression] }
| { not_equal: [Expression, Expression] }
| { less_than: [Expression, Expression] }
| { greater_than: [Expression, Expression] }
| { less_equal: [Expression, Expression] }
| { greater_equal: [Expression, Expression] };
type StringOperation =
| { contains: [Expression, Expression] } // haystack, needle
| { starts_with: [Expression, Expression] }
| { ends_with: [Expression, Expression] }
| { string_length: Expression } // number of characters
| { matches_pattern: [Expression, Expression] }; // value, ECMAScript regex literal
type OptionalOperation =
| { is_empty: Identifier }; // operand must be `optional`
type ArrayOperation =
| { length: Identifier } // number of items
| { map: { array: Expression; expression: Expression } } // per-item, item-scoped identifiers
| { any: Expression } // boolean array → true if any item true
| { all: Expression }; // boolean array → true if all items true
type DateOperation =
| { today: {} } // current date
| { age_in_years: Expression }; // full years between a date and today
type DomainSpecificOperation =
| { is_valid_iban: Expression }
| { is_valid_bic: Expression }
| { is_valid_email: Expression }
| { is_valid_date: [Expression, Expression, Expression] }; // day, month, year integers
Erläuterung der Grammatik
Ein Ausdruck (Expression) ist entweder ein Literal, ein Bezeichner oder eine
Operation. Operationen können beliebig geschachtelt werden; jeder Operand ist
selbst wieder ein Ausdruck. Die Grammatik ist bewusst nicht Turing-vollständig:
Sie enthält nur einfache logische, arithmetische, Vergleichs- und
Zeichenkettenoperationen sowie wenige fachliche Prüfungen.
Jeder Abschnitt zeigt vollständige Rule-Objekte, so wie sie in einem
ausgelieferten Dokument stehen. Die Feld-IDs sind fiktiv; angenommen werden
ein optionales Textfeld F001, ein Zahlenfeld F002, ein Codelistenfeld
F003, ein Datumsfeld F004, ein boolesches Feld F005, ein weiteres
optionales Textfeld F006, ein weiteres Zahlenfeld F007, drei
Ganzzahlfelder F020–F022 (Tag, Monat, Jahr), eine wiederholbare Gruppe
G001 mit einem Zahlenfeld F010 und einem optionalen Feld F011 sowie eine
nicht wiederholbare Untergruppe G002 mit einem optionalen Textfeld F030.
Literale
Ein nacktes JSON-string, boolean oder number steht für die
XDF-Datentypen Text, Wahrheitswert bzw. Zahl. Die umschlossenen Formen tragen
den genaueren Datentyp: num_int (Ganzzahl), num_currency (Geldbetrag),
date, time, datetime (jeweils als ISO-8601-String). Werte der Typen
text_latin, file und obj haben keine eigene Literalform und werden als
Text verglichen.
Die Formen date, time und datetime sind reserviert und beschreiben
das Zielbild: Die Referenzlaufzeit unterstützt sie bereits, die Übersetzung
erzeugt sie derzeit jedoch nicht – Regeln, die ein solches Literal benötigen,
gelten heute als nicht übersetzbar und kommen in ausgelieferten Dokumenten
nicht vor. Datumsregeln ohne festes Stichtags-Literal sind über today und
age_in_years ausdrückbar (siehe Datumsoperationen).
„Der Betrag (F002) darf 500,00 € nicht überschreiten."
{
"effect": "valid",
"message": "Der Betrag darf 500,00 € nicht überschreiten.",
"condition": { "less_equal": [{ "identifier": "F002" }, { "num_currency": 500.0 }] }
}
„Das Datum (F004) muss nach dem 31.12.2023 liegen." – Zielbild: nutzt das
reservierte date-Literal.
{
"effect": "valid",
"message": "Das Datum muss nach dem 31.12.2023 liegen.",
"condition": { "greater_than": [{ "identifier": "F004" }, { "date": "2023-12-31" }] }
}
Bezeichner
Bezeichner (Identifier) verweisen auf den Wert eines Elements der
Datenstruktur, siehe Abschnitt Bezeichner und Geltungsbereich. self. ist
optional; verschachtelte Kinder werden über den Punktpfad angesprochen.
„Wenn 'Zustimmung' (F005) angekreuzt ist, muss 'Kontakt-E-Mail' (F001) angegeben werden."
{
"effect": "require",
"target": "F001",
"message": "Die Kontakt-E-Mail muss angegeben werden, wenn die Zustimmung erteilt wurde.",
"condition": { "identifier": "F005" }
}
„Wenn in der Meldeadresse (G002) ein Ortsteil (F030) angegeben ist, muss die
Bemerkung (F006) angegeben werden." – der Punktpfad adressiert das Feld in der
Untergruppe; das führende self. ist optional.
{
"effect": "require",
"target": "F006",
"message": "Die Bemerkung muss angegeben werden, wenn ein Ortsteil eingetragen ist.",
"condition": { "not": { "is_empty": { "identifier": "self.G002.F030" } } }
}
Einträge einer wiederholbaren Gruppe sind nicht einzeln adressierbar; Regeln
darüber nutzen map (siehe Array-Operationen).
Logische Operationen
and, or, xor und not verknüpfen boolesche Ausdrücke. and, or und
xor nehmen eine Liste beliebiger Länge, not genau einen Operanden.
„Wenn 'Rechtsform' (F003) den Wert '01' oder '02' hat, muss 'Handelsregisternummer' (F001) angegeben werden."
{
"effect": "require",
"target": "F001",
"message": "Die Handelsregisternummer muss angegeben werden, wenn die Rechtsform 'GmbH' oder 'AG' ist.",
"condition": {
"or": [
{ "equal": [{ "identifier": "F003" }, "01"] },
{ "equal": [{ "identifier": "F003" }, "02"] }
]
}
}
„Es muss entweder eine IBAN (F001) oder eine Kontonummer (F006) angegeben werden, nicht beides."
{
"effect": "valid",
"message": "Es muss entweder eine IBAN oder eine Kontonummer angegeben werden.",
"condition": {
"xor": [
{ "not": { "is_empty": { "identifier": "F001" } } },
{ "not": { "is_empty": { "identifier": "F006" } } }
]
}
}
„Wenn 'Zustimmung' (F005) nicht erteilt ist und 'Rechtsform' (F003) den Wert '01' hat, darf 'Bemerkung' (F006) nicht angegeben werden."
{
"effect": "forbid",
"target": "F006",
"message": "Die Bemerkung darf ohne Zustimmung bei Rechtsform 'GmbH' nicht angegeben werden.",
"condition": {
"and": [
{ "not": { "identifier": "F005" } },
{ "equal": [{ "identifier": "F003" }, "01"] }
]
}
}
Arithmetische Operationen
add und multiply rechnen über eine Liste, subtract und divide über
genau zwei Operanden. Das Ergebnis ist eine Zahl und wird üblicherweise in
einem Vergleich verwendet.
„Der Gesamtbetrag (F002) zuzüglich 19 % Umsatzsteuer darf 1.000 nicht überschreiten."
{
"effect": "valid",
"message": "Der Gesamtbetrag inklusive Umsatzsteuer darf 1.000 nicht überschreiten.",
"condition": {
"less_equal": [
{ "multiply": [{ "identifier": "F002" }, 1.19] },
1000
]
}
}
„Die Differenz aus 'Anzahl Plätze' (F002) und 'Anzahl belegte Plätze' (F007) darf nicht negativ sein."
{
"effect": "valid",
"message": "Es dürfen nicht mehr Plätze belegt sein als vorhanden.",
"condition": {
"greater_equal": [
{ "subtract": [{ "identifier": "F002" }, { "identifier": "F007" }] },
0
]
}
}
Vergleichsoperationen
Vergleiche nehmen genau zwei Operanden und liefern einen Wahrheitswert.
equal und not_equal funktionieren für alle Typen; die geordneten
Vergleiche (less_than, greater_than, less_equal, greater_equal) für
zwei Zahlen oder zwei Werte desselben Datums-/Zeittyps (z. B. ein Datumsfeld
gegen today). Codelistenwerte werden gegen ihren Code verglichen.
„Das Feld 'Sonstige Staatsangehörigkeit' (F001) ist nur sichtbar und muss ausgefüllt werden, wenn 'Staatsangehörigkeit' (F003) den Wert '99 - Sonstige' hat." – ergibt zwei Regeln auf dasselbe Ziel:
[
{
"effect": "require",
"target": "F001",
"message": "Die sonstige Staatsangehörigkeit muss angegeben werden, wenn 'Sonstige' ausgewählt ist.",
"condition": { "equal": [{ "identifier": "F003" }, "99"] }
},
{
"effect": "forbid",
"target": "F001",
"message": "Die sonstige Staatsangehörigkeit darf nur angegeben werden, wenn 'Sonstige' ausgewählt ist.",
"condition": { "not_equal": [{ "identifier": "F003" }, "99"] }
}
]
„Die Anzahl (F002) muss größer als 0 und kleiner als 100 sein."
{
"effect": "valid",
"message": "Die Anzahl muss zwischen 1 und 99 liegen.",
"condition": {
"and": [
{ "greater_than": [{ "identifier": "F002" }, 0] },
{ "less_than": [{ "identifier": "F002" }, 100] }
]
}
}
Zeichenkettenoperationen
contains, starts_with und ends_with prüfen, ob der erste Text den zweiten
enthält bzw. mit ihm beginnt oder endet. string_length liefert die
Zeichenzahl eines Textes. matches_pattern prüft einen Text gegen einen
regulären Ausdruck; das Muster ist ein String-Literal in ECMAScript-Syntax.
„Wenn 'Postleitzahl' (F001) angegeben ist, muss sie fünfstellig und numerisch sein."
{
"effect": "valid",
"message": "Die Postleitzahl muss aus genau fünf Ziffern bestehen.",
"condition": {
"or": [
{ "is_empty": { "identifier": "F001" } },
{ "matches_pattern": [{ "identifier": "F001" }, "^[0-9]{5}$"] }
]
}
}
„Das Aktenzeichen (F006) muss mit 'AZ-' beginnen und darf höchstens 12 Zeichen lang sein."
{
"effect": "valid",
"message": "Das Aktenzeichen muss mit 'AZ-' beginnen und darf höchstens 12 Zeichen lang sein.",
"condition": {
"and": [
{ "starts_with": [{ "identifier": "F006" }, "AZ-"] },
{ "less_equal": [{ "string_length": { "identifier": "F006" } }, 12] }
]
}
}
„Wenn der Firmenname (F001) 'GmbH' enthält, muss die Handelsregisternummer (F006) angegeben werden."
{
"effect": "require",
"target": "F006",
"message": "Für eine GmbH muss die Handelsregisternummer angegeben werden.",
"condition": { "contains": [{ "identifier": "F001" }, "GmbH"] }
}
Optional-Operation
is_empty liefert true, wenn ein optionales Element nicht befüllt ist. Der
Operand ist immer ein Bezeichner auf ein Element vom Typ optional. Eine
„befüllt"-Prüfung ist { "not": { "is_empty": … } }.
„Wenn 'Abweichende Lieferadresse' (F006) befüllt ist, muss 'Empfänger' (F001) angegeben werden."
{
"effect": "require",
"target": "F001",
"message": "Der Empfänger muss angegeben werden, wenn eine abweichende Lieferadresse eingetragen ist.",
"condition": { "not": { "is_empty": { "identifier": "F006" } } }
}
„Wenn keine Kontonummer (F006) angegeben ist, muss die IBAN (F001) angegeben werden."
{
"effect": "require",
"target": "F001",
"message": "Die IBAN muss angegeben werden, wenn keine Kontonummer eingetragen ist.",
"condition": { "is_empty": { "identifier": "F006" } }
}
Array-Operationen
length liefert die Anzahl der Einträge eines Arrays. map wendet einen
Ausdruck auf jeden Eintrag an; Bezeichner innerhalb von expression werden
relativ zum jeweiligen Eintrag aufgelöst. any bzw. all liefern true,
wenn mindestens ein bzw. jeder Eintrag eines booleschen Arrays true ist –
zusammen mit map drücken sie Regeln wie „mindestens eine der Angaben muss …"
oder „alle Einträge müssen …" aus.
„Es müssen zwischen einem und drei Kinder (G001) angegeben werden, und für jedes Kind muss der Kindergeldbetrag (F010) größer als 0 sein."
{
"effect": "valid",
"message": "Es müssen ein bis drei Kinder angegeben werden, jeweils mit einem Kindergeldbetrag gr ößer als 0.",
"condition": {
"and": [
{ "greater_equal": [{ "length": { "identifier": "G001" } }, 1] },
{ "less_equal": [{ "length": { "identifier": "G001" } }, 3] },
{
"all": {
"map": {
"array": { "identifier": "G001" },
"expression": { "greater_than": [{ "identifier": "F010" }, 0] }
}
}
}
]
}
}
„Mindestens bei einem Kind (G001) muss die Schule (F011) angegeben sein."
{
"effect": "valid",
"message": "Mindestens bei einem Kind muss die Schule angegeben sein.",
"condition": {
"any": {
"map": {
"array": { "identifier": "G001" },
"expression": { "not": { "is_empty": { "identifier": "F011" } } }
}
}
}
}
„Die Anzahl der Anschriften (F002) muss der Zahl der eingetragenen Anschriften (G001) entsprechen."
{
"effect": "valid",
"message": "Die angegebene Anzahl der Anschriften muss mit den eingetragenen Anschriften übereinstimmen.",
"condition": {
"equal": [
{ "identifier": "F002" },
{ "length": { "identifier": "G001" } }
]
}
}
„Wenn mehr als eine Anschrift (G001) eingetragen ist, muss die Hauptanschrift (F001) benannt werden."
{
"effect": "require",
"target": "F001",
"message": "Bei mehreren Anschriften muss die Hauptanschrift benannt werden.",
"condition": { "greater_than": [{ "length": { "identifier": "G001" } }, 1] }
}
Datumsoperationen
today (ohne Operanden, geschrieben als {}) liefert das aktuelle Datum.
age_in_years liefert die vollen Jahre zwischen einem Datum und heute.
„Das Geburtsdatum (F004) darf nicht in der Zukunft liegen, und die Person muss volljährig sein."
{
"effect": "valid",
"message": "Das Geburtsdatum darf nicht in der Zukunft liegen und die Person muss mindestens 18 Jahre alt sein.",
"condition": {
"and": [
{ "less_equal": [{ "identifier": "F004" }, { "today": {} }] },
{ "greater_equal": [{ "age_in_years": { "identifier": "F004" } }, 18] }
]
}
}
„Ist die Person (Geburtsdatum F004) jünger als 16 Jahre, muss der gesetzliche Vertreter (F001) angegeben werden."
{
"effect": "require",
"target": "F001",
"message": "Für Personen unter 16 Jahren muss der gesetzliche Vertreter angegeben werden.",
"condition": { "less_than": [{ "age_in_years": { "identifier": "F004" } }, 16] }
}
Fachliche Prüfungen
is_valid_iban, is_valid_bic und is_valid_email prüfen einen Text auf
eine gültige IBAN, BIC bzw. E-Mail-Adresse. is_valid_date erwartet drei
Ganzzahlen (Tag, Monat, Jahr) und prüft, ob sie ein gültiges Datum bilden.
„Die IBAN (F001) muss, sofern angegeben, gültig sein."
{
"effect": "valid",
"message": "Die IBAN ist ungültig.",
"condition": {
"or": [
{ "is_empty": { "identifier": "F001" } },
{ "is_valid_iban": { "identifier": "F001" } }
]
}
}
„Die E-Mail-Adresse (F006) muss gültig sein; ist sie angegeben, muss auch die BIC (F001) angegeben und gültig sein."
[
{
"effect": "valid",
"message": "Die E-Mail-Adresse ist ungültig.",
"condition": {
"or": [
{ "is_empty": { "identifier": "F006" } },
{ "is_valid_email": { "identifier": "F006" } }
]
}
},
{
"effect": "require",
"target": "F001",
"message": "Die BIC muss angegeben werden, wenn eine E-Mail-Adresse eingetragen ist.",
"condition": { "not": { "is_empty": { "identifier": "F006" } } }
},
{
"effect": "valid",
"message": "Die BIC ist ungültig.",
"condition": {
"or": [
{ "is_empty": { "identifier": "F001" } },
{ "is_valid_bic": { "identifier": "F001" } }
]
}
}
]
„Tag (F020), Monat (F021) und Jahr (F022) müssen zusammen ein gültiges Datum ergeben."
{
"effect": "valid",
"message": "Tag, Monat und Jahr ergeben kein gültiges Datum.",
"condition": {
"is_valid_date": [
{ "identifier": "F020" },
{ "identifier": "F021" },
{ "identifier": "F022" }
]
}
}
Leere Werte
Ein optionales Element, das nicht befüllt ist, hat den Wert leer. Die Referenzlaufzeit definiert das Verhalten aller Operationen für leere Werte; andere Interpreter müssen sich identisch verhalten:
is_emptyliefert genau danntrue, wenn der Wert leer ist.equalbehandelt leer wie einen eigenen Wert: Ist genau ein Operand leer, ist das Ergebnisfalse; sind beide leer,true.not_equalist die Negation –{ "not_equal": [{ "identifier": "F003" }, "99"] }ist also auch danntrue, wennF003leer ist.- Geordnete Vergleiche (
less_than,greater_than,less_equal,greater_equal) liefernfalse, sobald ein Operand leer ist. - Die Zeichenkettenprädikate
contains,starts_with,ends_withundmatches_patternliefernfalse, sobald ein Operand leer ist. string_lengthundage_in_yearsreichen leer weiter: Ihr Ergebnis ist dann selbst leer, ein umgebender Vergleich damitfalse.- Bei
anyundallzählt ein leerer Eintrag des booleschen Arrays alsfalse. is_valid_iban,is_valid_bicundis_valid_emailliefernfalsefür leere Werte;is_valid_dateliefertfalse, sobald einer der drei Operanden leer oder keine Ganzzahl ist.- Logische (
and,or,xor,not) und arithmetische Operationen erwarten befüllte Operanden; dasselbe gilt für einen leeren Wert direkt an einer Stelle, die einen Wahrheitswert erfordert. Das ist ein Auswertungsfehler: Die Referenzlaufzeit verwirft dann die Auswertung des gesamten Dokuments und meldet einen allgemeinen Validierungsfehler.
Typregeln, auf die sich Konsumenten verlassen können
- Bedingungen von
require/forbid/validsind boolesche Ausdrücke. is_emptywird nur aufoptional-Kinder angewendet; eine „befüllt"-Prüfung ist{ "not": { "is_empty": … } }. Ein bloßer Bezeichner wird nur dann als boolescher Wert verwendet, wenn das Kind vom Typboolist.- Felder mit Codeliste (Enum) werden gegen ihren Code als String-Literal
verglichen (
"98"), nie gegen die Anzeigebezeichnung. - Vergleiche zwischen unpassenden Typen (z. B. Text mit Zahl) kommen in gespeicherten Dokumenten nicht vor; der Typprüfer weist sie zurück.
Referenzimplementierungen
- Laufzeit (TypeScript, maßgeblich für die Auswertungssemantik):
frontend_apps/xdf-formular/rule-interpreter.ts(TypenRule,Expressionund der Auswerter). - Typprüfer (Python):
fimportal/regelwerk/type_checker.py. - Parsen/Serialisieren eines Rule-Objekts (Python):
Rule.from_dict/Rule.to_dictinfimportal/regelwerk/interpreter.py. - API-Tests:
tests/test_api_get_translated_rules.py.