Brio-IO Communication Server

Transformer-Handbuch

Das Herzstück: Feldpfade lesen und schreiben, Segmente anlegen, Wiederholfelder, Escapes — in Groovy, GraalJS und Rhino, mit Beispielen, die genau so laufen.

Stand: Beispiele geprüft gegen brio-server fa237ec7ad

Ein Integrationsserver verbringt die meiste Zeit damit, Nachrichten umzuschreiben. In Brio-IO passiert das im Transformer. Dieses Kapitel ist die Referenz dafür: jedes Beispiel ist gegen den laufenden Server geprüft, in allen drei Engines.

Falls du das Modell dahinter noch nicht kennst — kanonisches XML, Underscores, positionale Komponenten — lies zuerst Konzepte.

Die Regel für Feldpfade

  • Einfaches Feld → direkt: msg.MSH.MSH_10
  • Zusammengesetztes Feld → eine Ebene je Komponente: msg.PID.PID_5.PID_5_1
  • Subkomponente → noch eine Ebene: msg.PID.PID_5.PID_5_1_1

Ein Zugriff auf einen Pfad, den es nicht gibt, wirft nicht — er liefert einen leeren String. Das ist Absicht und entspricht dem Verhalten, das Mirth-Praktiker kennen: eine Nachricht ohne das erwartete Feld soll die Verarbeitung nicht abbrechen.

msg ist das Wurzel-Element. Du steigst also unterhalb der Wurzel ein — kein ADT_A01-Präfix.

Lesen

// Groovy — .text() liefert den Feldwert
def nachname = msg.PID.PID_5.PID_5_1.text()   // "Schmidt"
def vorname  = msg.PID.PID_5.PID_5_2.text()   // "Hans"
// GraalJS (js-modern) — ein Text-Feld liest sich direkt als String
let nachname = msg.PID.PID_5.PID_5_1;   // "Schmidt"
let patId    = msg.PID.PID_3.PID_3_1;   // "12345"
// Rhino (js-legacy) — E4X-Brackets mit Punkt-Notation, wie in Mirth
var nachname = msg['PID']['PID.5']['PID.5.1'].toString();

Dass Rhino die Punkt-Variante derselben Nachricht sieht, erledigt der Pipeline-Prozessor. Du musst dich nicht darum kümmern.

Schreiben und zurückgeben

Ein Transformer verändert das geparste msg-Objekt und gibt es zurück. Die Engine serialisiert an der Schritt-Grenze automatisch zurück nach kanonischem XML — von Hand serialisieren musst du nichts.

// Groovy: explizites return, Wert am Knoten über [0].value setzen
def nachname = msg.PID.PID_5.PID_5_1.text()
msg.PID.PID_5.PID_5_1[0].value = nachname.toUpperCase()
return msg
// GraalJS: direkte Zuweisung, letzter Ausdruck ist das Ergebnis
msg.PID.PID_5.PID_5_1 = msg.PID.PID_5.PID_5_1.toUpperCase();
msg;
// Rhino: Feld setzen, msg als letzter Ausdruck
msg['PID']['PID.5']['PID.5.1'] = 'Mueller';
msg;

Der Unterschied zwischen Groovy und GraalJS ist nur Sprach-Idiom: Groovy braucht ein explizites return und setzt den Wert am Knoten ([0].value), in GraalJS reicht der letzte Ausdruck und die direkte Zuweisung.

Fehlende Pfade werden angelegt

Schreibst du auf einen Pfad, den es in der Nachricht nicht gibt, legt Brio-IO die fehlenden Ebenen an. Du musst also nicht erst prüfen, ob ein Feld existiert, bevor du es setzt.

Filter: gib einen Wahrheitswert zurück

Ein Filter muss einen Boolean liefern. true lässt die Nachricht durch, alles andere verwirft sie — sichtbar als FILTERED im Message-Browser.

// Groovy: nur Nachrichten mit gesetzter Patienten-ID
return msg.PID.PID_3.PID_3_1.text() != ""
// GraalJS
msg.PID.PID_3.PID_3_1 !== "";

Ein Beispiel, das in der Demo genau so läuft — nur ADT^A08 durchlassen:

msg.MSH.MSH_9.MSH_9_2.text() == 'A08'

Wiederholfelder

Ein wiederholtes Element liest sich als indexierbare Liste:

// GraalJS — zwei Wiederholungen von PID-3
msg.PID.PID_3[0]      // "MRN-1"
msg.PID.PID_3[1]      // "MRN-2"
msg.PID.PID_3.length  // 2

In Groovy gilt dasselbe Muster über den Index (msg.PID.PID_3[0].text()). Wenn du komplexer navigierst, steht dir zusätzlich der volle XPath-Zugriff über xml.xpath offen.

Segmente anlegen

Ein neues Segment — etwa ein Z-Segment für hauseigene Zusatzinformationen — legst du mit createSegment an. Das Ergebnis ist ein Knoten, den du direkt befüllst.

// Groovy: ZBR anhängen und füllen
def z = createSegment('ZBR', msg)
z.appendNode('ZBR_1', 'script-created')
msg
// GraalJS: ZBR anhängen und füllen
var z = createSegment('ZBR', msg);
z.ZBR_1 = 'script-created';
msg;

