Zum Inhalt springen

XML-Nachrichten nach Knotentyp in Jitterbit Studio weiterleiten

Einführung

Wenn ein einzelner Endpunkt mehrere Arten von XML-Nachrichten bereitstellt, muss die Integration die Payload vor der Verarbeitung inspizieren. Ein freigegebener Inbound-Ordner, eine Warteschlange oder ein API-Endpunkt können sowohl eine Datensatzaktualisierung als auch eine Datensatzstornierung enthalten, die jeweils ein anderes Element unter demselben Root verwenden. Der Vorgang, der Aktualisierungen verarbeitet, kann eine Stornierungsnachricht nicht verarbeiten. Die Payload muss daher untersucht und an den entsprechenden Vorgang weitergeleitet werden.

Um zu prüfen, ob ein bestimmter Knoten vorhanden ist, führe eine XPath-Abfrage gegen die Payload mit SelectNodes aus und zähle die Ergebnisse mit Length. Ein leeres Ergebnis bedeutet, dass der Knoten fehlt. Dann verzweigst du dich basierend auf diesem Ergebnis und rufst den entsprechenden Vorgang mit RunOperation auf.

In dieser Anleitung wird eine Bestellnachricht als Beispiel verwendet. Beide Nachrichtentypen teilen das gleiche Root- und Body-Element und unterscheiden sich nur im Element direkt unter dem Body:

Create or update message        Cancellation message

Envelope                        Envelope
└── Body                        └── Body
    └── OrderUpdate                 └── OrderCancellation
        └── Order                       └── Order

Diese Struktur ist typisch für Interchange-Style-Envelopes, bei denen ein gemeinsames Root-Element entweder einen Datensatz oder ein Ereignis zu diesem Datensatz trägt. Das Ziel ist es, zu erkennen, welches von OrderUpdate oder OrderCancellation vorhanden ist, und dann entweder den Vorgang Insert or Update Order oder den Vorgang Cancel Order aufzurufen.

Hinweis

GetNodeName ist dafür konzipiert, den Namen eines Knotens zu lesen, der bereits von einer Funktion wie SelectSingleNode zurückgegeben wurde, nicht um die Präsenz eines Knotens direkt zu prüfen. Um es für das Routing zu verwenden, kombiniere es mit SelectSingleNode, wie in Option B unten gezeigt.

Designmuster

Die Routing-Logik befindet sich in einem Skriptschritt in einer eigenen Router-Operation, upstream der Operationen, die die Arbeit leisten:

%%{init: {"themeVariables": {"lineColor": "#6b7280", "edgeLabelBackground": "#f5f5f5"}}}%% flowchart LR A[Source activity
reads XML] --> B[Script step
router] B --> C[Insert or
Update Order] B --> D[Cancel Order]

Die Router-Operation liest die Payload, bestimmt den Nachrichtentyp und ruft genau einen downstream-Vorgang auf. Jeder downstream-Vorgang bleibt einfach, da er nur den Nachrichtentyp erhält, für den er entwickelt wurde.

Schritt 1: Payload in eine Variable einlesen

Das Router-Skript benötigt das reine XML als String. Wie man es erhält, hängt davon ab, wie die Nachricht ankommt.

  • Aus einer Datei- oder Speicheraktivität: Verwende ReadFile mit einem Referenzpfad zur Read-Aktivität:

    $gv_payload = ReadFile("<TAG>activity:tempstorage/Inbound XML/tempstorage_read/Read Message</TAG>");
    
  • Aus einer API Manager API: Verwende die Variable jitterbit.api.request.body, die die übermittelte Payload enthält:

    $gv_payload = $jitterbit.api.request.body;
    

Die Zuweisung der Payload zu einer globalen Variable dient auch einem zweiten Zweck. Operationen, die mit RunOperation aufgerufen werden, erben alle globalen Variablen, sodass die downstream-Operation die Nachricht von $gv_payload lesen kann, ohne die Quelle ein zweites Mal zu lesen.

Schritt 2: Namespace identifizieren

Die meisten XML-Daten aus Business-Anwendungen sind mit einem Namespace versehen. Wenn das Root-Element der Payload ein xmlns-Attribut trägt, gehört jedes Element im Dokument zu diesem Namespace, und eine XPath-Abfrage, die ihn auslässt, findet nichts.

Öffne eine Sample-Payload und schaue dir das Root-Element an:

<Envelope xmlns="http://example.com/schemas/orders">

Kopiere den Namespace-URI. Im nächsten Schritt deklarierst du ein Präfix dafür und verwendest dieses Präfix bei jedem Element in der XPath-Abfrage. SelectNodes akzeptiert diese Deklarationen als zusätzliche String-Argumente in der Form prefix=uri.

