XML und JSON Parsing am Beispiel

Auf dieser Seite sammeln wir Beispiele, wie man XML oder JSON Daten mit Synesty einlesen kann. 
Diese Seiten sind Ergänzung zu JSONReader und XMLReader. Im Forum finden Sie in der Suche auch weitere Inhalt und Diskussionen (z.B. hieropen in new window oder hieropen in new window)

Beispiel 1 - XML - Einfache XML Struktur

Quelle: XML2Spreadsheet#XMLParsingVariante1

<?xml version="1.0" encoding="ISO-8859-1"?>
<ARTIKELLISTE>
    <ARTIKEL>
        <LFSN>ZEG</LFSN>
        <LFKURZBEZ>ZEG</LFKURZBEZ>
        <INTARTNR>8592</INTARTNR>
        <ARTNR>010-12983</ARTNR>
        <REFARTNR>010-12983</REFARTNR>
        <HERSTNR>870310 / 10870310</HERSTNR>
        <MARKE>Schwalbe</MARKE>
        <MARKENLOGO></MARKENLOGO>
        <MODELL>High Pressure</MODELL>
        <MODELLJAHR>0</MODELLJAHR>
        <FARBE>blau</FARBE>
    </ARTIKEL>
    <ARTIKEL>
        <LFSN>ZEG</LFSN>
        <LFKURZBEZ>ZEG</LFKURZBEZ>
        <INTARTNR>8593</INTARTNR>
        <ARTNR>010-12984</ARTNR>
        <REFARTNR>010-12984</REFARTNR>
        <HERSTNR>870311 / 10870311</HERSTNR>
        <MARKE>Simson</MARKE>
        <MARKENLOGO></MARKENLOGO>
        <MODELL>High Pressure</MODELL>
        <MODELLJAHR>0</MODELLJAHR>
        <FARBE>rot</FARBE>
    </ARTIKEL>
</ARTIKELLISTE>

Parsing Template:

<#assign row = target.addRow()>
 
<#list xml["ARTIKELLISTE"]["ARTIKEL"] as art>
    <#assign row = target.addRow()>
    ${addColumns(row, art)}
</#list>

oder Alternative:

<#assign row = target.addRow()>
 
<#list xml["ARTIKELLISTE"]["ARTIKEL"] as art>
    <#assign row = target.addRow()>
    ${row.addCol("Artikelnummer",art["ARTNR"])}
    ${row.addCol("Marke",art["MARKE"])}
    ${row.addCol("Modell",art["MODELL"])}
</#list>

Ergebnis:

Beispiel 2 - XML - Komplexe Struktur mit Attributen und Referenzelementen

Anforderung:

Ich habe eine Artikeldatei, die in einem XML Abschnitt Artikel mit Varianten enthält. Nach diesem Schema sind die Artikel mit Eigenschaften der Varianten in Unterabschnitten enthalten.
Es soll für jeden GrIndex eine Zeile geschrieben werden.

<Artikeldaten>
        <Artikel Aktiv="1">
            <ArtikelNr>123456</ArtikelNr>
                        <ArtikelName>Toller Artikel</ArtikelName>
<VKPreise>
    <VKPreis GrIndex="6" Waehrung="EUR" VK1="34,95" VK2="0,00" VK3="0,00">34,95</VKPreis>
    <VKPreis GrIndex="7" Waehrung="EUR" VK1="34,95" VK2="0,00" VK3="0,00">34,95</VKPreis>
</VKPreise>
<Bestaende>
   <Bestand GrIndex="6">2</Bestand>
   <Bestand GrIndex="7">8</Bestand>
</Bestaende>
<BestaendeDetails>
                <BestaendeDetail Filiale="001" GLN="4399902192156">
                    <Bestand GrIndex="6">1</Bestand>
                    <Bestand GrIndex="7">1</Bestand>
                </BestaendeDetail>
                <BestaendeDetail Filiale="002" GLN="4399902347143">
                    <Bestand GrIndex="6">1</Bestand>
                    <Bestand GrIndex="7">1</Bestand>
                </BestaendeDetail>
                <BestaendeDetail Filiale="003" GLN="4399902347136">
                    <Bestand GrIndex="6">0</Bestand>
                    <Bestand GrIndex="7">6</Bestand>
                </BestaendeDetail>
            </BestaendeDetails>
