XMLReader Fehler bei Flow ausführen aber nicht in Vorschau

Bei UID b5dc68e8-b69f-11ee-839a-901b0ea49fee habe ich im Flow Veloconnect Daily Lin… Daily das Problem, dass in der Vorschau alles Steps sauber funktionieren, allerdings, wenn ich den Flow auführen lasse stoppt der Flow aufgrund des XML Reader, weil er sagt, dass keine Input-Datei vorhanden sei (No input files. Please check previous steps.). Was aber laut Vorschau nicht korrekt ist.

Kann mir hier jemand helfen?

Gruß Markus

Hallo Markus,

einige Steps verhalten sich bei der Vorschau etwas anders als bei der „normalen“ Ausführung.
Kannst du mir sagen, was du als input (xmlfile) im XMLReader Step verwendest ?

Die Veloconnect Steps sollten schon ein Spreadsheet als Ergebnis liefern. Da ist ein XMLReader Step im Normalfall gar nicht mehr notwendig.

Viele Grüße
Torsten

Hallo Torsten,

danke für Deine Rückmeldung. Als Inputfile nutze ich TEMPLATE_OUTPUT_STRING@TextHTMLWriter_31 aus Step 4.

Beste Grüße
Markus Ramsperger

Hallo Markus,

der Output des TextHTMLWriter Steps sollte eigentlich nicht leer sein. Kannst du uns das template des Steps eventuell schicken oder uns sagen ob du im template des TextHTMLWriter Steps outputs anderer Steps (z.B. ${output@stepname_12} ) oder Flow/Projekt Variablen (z.B. ${FlowVariable} ) verwendest?

Viele Grüße
Torsten

Hallo Torsten,

danke für die schnelle Rückmeldung.

Klar gerne:
Template für TextHTMLWriter

<#list responses@APICall_15 as elem>
<#-- Entfernt die Anfangs- und Endklammern aus dem Antwort-String →
<#assign cleanElem = elem?replace(„[“, „“)?replace(„]“, „“)>

<#-- Entfernt Präfixe wie inp2:, inp3: usw. aus den XML-Tags -->
<#assign namespaceRemoved = cleanElem?replace(r"\binp\d+:|\bplnk:|\btns:|\bns\d+:", "", "r")>

<#-- Entfernt den spezifischen XML-Abschnitt -->
<#assign resultRemoved = namespaceRemoved?replace("<BuyersID>[\\s\\S]*?</ResultFormat>", "", "r")>

<#-- Kürzt das <SearchResultResponse> Tag -->
<#assign searchResponseShortened = resultRemoved?replace("<SearchResultResponse[\\s\\S]*?>", "<SearchResultResponse>", "r")>

${searchResponseShortened}

</#list>

TransformationTemplate im nachfolgenden XMLReader

<#assign row = target.addRow()>
<#list xml[„SearchResultResponse/ItemDetail“] as articleDetail>
<#assign row = target.addRow()>
<#-- Definieren der Variablen →
<#assign article = articleDetail[„Item“]>
<#assign description = article[„Description“]?if_exists>
<#assign sellerId = article[„SellersItemIdentification/ID“]?if_exists>
<#assign standardId = article[„StandardItemIdentification/ID“]?if_exists>
<#assign priceAmount = article[„BasePrice/PriceAmount“]?if_exists>
<#assign currencyId = article[„BasePrice/PriceAmount/@amountCurrencyID“]?if_exists>
<#assign recommendedRetailPrice = article[„RecommendedRetailPrice/PriceAmount“]?if_exists>
<#assign availability = articleDetail[„Availability/Code“]?if_exists>