Wenn das Root-Element kein xmlns-Attribut hat, ist das Dokument nicht mit einem Namespace versehen. Lasse die Präfix-Deklarationen und die Präfixe bei den Elementnamen weg.

Schritt 3: Prüfen, ob der Knoten existiert

SelectNodes gibt ein Array aller Knoten zurück, die die Abfrage erfüllen. Wenn nichts passt, ist das Array leer, sodass Length den Wert 0 zurückgibt. Diesen Wert mit null zu vergleichen ist der Existenztest:

$gv_updateNodes = SelectNodes($gv_payload,
    "/o:Envelope/o:Body/o:OrderUpdate",
    "o=http://example.com/schemas/orders");

// True when the payload contains a create or update message
Length($gv_updateNodes) > 0;

Ersetzen Sie den Namespace-URI durch den aus Schritt 2 und die Elementnamen durch die in Ihrer eigenen Nutzlast.

Tipp

Abfragen Sie den spezifischen Pfad statt nur des Elementnamens. Ein Pfad mit Verankerung am Root bestätigt sowohl, dass das Element vorhanden ist, als auch dass es dort erscheint, wo das Schema es erwartet. Dies vermeidet eine falsche Übereinstimmung mit einem gleichnamigen Element an anderer Stelle im Dokument.

Schritt 4: Zur passenden Operation weiterleiten

Wählen Sie einen der beiden folgenden Ansätze. Option A ist die direktere Wahl für zwei Nachrichtentypen. Option B ist besser geeignet für drei oder mehr, da nur eine XPath-Abfrage unabhängig von der Anzahl der Nachrichtentypen am Endpunkt ausgeführt wird.

Option A: Jeden Nachrichtentyp nacheinander prüfen

Fügen Sie einen Script-Schritt zur Router-Operation hinzu. Das Script prüft zunächst auf die Update-Nachricht und fällt auf die Stornierungsnachricht zurück.

Ersetzen Sie den Aktivitätsverweis-Pfad, den Namespace-URI, die Elementnamen und die beiden Operation-Verweis-Pfade durch die Werte aus Ihrem eigenen Projekt.

// Read the inbound message
$gv_payload = ReadFile("<TAG>activity:tempstorage/Inbound XML/tempstorage_read/Read Message</TAG>");

// Look for the create or update message: Envelope/Body/OrderUpdate
$gv_updateNodes = SelectNodes($gv_payload,
    "/o:Envelope/o:Body/o:OrderUpdate",
    "o=http://example.com/schemas/orders");

// Look for the cancellation message: Envelope/Body/OrderCancellation
$gv_cancelNodes = SelectNodes($gv_payload,
    "/o:Envelope/o:Body/o:OrderCancellation",
    "o=http://example.com/schemas/orders");

If(Length($gv_updateNodes) > 0,
    WriteToOperationLog("Routing to insert or update.");
    If(!RunOperation("<TAG>operation:Insert or Update Order</TAG>"),
        RaiseError(GetLastError())
    );
,
    If(Length($gv_cancelNodes) > 0,
        WriteToOperationLog("Routing to cancellation.");
        If(!RunOperation("<TAG>operation:Cancel Order</TAG>"),
            RaiseError(GetLastError())
        );
    ,
        RaiseError("Unrecognized message type: no OrderUpdate or OrderCancellation node found.")
    );
);

Option B: Den Nachrichtentyp aus dem Body lesen

Da beide Nachrichtentypen an derselben Position im Dokument stehen, können Sie jedes Element unter dem Body auswählen und seinen Namen lesen, dann auf diesem einzelnen Wert verzweigen. SelectSingleNode gibt den ersten passenden Knoten zurück, und GetNodeName gibt seinen Namen zurück. Das * in der XPath-Abfrage passt auf ein Element mit beliebigem Namen, sodass die Abfrage die Nachrichtentypen nicht im Voraus kennen muss.

Dieser Ansatz speichert auch den Nachrichtentyp bei jedem Durchlauf im Operation Log, was hilfreich ist, wenn die Nutzlasten des Endpunkts nicht vollständig dokumentiert sind.

// Read the inbound message
$gv_payload = ReadFile("<TAG>activity:tempstorage/Inbound XML/tempstorage_read/Read Message</TAG>");

// Select whatever element sits directly under Body, then read its name
$gv_bodyChild = SelectSingleNode($gv_payload,
    "/o:Envelope/o:Body/*",
    "o=http://example.com/schemas/orders");
$gv_messageType = GetNodeName($gv_bodyChild);

WriteToOperationLog("Message type: " + $gv_messageType);