Beides erzeugt <ZBR><ZBR_1>script-created</ZBR_1></ZBR> am Ende der Nachricht.

An eine bestimmte Position einfügen statt anhängen:

// Index 1 = direkt nach dem ersten Segment (MSH)
createSegment('ZZZ', msg, 1);
msg;

Direkt hinter ein bestimmtes Segment einfügen:

// GraalJS
createSegmentAfter('ZZZ', msg.MSH);
msg;
// Groovy — hier den Knoten explizit adressieren
createSegmentAfter('ZZZ', msg.MSH[0])
msg

In GraalJS gibt es zusätzlich die Kurzform, die Segment und Inhalt in einem Schritt erzeugt:

msg.ZBR = 'script-created';
msg;

Escapes

HL7-Escape-Sequenzen stehen in der kanonischen XML verbatim\S\ bleibt \S\. Wer sie im Skript auflösen oder erzeugen will, nutzt die beiden Helfer, die es in allen drei Engines gibt:

escapeHl7('a^b&c')        // "a\S\b\T\c"
unescapeHl7('a\\S\\b\\T\\c')  // "a^b&c"

Das ist der Weg, um einen Wert sicher in ein Feld zu schreiben, der selbst HL7-Trennzeichen enthält — ohne die Nachrichtenstruktur zu zerlegen.

Eine zweite Nachricht parsen

Manchmal braucht ein Skript eine zweite Nachricht — etwa eine, die in einer Map liegt. Dafür gibt es die Serializer-Fassade:

// GraalJS
var s = SerializerFactory.getSerializer('HL7V2');
var x = s.toXML(einAndererEr7String);
// x ist jetzt kanonisches XML, gegen das dieselben Pfade laufen

Der Nachrichten-Kontext: Maps

Werte zwischen Filter, Transformer und Destinations weiterreichen:

def patientId = msg.PID.PID_3.PID_3_1.text()
channelMap.put("patientId", patientId)
return msg
  • channelMap — lebt für diese eine Nachricht bis zu den Destinations
  • sourceMap, responseMap — Kontext des Source-Connectors bzw. der Antwort
  • globalChannelMap — channelweit persistent
  • globalMap — serverweit persistent
  • tmp — Scratchpad, nur für diese eine Ausführung

xml.get / xml.set: die Übergangs-API

Für Bestandsskripte und Nicht-XML-Payloads (JSON, Raw — dort bleibt msg ein String) gibt es den Pfad-Helfer xml. Er ist engine-übergreifend identisch und akzeptiert msg sowohl als String als auch als geparstes Objekt:

// Pfad ab der Wurzel, inklusive Wurzel-Element
def nachname = xml.get(msg, "ADT_A01/PID/PID_5/PID_5_1")

Anders als beim Property-Zugriff startet der Helfer-Pfad am Wurzel-Element. Als Trennzeichen akzeptiert er Schrägstrich und Punkt. Neben get gibt es set zum Schreiben und xpath für vollen XPath-Zugriff:

let nachname = xml.xpath(msg, "//*[local-name()='PID_5_1']/text()");

Für neue Skripte ist der Property-Zugriff der empfohlene Weg — er ist lesbarer und schneller, weil er direkt auf dem geparsten Objekt arbeitet, statt die Nachricht bei jedem Feldzugriff neu zu serialisieren.

Was in Rhino anders ist

Rhino (js-legacy) existiert für importierte Mirth-Skripte und bekommt als einzige Engine die Punkt-Notation — die Welt, die diese Skripte erwarten. msg ist dort direkt ein E4X-XML-Objekt; das aus Mirth gewohnte new XML(msg) ist nicht mehr nötig.

Rhino ist bewusst deprecated: Bei jedem Deploy eines Channels mit mindestens einem Rhino-Skript gibt Brio-IO eine Deprecation-Warnung aus — im Log und in der API-Antwort — und nennt die betroffenen Skripte namentlich. Auch im Skript-Editor erscheint eine Warnung, sobald du js-legacy wählst.

Der Migrationsweg steht in der Mirth-Migration.

Häufige Stolpersteine

Datentyp-Pfade aus einer Zwischenfassung. msg.PID.PID_5.XPN_1.FN_1 liefert heute still einen leeren String. Richtig ist msg.PID.PID_5.PID_5_1. Beim Deployment warnt Brio-IO namentlich, wenn ein Skript noch solche Pfade enthält — diese Warnung ernst nehmen.

Vergessenes return in Groovy. Ohne return msg gibt der Transformer den Wert des letzten Ausdrucks zurück, und das ist selten die Nachricht.

Mehrstufige Vivification per Property in Groovy/GraalJS. Ein fehlender Zwischenknoten lässt sich in diesen beiden Engines nicht durch reine Property-Zuweisung erzeugen — dafür gibt es createSegment bzw. xml.set. Nur Rhino/E4X kann das nativ.

router.routeMessage() gibt es nicht. Der Weg von Channel zu Channel ist eine Destination am Channel.

Diese Seite gibt es bisher auf Deutsch und Englisch. Die spanische Fassung folgt.