Zum Inhalt

Strukturierte Daten und Regel-Pipelines

Mit Kompositionselementen formst du verschachtelte Ausgaben; Regelelemente transformieren Quelldatensätze in klar getrennten Phasen. Das sind unterschiedliche Aufgaben: nestedKey, list und array definieren die Produktstruktur. sourceConstraints, mapping und targetConstraints legen fest, ob ein geladener Datensatz zugelassen und wie er transformiert wird.

Verschachtelte Objekte, Listen und Arrays erzeugen

nestedKey type="dict" erzeugt ein verschachteltes Objekt. Eine list wertet jedes deklarierte item aus; eine falsche Item-Bedingung erzeugt einen leeren Platz. Verwende deshalb RemoveNoneOrEmptyElement, wenn die exportierte Liste nur passende Einträge enthalten soll. Ein array type="literal" übernimmt seine deklarierten value-Konstanten in derselben Reihenfolge. Verwende condition, wenn sich Zweige der Ausgabestruktur gegenseitig ausschließen.

structured-profile.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<setup rngSeed="42" numProcess="1">
    <generate name="profiles" count="3" target="LogExporter">
        <key name="id" generator="IncrementGenerator"/>
        <nestedKey name="address" type="dict">
            <key name="city" constant="Berlin"/>
            <key name="postal_code" constant="10115"/>
        </nestedKey>
        <list name="contacts" converter="RemoveNoneOrEmptyElement">
            <item>
                <key name="channel" constant="email"/>
                <key name="value" script="'customer-' + str(id) + '@example.test'"/>
            </item>
            <item condition="id % 2 == 0">
                <key name="channel" constant="sms"/>
                <key name="value" script="'+49-30-' + str(100000 + id)"/>
            </item>
        </list>
        <array name="roles" type="literal">
            <value constant="customer"/>
            <value constant="newsletter"/>
        </array>
        <condition>
            <if condition="id == 1">
                <key name="segment" constant="new"/>
            </if>
            <else-if condition="id == 2">
                <key name="segment" constant="active"/>
            </else-if>
            <else>
                <key name="segment" constant="loyal"/>
            </else>
        </condition>
    </generate>
</setup>

Dieser Descriptor erzeugt drei Profile. Jedes Profil besitzt eine Adresse, zwei Rollen und einen E-Mail-Kontakt; nur Profil 2 erhält zusätzlich einen SMS-Kontakt. Genau ein Bedingungszweig liefert segment.

Verwende nestedKey type="list", wenn alle Einträge dieselbe generierte Struktur und Anzahl haben. list/item ist für Einträge mit unterschiedlichen Strukturen oder individuellen Bedingungen gedacht. array eignet sich für eine homogene Sammlung skalarer Werte.

Quelldatensätze filtern, abbilden und validieren

Die Regel-Pipeline besitzt eine feste Reihenfolge:

  1. sourceConstraints sieht den geladenen Quelldatensatz und kann ihn verwerfen, bevor generierte Keys und Mappings ausgeführt werden.
  2. mapping wird in Dokumentreihenfolge ausgewertet und kann Felder ergänzen oder überschreiben. Spätere Mappings können Werte früherer Mappings verwenden.
  3. targetConstraints sieht den fertigen generierten Datensatz und kann ihn vor dem Export verwerfen.

Die folgenden beiden Dateien bilden gemeinsam ein ausführbares Projekt.

data/customers.json
1
2
3
4
5
6
[
  {"customer_id": "C-100", "active": true,  "income": 120000, "credit_score": 780},
  {"customer_id": "C-200", "active": false, "income": 90000,  "credit_score": 760},
  {"customer_id": "C-300", "active": true,  "income": 60000,  "credit_score": 700},
  {"customer_id": "C-400", "active": true,  "income": 18000,  "credit_score": 650}
]
rule-pipeline.xml
1
2
3
4
5
6
7
8
9
<setup numProcess="1">
    <generate name="eligible_customers" count="4" source="data/customers.json" target="LogExporter">
        <sourceConstraints if="active == False" require="False"/>
        <mapping if="income >= 100000 and credit_score >= 750" set="segment = 'premium'"/>
        <mapping if="not (income >= 100000 and credit_score >= 750)" set="segment = 'standard'"/>
        <mapping if="True" set="monthly_budget = income / 12"/>
        <targetConstraints if="30000 > income" require="False"/>
    </generate>
</setup>

C-200 wird in der Quellphase entfernt, weil der Datensatz inaktiv ist. Die Mappings klassifizieren die verbleibenden Datensätze und berechnen monthly_budget. C-400 wird in der Zielphase entfernt, weil das Einkommen unter der abschließenden Eignungsgrenze liegt. Exportiert werden daher exakt C-100 und C-300.

Filter gehören in Constraints, Transformationen in Mappings. Ein Constraint mit require="False" ist eine beabsichtigte Ablehnung; Auswertungsfehler werden über den zentralen DSL-Fehlerkatalog gemeldet und nicht wie ein abgelehnter Datensatz behandelt.