<EAN_Codes>
    <EAN GrIndex="6" />
    <EAN GrIndex="7">8023121212111</EAN>
</EAN_Codes>
</Artikel>

</Artikeldaten>

Parsing Template:

<#assign row = target.addRow()>
<#list xml["Artikeldaten"]["Artikel"] as art>
  <#assign row = target.addRow()>
  ${addColumns(row, art)}
 <#list art['VKPreise']["VKPreis"] as vkp>
    <#assign vkprow = target.addRow()>
    ${vkprow.addCol("VKPreis", vkp)}
    ${vkprow.addCol("GrIndex", attr("GrIndex", vkp)!)}  
    ${vkprow.addCol("VK1", attr("VK1", vkp)!)}

    <#-- now the difficult part: getting the stock and EAN for each GrIndex -->
    ${vkprow.addCol("Bestand", art['Bestaende']['Bestand']?filter(bestand -> attr("GrIndex", bestand)! == attr("GrIndex", vkp)!)?first!)}
    ${vkprow.addCol("EANCode", art['EAN_Codes']['EAN']?filter(ean -> attr("GrIndex", ean)! == attr("GrIndex", vkp)!)?first!)}

    <#list art["BestaendeDetails"]["BestaendeDetail"] as bestandDetail>
      <#list bestandDetail["Bestand"] as bestand>
         <#if attr("GrIndex", bestand)! == attr("GrIndex", vkp)>
           ${vkprow.addCol("Bestand_Filiale_"+attr("Filiale", bestandDetail), bestand!)}
        </#if>
      </#list>
    </#list>

  </#list>
</#list>

Ergebnis

Weitere Informationen

Beispiel 3 - XML Produktkatalog mit mehreren Merkmal Tags

Quelle: https://forum.synesty.com/t/xml-reader/1902open in new window

Anforderung:"Mein Problem ist, dass der Knoten "Merkmal"  mehrfach vorkommen kann und ich insgesamt ca. 500 verschiedene Merkmalschlüssel habe."

<?xml version="1.0" encoding="UTF-8"?>
<ZEGSHOP>
  <DELETE>1</DELETE>
  <HAUPTKATEGORIE>
    <KATEGORIE>
      <ARTIKEL>
        <INTARTNR>22581</INTARTNR>
        <MERKMAL>
          <MERKMALSCHLUESSEL>FARB</MERKMALSCHLUESSEL>
          <AUSPRAEGUNGSSCHLUESSEL>SW</AUSPRAEGUNGSSCHLUESSEL>
          <MERKMAL>Farbe</MERKMAL>
          <AUSPRAEGUNG>schwarz</AUSPRAEGUNG>
        </MERKMAL>
        <MERKMAL>
          <MERKMALSCHLUESSEL>FARR</MERKMALSCHLUESSEL>
          <AUSPRAEGUNGSSCHLUESSEL>SW</AUSPRAEGUNGSSCHLUESSEL>
          <MERKMAL>Farbrichtung</MERKMAL>
          <AUSPRAEGUNG>schwarz</AUSPRAEGUNG>
        </MERKMAL>
        <MERKMAL>
          <MERKMALSCHLUESSEL>FUTI</MERKMALSCHLUESSEL>
          <AUSPRAEGUNGSSCHLUESSEL>FU200</AUSPRAEGUNGSSCHLUESSEL>
          <MERKMAL>Funktion</MERKMAL>
          <AUSPRAEGUNG>31</AUSPRAEGUNG>
        </MERKMAL>
      </ARTIKEL>
    </KATEGORIE>
  </HAUPTKATEGORIE>
</ZEGSHOP>

Parsing Code:

<#assign row = target.addRow()>
<#list xml["ZEGSHOP"]["HAUPTKATEGORIE"]["KATEGORIE"]["ARTIKEL"] as p>
  <#assign row = target.addRow()>
  <#-- ${addColumns(row, p, '', {'columns':['MERKMAL'], 'mode':'exclude'})} -->


 <#list p["MERKMAL"] as t>
   ${row.addCol("Merkmalschlüssel-" + t["MERKMALSCHLUESSEL"], t["MERKMALSCHLUESSEL"])}
   ${row.addCol("Ausprägungsschlüssel- " + t["MERKMALSCHLUESSEL"], t["AUSPRAEGUNGSSCHLUESSEL"])}
   ${row.addCol("Merkmal- " + t["MERKMALSCHLUESSEL"], t["MERKMAL"])}
   ${row.addCol("Ausprägung- " + t["MERKMALSCHLUESSEL"], t["AUSPRAEGUNG"])}
 </#list>

 ${addColumns(row, p, '', {'columns':['MERKMAL'], 'mode':'exclude'})}

</#list>

Ergebnis:

Weitere Informationen

  • Herausforderung ist, dass die Merkmals-Wertein Tags wie z.B. <MERKMALSCHLUESSEL> **,**gleichzeitig auch die Spaltentitel sein sollen
  • allein mit addColumns() kommt man nicht weit, da die Anforderung zu speziell ist
  • daher wird die Funktion *row.addCol(title, value) *genutzt (siehe XMLReader - Parsing Variante 2), die volle Kontrolle über die Spaltentitel bietet

Beispiel 4 - SOAP XML-Webservice Response verarbeiten

SOAP Webservices sind im Grund nichts anderes als HTTP-Requests mit XML als Austauschformat analog zu den mittlerweile weit verbreiteten REST-APIs
D.h. man arbeitet genau so mit den Steps wie APICallopen in new window, SpreadsheetURLDownloadopen in new window und XMLReaderopen in new window.
Beim Verarbeiten der etwas komplexeren XML-Struktur muss man ein paar Dinge beachten wie z.B.  XML-Namespaces (die xmlns Angaben im Beispiel unten).

Das Beispiel zeigt eine beispielhafte SOAP XML-Response eines Webservices. Darin enthalten ist eine Liste mit zwei **PackageGroupRequest-Objekten,**die als Spreadsheet ausgegeben werden sollen.

<soap:Envelope
     xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
     xmlns="http://www.meinprefix.net/foo/2013/04">
   <soap:Header/>
     <soap:Body>

       <PackageGroupRequest
         xmlns="http://www.meinprefix.net/foo/2013/04"
         AuthKey="XXXXX">
         <TravelPeriod>
           <DepartureDate>2015-08-01</DepartureDate>
           <Duration>128</Duration>
         </TravelPeriod>
         <Travellers>
           <Adult Age="28"/>
         </Travellers>
         <Hotel>
           <Rooms />
         </Hotel>
       </PackageGroupRequest>

       <PackageGroupRequest
         xmlns="http://www.meinprefix.net/foo/2013/04"
         AuthKey="XXXXX">
         <TravelPeriod>
           <DepartureDate>2013-08-01</DepartureDate>
           <Duration>31</Duration>
         </TravelPeriod>
         <Travellers>
           <Adult Age="19"/>
         </Travellers>
         <Hotel>
           <Rooms />
         </Hotel>
       </PackageGroupRequest>

     </soap:Body>
   </soap:Envelope>

parsingTemplate im XMLReader:

<#ftl ns_prefixes={"A":"http://www.w3.org/2003/05/soap-envelope",
                   "B":"http://www.meinprefix.net/foo/2013/04"}>

<#assign row = target.addRow()>
<#list xml["A:Envelope"]["A:Body"]["B:PackageGroupRequest"] as item>
    <#assign row = target.addRow()>
    ${addColumns(row, item, "", {"autoExpand": "asColumns"})}
</#list>

Ergebnis:

Weitere Informationen und Besonderheiten

  • wichtig ist die Angaben der Namespaces in ns_prefixes mit den Aliasen bzw. Prefixen A und B.
    Dieses A und B wird auch in der <#list> Anweisung verwendet
  • *{"autoExpand": "asColumns"} *ist eine Option des XMLReader, um Unter-Nodes eines XML-Tags automatisch als Spalten darzustellen.