// Route on the message type. Add a branch for each additional type,
// and raise an error on anything unrecognized.
If($gv_messageType == "OrderUpdate",
    If(!RunOperation("<TAG>operation:Insert or Update Order</TAG>"),
        RaiseError(GetLastError())
    );
,
    If($gv_messageType == "OrderCancellation",
        If(!RunOperation("<TAG>operation:Cancel Order</TAG>"),
            RaiseError(GetLastError())
        );
    ,
        RaiseError("Unrecognized message type: " + $gv_messageType)
    );
);

Eine Nutzlast, deren Body-Element keinem der Zweige entspricht, geht durch zur finalen RaiseError, was auch den Fall abdeckt, wenn das Body-Element kein untergeordnetes Element hat.

Wichtige Punkte zu beiden Scripts:

  • RunOperation gibt false zurück, wenn die aufgerufene Operation fehlschlägt. Das Testen dieses Rückgabewerts und das Aufrufen von RaiseError mit GetLastError stoppt die Router-Operation und löst ihre konfigurierte On Fail-Aktion aus. Ohne diesen Test kann ein Fehler in der nachgelagerten Operation den Router als erfolgreich melden.
  • Das Auslösen eines Fehlers bei einem unbekannten Nachrichtentyp macht Schemaänderungen und fehlerhafte Nutzlasten in den Operation Logs sichtbar, anstatt die Nachricht unverarbeitet passieren zu lassen.
  • RunOperation läuft standardmäßig synchron, sodass der Router auf den Abschluss der nachgelagerten Operation wartet. Dies ermöglicht es dem Router, einen Fehler in der nachgelagerten Operation zu erkennen. Ein asynchrones Ausführen würde die Kontrolle sofort zurückgeben und nur melden, ob die Operation in die Warteschlange eingereiht wurde.
  • Mit RunOperation aufgerufene Operationen sind verkettet und laufen auf demselben Agent wie der Router.
  • WriteToOperationLog speichert, welcher Zweig genommen wurde, was eine falsch geleitete Nachricht danach einfach zu diagnostizieren macht.

Schritt 5: Den nachgelagerten Operationen die Eingabe geben

Jede nachgelagerte Operation benötigt die Nutzlast, die der Router inspiziert hat. Verwenden Sie je nachdem, was zum Projekt passt:

  • Aus der globalen Variablen lesen. Die nachgelagerte Operation erbt $gv_payload vom Router. Ordnen Sie direkt aus dieser Variablen zu oder schreiben Sie ein Script dafür, ohne eine zweite Lesung der Quelle.
  • Die Quelle erneut lesen. Wenn die Nutzlast in temporärer Speicherung oder an einem anderen persistenten Ort sitzt, kann die nachgelagerte Operation ihre eigene Leseaktivität gegen denselben Ort verwenden.

Jeder nachgelagerte Vorgang hat ein Quellschema für nur einen Nachrichtentyp, wodurch die Transformation unkompliziert bleibt.

Integration überprüfen

  1. Bereiten Sie für jeden Nachrichtentyp eine Beispiel-Payload vor, plus eine dritte Payload, deren Body-Element keinem entspricht.

  2. Stellen Sie den Router-Vorgang bereit und führen Sie ihn aus für die Create- oder Update-Nachricht.

  3. Bestätigen Sie in den Vorgangslogs, dass der Router den Update-Pfad protokolliert hat und der Vorgang Insert or Update Order ausgeführt wurde. Bestätigen Sie, dass der Vorgang Cancel Order nicht ausgeführt wurde.

  4. Führen Sie den Router für die Stornierungsnachricht aus und bestätigen Sie das Gegenteil: Der Vorgang Cancel Order wurde ausgeführt und der Vorgang Insert or Update Order nicht.

  5. Führen Sie den Router für die nicht erkannte Payload aus und bestätigen Sie, dass der Vorgang mit dem Fehler „Unrecognized message type" fehlgeschlagen ist und keiner der nachgelagerten Vorgänge ausgeführt wurde.

  6. Wenn alle Payloads zum nicht erkannten Branch durchfallen, ist der Namespace das erste, das überprüft werden sollte. Bestätigen Sie, dass die URI im Skript exakt dem xmlns-Wert im Root-Element der Payload entspricht (einschließlich nachfolgender Schrägstriche) und dass jedes Element in der XPath-Abfrage das deklarierte Präfix trägt.

  7. Wenn eine Payload zum falschen Vorgang geroutet wird, protokollieren Sie die Knotenzahl (Option A) oder den Nachrichtentyp (Option B) und vergleichen Sie diese mit der Beispiel-Payload. WriteToOperationLog("Update nodes: " + Length($gv_updateNodes)) bestätigt, was die Abfrage tatsächlich erfasst hat.