<#-- Hinzufügen der Werte zu den Spalten →
<#if description?has_content>
${row.addCol(„description“, description)}
</#if>
<#if sellerId?has_content>
${row.addCol(„sellerId“, sellerId)}
</#if>
<#if standardId?has_content>
${row.addCol(„standardId“, standardId)}
</#if>
<#if priceAmount?has_content>
${row.addCol(„priceAmount“, priceAmount)}
</#if>
<#if currencyId?has_content>
${row.addCol(„currencyId“, currencyId)}
</#if>
<#if recommendedRetailPrice?has_content>
${row.addCol(„recommendedRetailPrice“, recommendedRetailPrice)}
</#if>
<#if availability?has_content>
${row.addCol(„availability“, availability)}
</#if>
</#list>

ich benutze keine Flow Variable.

Hallo Markus,

vielen Dank, jetzt habe ich die Ursache gefunden. Das Problem ist der responses Output des APICall Steps (responses@APICall_15). Der ist nur zum Debuggen in der Vorschau verfügbar.

Im Normalfall sollte das parsing direkt APICall Step erfolgen. Kannst du mir sagen, warum du die responses nochmal im TextHTMLWriter „bearbeitest“ bevor sie vom XMLReader verarbeitet werden?

Hast du Probleme mit den XML Namespaces? Im Normalfall sollte es möglich sein, die Namespace im parsing template anzugeben, siehe hier . Sie müssen nicht aus der Response entfernt werden.

<#ftl ns_prefixes={"D":"http://meistensirgendeinelangeurl.de/", "inp2":"http://meistensirgendeinelangeurl.de/erweitert"}>

Viele Grüße
Torsten

Hallo Torsten,

da komme ich noch nicht ganz mit. Hab auch die Doku gerade durchgelesen, bin aber noch nicht ganz schlau draus.

Also Du meinst, dass ich direkt nach dem API Call einen XMLReader Step einrichten soll und den Zwischenschritt über TExtHTMLWriter lassen. Das kann ich nachvollziehen.

Mein Problem war eben, dass durch den Namespace inp2 und ähnliche ein Problem beim Einlesen passiert ist. Das habe ich aber schon im APICall über das parsingTemplate

<#if response?has_content>
<#-- Entfernt die Anfangs- und Endklammern aus dem Antwort-String →
<#assign cleanResponse = response?replace(„[“, „“)?replace(„]“, „“)>

<#-- Entfernt Präfixe wie inp2:, inp3: usw. aus den XML-Tags -->
<#assign namespaceRemoved = cleanResponse?replace(r"\binp\d+:|\bplnk:|\btns:|\bns\d+:", "", "r")>

${namespaceRemoved}

</#if>

Behoben. Danach gab es aber weitere Probleme beim einlesen der XML, somit habe ich die Steps aus dem TextHTMLWriter abarbeiten lassen.

Was mir nicht ganz klar ist, welche URL ich in dem ParsingTemplate im XMLReader Step ich eintragen soll. Der API-Aufruf findet doch schon im APICall statt.

Hier habe ich wohl noch eine Denkblockade. Kannst du mir hier auf die Sprünge helfen?

Evtl. Hilft es auch, wenn ich mal den APICall Response hier poste:

> [<SearchResultResponse xmlns:ns18="urn:oasis:names:specification:ubl:schema:xsd:ChipCode-1.0" xmlns:ns17="urn:oasis:names:specification:ubl:schema:xsd:LongitudeDirectionCode-1.0" xmlns:ns16="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-1.0" xmlns:tns="###://xmlns.####.###/###_Work/GetProfileRequestor/GetProfileRequestorRS" xmlns:ns15="urn:oasis:names:specification:ubl:schema:xsd:AllowanceChargeReasonCode-1.0" xmlns:ns14="urn:oasis:names:specification:ubl:schema:xsd:CountryIdentificationCode-1.0" xmlns:ns13="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-1.0" xmlns:ns12="urn:oasis:names:specification:ubl:schema:xsd:CoreComponentTypes-1.0" xmlns:ns11="urn:oasis:names:specification:ubl:schema:xsd:UnspecializedDatatypes-1.0" xmlns:ns10="urn:oasis:names:specification:ubl:schema:xsd:CoreComponentParameters-1.0" xmlns:ns9="urn:oasis:names:specification:ubl:schema:xsd:###Datatypes-1.0" xmlns:ns8="urn:oasis:names:specification:ubl:schema:xsd:ChannelCode-1.0" xmlns:ns7="urn:oasis:names:specification:ubl:schema:xsd:CurrencyCode-1.0" xmlns:ns6="urn:oasis:names:specification:ubl:schema:xsd:LineStatusCode-1.0" xmlns:ns5="urn:oasis:names:specification:ubl:schema:xsd:PaymentMeansCode-1.0" xmlns:ns4="urn:oasis:names:specification:ubl:schema:xsd:DocumentStatusCode-1.0" xmlns:ns3="urn:oasis:names:specification:ubl:schema:xsd:OperatorCode-1.0" xmlns:plnk="###://docs.oasis-open.org/wsbpel/2.0/plnktype" xmlns:ns2="urn:oasis:names:specification:ubl:schema:xsd:LatitudeDirectionCode-1.0" xmlns:ns1="urn:oasis:names:specification:ubl:schema:xsd:SubstitutionStatusCode-1.0" xmlns:ns0="urn:veloconnect:catalog-1.1" xmlns:inp8="urn:veloconnect:stock-B2B-1.4" xmlns:inp7="###://###.###.###/VeloConnect" xmlns:wsdl="###://schemas.xmlsoap.###/wsdl/" xmlns:inp3="urn:veloconnect:transaction-1.0" xmlns:inp2="urn:veloconnect:order-1.1" xmlns:inp1="urn:veloconnect:profile-1.1" xmlns:ns19="urn:veloconnect:stock-1.4" xmlns="urn:veloconnect:catalog-1.1">
>    <inp3:BuyersID>###@###</inp3:BuyersID>
>    <inp3:ResponseCode>200</inp3:ResponseCode>
>    <inp3:ResponseMessage>Operation wurde erfolgreich durchgeführt.</inp3:ResponseMessage>
>    <inp3:TransactionID>####</inp3:TransactionID>
>    <inp3:StatusCode>2</inp3:StatusCode>
>    <ns0:StartIndex>0</ns0:StartIndex>
>    <ns0:Count>10</ns0:Count>
>    <ns0:TotalCount>####</ns0:TotalCount>
>    <ns0:ResultFormat>ITEM_DETAIL</ns0:ResultFormat>
>    <inp2:ItemDetail>
>       <ns16:Item>
>          <ns13:Description>DEFLECT UV CYCLING CAP SPEED OF LIGHT INFRARED S</ns13:Description>
>          <ns16:SellersItemIdentification>
>             <ns16:ID>####-0902</ns16:ID>
>          </ns16:SellersItemIdentification>
>          <ns16:StandardItemIdentification>
>             <ns16:ID identificationSchemeID="EAN/UCC-13">##</ns16:ID>
>          </ns16:StandardItemIdentification>
>          <ns16:BasePrice>
>             <ns13:PriceAmount amountCurrencyID="EUR">##</ns13:PriceAmount>
>             <ns13:BaseQuantity quantityUnitCode="EA">1</ns13:BaseQuantity>
>          </ns16:BasePrice>
>          <ns16:RecommendedRetailPrice>
>             <ns13:PriceAmount amountCurrencyID="EUR">##</ns13:PriceAmount>
>             <ns13:BaseQuantity quantityUnitCode="EA">1</ns13:BaseQuantity>
>          </ns16:RecommendedRetailPrice>
>          <ns0:ItemInformation>
>             <ns0:InformationURL>
>                <ns0:URI>###://assets.###.###/i/###/64821-090_APP_DEFLECT-UV-CYCLING-CAP-SPEED-OF-LIGHT-INFRARED-M_FRONT-3-4?$Display$</ns0:URI>
>                <ns0:Disposition>picture</ns0:Disposition>
>                <ns0:Description>Detailed Picture of DEFLECT UV CYCLING CAP SPEED OF LIGHT INFRARED S</ns0:Description>
>             </ns0:InformationURL>
>             <ns0:ClassificationGroupMember>
>                <ns0:ClassificationSchemeID>50269</ns0:ClassificationSchemeID>
>                <ns0:GroupID>HATDEFLECT</ns0:GroupID>
>             </ns0:ClassificationGroupMember>
>          </ns0:ItemInformation>
>       </ns16:Item>
>       <inp2:Availability>
>          <inp2:Code>available</inp2:Code>
>       </inp2:Availability>
>    </inp2:ItemDetail>...

Die kritischen Stellen habe ich mit ### maskiert.

Der APICall Step kann die XML Response direkt parsen und ein Spreadsheet daraus erstellen. Der TextHTMLWriter und XMLReader Steps sind nicht notwendig. Du kannst als responseFormat XML auswählen und dann das parsingTemplate im XML Step hinterlegen.

Ja, das kann ich sehr gut nachvollziehen. Das parsing ist immer etwas komplizierter mit den Namespaces. Außerdem scheint in deiner response der Namespace „urn:veloconnect:catalog-1.1“ doppelt vorhanden zu sein. Das mag unser XML parser gar nicht. Mit einem kleinen workarround solltest du die Response aber trotzdem verarbeitet bekommen.

Für den Workarround im parsingTemplate kannst du auf die Deklaration der Namespaces verzichten (<#ftl ns_prefixes ... ). Im parsingTemplate verwendest du dann local-name() ( der Name des Elements ohne Namespace, technische Detail gibt es hier und hier), um an die Werte der jeweiligen XML Elemente zu kommen.

Das ganze sollte dann dann in etwa folgendermaßen aussehen:

<#assign row = target.addRow()>

<#list xml["/*[local-name()='SearchResultResponse']/*[local-name()='ItemDetail']"] as articleDetail>
  <#assign row = target.addRow()>

  <#-- Definieren der Variablen -->
  <#assign article = articleDetail["*[local-name()='Item']"]>

  ${row.addCol("description", article["*[local-name()='Description']"]! )}
  ${row.addCol("sellerId", article["*[local-name()='SellersItemIdentification']/*[local-name()='ID']"]! )}
  ${row.addCol("standardId", article["*[local-name()='StandardItemIdentification']/*[local-name()='ID']"]! )}
  
</#list>

Ich hoffe das hilft dir etwas weiter. Falls du Fragen dazu hast. kannst du dich gern bei mir melden.

VG Torsten

Guten Morgen Torsten,

super. das hat mir schon sehr geholfen. mit diesem Step scheine ich schon weiter zu sein. Jetzt läuft der Flow auch sauber durch. Allerdings liefert er eine leere CSV. Obwohl in der Vorschau bis inklusive zum FTP Export Step alles korrekt befüllt ist. Jetzt habe ich die Vermutung, dass ich das gleiche Problem bei meinem ersten APICall Step habe.

Hier habe ich nun auch versucht über das das parsingTemplate direkt über den APICall anzupassen. Und zwar so:


<#if (response?has_content)>
  <#-- Entfernt die Anfangs- und Endklammern aus dem Antwort-String -->
  <#assign cleanResponse = response?replace("[", "")?replace("]", "")>
  
  <#-- Sucht nach der TransactionID im bereinigten Response -->
  <#assign transactionIdRegex = "<inp3:TransactionID>(\\d+)</inp3:TransactionID>">
  <#assign transactionMatch = cleanResponse?matches(transactionIdRegex)>
  
  <#-- Überprüft, ob eine Übereinstimmung gefunden wurde und extrahiert den Wert -->
  <#if transactionMatch?size gt 0>
    <#assign transactionId = transactionMatch[0]?groups[1]>
    ${transactionId}
  <#else>
    <!-- Keine TransactionID gefunden -->
  </#if>
</#if>

Allerdings ist der Output leer. wenn ich über einen nachgelagerten Step auf den Output zugreifen möchte, ist dieser leer.

Bisher habe ich es so gemacht, dass ich über einen TextHTMLWriter die TransactionID rausgefilter habe, über das Template

<#list responses@APICall_14 as elem>
  <#-- Verwende einen regulären Ausdruck, um die TransactionID zu finden -->
  <#-- Der reguläre Ausdruck sucht nach dem Muster '<inp3:TransactionID>...</inp3:TransactionID>' -->
  <#assign transactionIdRegex = "<inp3:TransactionID>(.+?)</inp3:TransactionID>">
  <#assign transactionMatch = elem?matches(transactionIdRegex)>

  <#-- Überprüft, ob eine Übereinstimmung gefunden wurde und extrahiert den Wert -->
  <#if transactionMatch?size gt 0>
    <#assign transactionId = transactionMatch[0]?groups[1]>
    ${transactionId}
  </#if>
</#list>

Kannst Du mir hier nochmal helfen, wo hier mein Gedankenfehler ist?

Gurß Markus

Hallo Markus,

das parsingTemplate wird nicht funktionieren weil keine response Variable zur Verfügung steht. Die response wird je nach eingestellten parsingMode (bei JSON oder XML) direkt geparst und auf die Elemente / das Ergebnis kannst du mit json[...] (parsingMode = JSON) und xml[...] (parsingMode = XML) zugreifen.

Kannst du bitte mal folgendes Template in deinem 1. APICall Step probieren:

<#assign row = target.addRow()>
<#assign row = target.addRow()>
${row.addCol("TransactionID", xml["/*[local-name()='CreateTextSearchResponse']/*[local-name()='TransactionID']"][0]!)}

CreateTextSearchResponse geht aus deinem parsingTemplate nicht hervor / hab ich mal von Veloconnect übernommen. Wenn ich falsch liege musst du das noch durch den richtigen „parent“ Namen ersetzen

Das sollte dir ein Spreadsheet mit einer Spalte „TransactionID“ erzeugen. Darauf kannst du dann im 2. APICall Step z.B. über ${output@APICall_X.firstRow("TransactionID")} zugreifen. Das X ist die ID des Step und muss noch ersetzt werden. Die Outputs kannst du dir mit klick auf den Pfeil nach dem 1. API Call Step anzeigen lassen:

image

image

Viele Grüße
Torsten

Das hat super funktionert. Super, danke. Jetzt muss ich mich noch mit der Pagination herumschlagen. Da bin ich auch nicht so ganz firm. Kannst du mir hier evtl. auch noch helfen? wie mus sich genau vorgehen, damit ich den APICall immer inkl. 100 Zeilen je Call aufteile und abarbeite bis alle evtl. 6.000 Zeilen / Artikel im parsingTemplate gespeichert werden?

Du kannst das parsingTemplate von oben (für SearchResultResponse) etwas erweitern.

Dein Wert im Feld host sollte in etwa so aussehen:

host: https://domain.de?TransactionID=XY&StartIndex=0&Count=100&...

Der Parameter Count gibt die Anzahl der Ergebnisse pro Seite an, StartIndex im Prinzip die Seite die du abrufen willst.

In der Response sollten ein TotalCount und ein StartIndex Element vorhanden sein. Diese kannst du für die pagination Funktion verwenden. Der pagination Teil sollte dann in etwa wie folgt aussehen. Wichtig ist, dass du in der URL der pagination Funktion den Parameter StartIndex weglässt, da dieser automatisch hinzugefügt wird:


<#assign totalResults =  xml["/*[local-name()='SearchResultResponse']/*[local-name()='TotalCount']"]!?number  />
<#assign currentStartIndex =  xml["/*[local-name()='SearchResultResponse']/*[local-name()='StartIndex']"]!?number  />

${ nextUrl( pagination( 'https://domain.de?TransactionID=XY&Count=100', currentStartIndex, totalResults, 100).plusOne('StartIndex') )}

Viele Grüße
Torsten