Beispiel 5 - JSON Parsing

Quelle: https://forum.synesty.com/t/json-parsingtemplate/2310open in new window

Anforderung:

Im Spreadsheet werden die Werte

  • produzent_4711
  • Produzent 4711
  • 123456
  • Stadt
  • Land

jeweils in einer Spalte benötigt.

JSON:

{
    "_links": {
        "self": {
            "href": "xxxxx"
        },
    "first":{
        "href": "xxxxx"
        },
    "next": {
        "href": "xxxxx"
        }
    },
    "_embedded":{
        "items":[
            {
                "links":{
                    "self":{
                        "href": "xxxxx"
                    }
                },
                "code": "produzent_4711",
                "values":{
                    "label":[
                        {
                            "locale": "de_DE",
                            "channel": null,
                            "data": "Produzent 4711"
                        },
                    ],
                    "address_zip_code":[
                        {
                            "locale": "de_DE",
                            "channel": null,
                            "data": "123456"
                        }
                    ],
                    "address_city":[
                        {
                            "locale": "de_DE",
                            "channel": null,
                            "data": "Stadt"
                        }
                    ],
                    "address_country":[
                        {
                            "locale": null,
                            "channel": null,
                            "data": "Land"
                        }
                    ]
                }
            }
        ]
    }
}

parsingCode für JSON Reader:

<#assign row = target.addRow()>
<#list json["_embedded"]["items"] as r >
    <#assign itemsRow = target.addRow()>
    ${itemsRow.addCol("code",r["code"]!)}
    ${itemsRow.addCol("label",r["values"]["label"][0]["data"]!)}
    ${itemsRow.addCol("address_zip_code",r["values"]["address_zip_code"][0]["data"]!)}
    ${itemsRow.addCol("address_city",r["values"]["address_city"][0]["data"]!)}
    ${itemsRow.addCol("address_country",r["values"]["address_country"][0]["data"]!)}
</#list>

Ergebnis:

Beispiel 6 - JSON Parsing

Quelle: https://forum.synesty.com/t/plenty-newsletter-ordner-abrufen/2387open in new window

Anforderung:

  • KundenID in erster Spalte
  • dann die enthaltenen Felder (bzw: email und confirmedTimestamp) als weitere Spalten

JSON:

{
    "12345": {
        "id": 140937,
        "folderId": "18",
        "contactId": "0",
        "firstName": "redacted",
        "lastName": "redacted",
        "email": "redacted",
        "gender": null,
        "birthday": "0000-00-00",
        "timestamp": "2021-08-07 17:43:27",
        "templateLang": "de",
        "confirmedTimestamp": "2021-08-07 17:44:09",
        "confirmationURL": null
    },
    "67890": {
        "id": 140938,
        "folderId": "18",
        "contactId": "0",
        "firstName": "redacted",
        "lastName": "redacted",
        "email": "redacted",
        "gender": null,
        "birthday": "0000-00-00",
        "timestamp": "2021-08-09 20:24:18",
        "templateLang": "de",
        "confirmedTimestamp": "0000-00-00 00:00:00",
        "confirmationURL": null
    }
}

parsingCode für JSON Reader:

<#assign row = target.addRow()>

<#list json as key, value>
  <#assign row = target.addRow()>
  ${row.addCol("customerid", key)}
  ${addColumns(row, value, "data_")}
</#list>

Ergebnis:

Weitere Informationen und Besonderheiten

Die Schwierigkeit war, dass dieses JSON-Objekt keine Liste (Array) ist, sondern eine Map aus Key-Value-Paaren ist.
Mit <#list json as key, value> kann man über die einzelnen Key-Value-Paare dieser Map iterieren und auf den Key und den Value zugreifen. 
Dies ist die Freemarker-Schreibweise für Key-Value-Paare einer Map / Hashopen in new window.

Mehr zum Thema XML, JSON und API-Anbindungen

Siehe API Connector Tools