Da stößt der VisualXMLReader vermutlich an seine Grenzen.
Man könnte mal mit dem "normalen" XMLReader probieren.
Hier mal noch ein umschließendes <products> mit drum herum, damit es eine Liste von <product> wird (werden kann):
Quelle (mit Hilfe des Steps StringToFile simuliert):
<products>
<product>
<productidentifier>
<id_type>01</id_type>
<id_value>123ABC</id_value>
</productidentifier>
<productidentifier>
<id_type>02</id_type>
<id_value>456DEF</id_value>
</productidentifier>
</product>
</products>
ParsingTemplate:
<#assign row = target.addRow()>
<#list xml["products"]["product"]["productidentifier"] as p>
<#assign row = target.addRow()>
${addColumns(row, p)}
<#-- or Example 2: Exclude certain fields: ${addColumns(row, p, '', {'columns':['fields', 'col2'], 'mode':'exclude'})} -->
<#-- or Example 3: Add a sub-object and prefix all columns: ${addColumns(row, p['fields'],'field_')} -->
</#list>
Ergebnis:

Mehr zu der addColumns() Funktion gibt es hier.
Ok, das ging nicht draus hervor. Eine Tabelle als Beispiel des gewünschten Ergebnisses zu den Quelldaten ist oft hilfreich.
Vermutlich muss man bei dieser dynamischen Struktur den komplett "manuellen" Weg gehen, da die "Magic" (addColumns oder VisualXMLReader) solche Strukturen nicht erkennt.
Quelldaten:
<products>
<product>
<productidentifier>
<id_type>01</id_type>
<id_value>123ABC</id_value>
</productidentifier>
<productidentifier>
<id_type>02</id_type>
<id_value>456DEF</id_value>
</productidentifier>
<contributor>
<ContributorRole>A01</ContributorRole>
<PersonName>Max Mustermann</PersonName>
</contributor>
<contributor>
<ContributorRole>B02</ContributorRole>
<PersonName>Madeleine Musterfrau</PersonName>
</contributor>
<contributor>
<ContributorRole>B02</ContributorRole>
<PersonName>Bart Simpson</PersonName>
</contributor>
</product>
<product>
<productidentifier>
<id_type>01a</id_type>
<id_value>123ABCa</id_value>
</productidentifier>
<productidentifier>
<id_type>02a</id_type>
<id_value>456DEFa</id_value>
</productidentifier>
<contributor>
<ContributorRole>A01</ContributorRole>
<PersonName>Max Mustermann</PersonName>
</contributor>
<contributor>
<ContributorRole>B02</ContributorRole>
<PersonName>Madeleine Musterfrau</PersonName>
</contributor>
<contributor>
<ContributorRole>B02</ContributorRole>
<PersonName>Bart Simpson</PersonName>
</contributor>
</product>
</products>
ParsingTemplate:
<#assign row = target.addRow()>
<#list xml["products"]["product"] as p>
<#assign row = target.addRow()>
${row.addCol("id1",p["productidentifier"][0]["id_type"])}
${row.addCol("id1val",p["productidentifier"][0]["id_value"])}
${row.addCol("id2",p["productidentifier"][1]["id_type"])}
${row.addCol("id2val",p["productidentifier"][1]["id_type"])}
<#list p["contributor"] as c>
${row.addCol("contrib"+c?index , c["ContributorRole"])}
${row.addCol("contrib"+c?index , c["PersonName"])}
</#list>
</#list>
Das Beispiel zeigt 2 verschiedene Ansätze:
- die productidentifier sind händisch per Index in eckigen Klammern angegeben. Vorteil: Man sieht genau was passiert. Nachteil: muss aber wissen wie viele identifier es geben kann.
- die Contributors sind dynamisch mit Hilfe einer Schleife (Freemarker <#list>) erzeugt und auch über einen Zähler eindeutig gemacht (?index). Vorteil: es ist egal wie viele Contributors es gibt. Nachteil: Etwas schwerer zu verstehen auf den ersten Blick
Man kann beide Ansätze kombinieren.
Ergebnis:

Die vorher erwähnte addColumns() Funktion passt hier leider nicht.
Also die IF-Bedinungung innerhalb der Liste ist an dieser Stelle schon der korrekte Weg, wenn man sich wirklich nur einen einzigen identifier anhand der ProductIDType herauspicken möchte. Die XML-Struktur erfordert das.
Evtl. könnte man das noch mit dem ?filter Ausdruck anders schreiben (z.B. ${row.addCol("SKU" , p["ProductIdentifier"]?filter(pi -> pi["ProductIDType"] == "03")[0]) ), aber an der Stelle ist <#list> und <#if> vielleicht etwas lesbarer. Am Ende Geschmackssache.
Zur Frage, ob der Mapper notwendig ist?
Also wenn aus dem XMLReader alles schon so rauskommt, wie man es braucht, dann kann der Mapper entfallen.
Man könnte aber auch im XMLReader so wenig wie möglich Logik (if-else) reinpacken (d.h. einfach alle IDs ausgeben) und dann hinterher per Mapper, die Spalten anpassen und entfernen, was man nicht braucht.
Am Ende Geschmackssache. Ist die Frage, wer das später pflegen muss und ob man evtl. mal doch noch die anderen Identifier braucht, z.B. für einen anderen späteren Step. Mit dem Mapper geht das vermutlich einfacher von der Hand als in diesem Parsing-Script rumzuwerkeln. Wir würden an dieser Stelle aus Wartbarkeitsaspekten eher den Mapperansatz empfehlen. Auf der anderen Seite ist ihr Ansatz natürlich sparsamer, was die Menge der ausgegebenen Daten angeht.