JSON-Template mit Liquid-Syntax
Das JSON, das dein AssetFeed ausliefert, bestimmst du selbst: Ausgaben, Bedingungen und Schleifen in Liquid-Syntax, alle verfügbaren Variablen und die Funktionen für Text und Listen.
Jeder Listen-Endpunkt und jeder Einzel-Asset-Endpunkt im Modus Metadaten als JSON hat ein eigenes JSON-Template (Liquid). Du bearbeitest es im Dialog AssetFeed anpassen in einem Code-Editor mit Syntaxhervorhebung. Das Template ist ein JSON-Dokument, in das du mit der Liquid-Syntax Werte einsetzt, Bedingungen prüfst und über Listen läufst.
Beim Speichern prüft VAULO die Syntax des Templates. Ob alle verwendeten Funktionen existieren, zeigt sich erst beim Abruf: Ein Template mit einer unbekannten Funktion antwortet mit dem Statuscode 500. Öffne die Feed-URL deshalb nach jeder Änderung einmal im Browser.
Grundlagen
Zwei Schreibweisen reichen für fast alles:
{{ … }}gibt einen Wert aus, zum Beispiel{{ feed.title }}.{% … %}steuert den Ablauf: Bedingungen, Schleifen, Variablen.
{
"title": "{{ feed.title }}",
"count": {{ page.totalItems }},
"assets": [
{% for asset in assets %}
{
"id": "{{ asset.id }}",
"isImage": {% if asset.fileType == "Image" %}true{% else %}false{% endif %}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
}Bedingungen
{% if asset.iptc.title %}"{{ asset.iptc.title }}"{% else %}null{% endif %}
{% if page.hasNext and page.index == 1 %} … {% endif %}
{% unless forloop.last %},{% endunless %}
{% if asset.fileType == "Video" %} … {% elsif asset.fileType == "Image" %} … {% else %} … {% endif %}Vergleiche schreibst du mit ==, !=, <, <=, > und >=, verknüpft mit and und or. Ein Wert gilt als nicht erfüllt, wenn er fehlt oder null ist. Ein leerer Text "" gilt dagegen als erfüllt – prüfe Texte deshalb mit string.size oder ersetze sie mit object.default (siehe Funktionen).
Schleifen
{% for asset in assets %}
{{ forloop.index }} ← 0, 1, 2, … (beginnt bei 0)
{{ forloop.first }} ← true beim ersten Eintrag
{{ forloop.last }} ← true beim letzten Eintrag
{% endfor %}
{% for keyword in asset.iptc.keywords limit: 5 %} … {% endfor %}Variablen, Kommentare und Rohtext
{% assign kind = asset.fileType %}
{% capture label %}{{ asset.filename }} ({{ kind }}){% endcapture %}
"label": "{{ label }}",
{% comment %} Wird nicht ausgegeben {% endcomment %}
{% raw %}{{ bleibt wörtlich stehen }}{% endraw %}Mit {{- und -%} beziehungsweise {%- und -%} entfernst du Leerzeichen und Zeilenumbrüche rund um einen Tag.
Funktionen
Die klassischen Liquid-Filter wie | upcase, | date: "%Y" oder | default: "…" stehen nicht zur Verfügung. VAULO nutzt die Funktionen der Template-Engine Scriban: Der Name enthält einen Punkt, Argumente folgen durch Leerzeichen getrennt – ohne Doppelpunkt und ohne Komma.
{{ asset.filename | string.upcase }} → LANDSCAPE10.JPG
{{ asset.filename | string.downcase }} → landscape10.jpg
{{ asset.filename | string.slice 0 9 }} → Landscape (ab Position 0, 9 Zeichen)
{{ asset.iptc.description | string.truncate 20 }} → Ein sehr langer B...
{{ asset.filename | string.replace ".jpg" "" }} → Landscape10
{{ asset.filename | string.size }} → 15
{{ asset.filename | string.contains ".jpg" }} → true
{{ asset.filename | string.split "." | array.last }} → jpg
{{ asset.iptc.keywords | array.join ", " }} → Berlin, Sommer, Fest
{{ asset.iptc.keywords | array.size }} → 3
{{ asset.iptc.keywords | array.first }} → Berlin
{{ asset.iptc.title | object.default "Ohne Titel" }} → Ersatzwert, wenn der Wert fehlt oder leer ist
{{ asset.updatedAt | date.parse | date.to_string "%d.%m.%Y" }} → 09.09.2026Funktionen lassen sich verketten: {{ asset.iptc.keywords | array.join ", " | string.upcase }}. Alles zu Datum und Uhrzeit steht unter Datum und Uhrzeit formatieren.
Variablen
feed
In jedem Template verfügbar.
| Variable | Inhalt |
|---|---|
feed.id | Die ID des Feeds |
feed.title | Der Titel des Feeds |
feed.url | Die Feed-URL |
feed.generatedAt | Zeitpunkt der Antwort, ISO 8601 in UTC |
assets und asset
Ein Listen-Endpunkt erhält die Liste assets – eine Seite der veröffentlichten Dateien. Ein Einzel-Asset-Endpunkt im Modus Metadaten als JSON erhält die eine Datei als asset. Jedes Asset hat diese Felder:
| Variable | Inhalt |
|---|---|
asset.id | Die ID der Datei |
asset.assetId | Die Asset-ID der Datei aus dem Data Studio, zum Beispiel 000123. Leer, wenn der Datei noch keine Asset-ID zugewiesen ist – {% if asset.assetId %} prüft das |
asset.filename | Dateiname mit Endung |
asset.contentType | MIME-Typ, zum Beispiel image/jpeg |
asset.fileType | Image, Video, Audio, Pdf, MicrosoftOffice, Text, Markdown, Photoshop, Illustrator oder Unknown |
asset.updatedAt | Letzte Änderung, ISO 8601 in UTC |
asset.url | Temporäre Direkt-URL auf die Datei in der angeforderten Größe |
asset.iptc | IPTC-Metadaten der aktiven Version (siehe unten) |
asset.exif | EXIF-Daten der Datei (siehe unten) |
asset.tenantProps | Benutzerdefinierte Metadaten (siehe unten) |
asset.iptc
Alle Felder sind Texte; fehlende Werte sind leer. asset.iptc.keywords ist eine Liste.
| Gruppe | Felder |
|---|---|
| Beschreibung | title, headline, description, descriptionWriter, keywords, personInImage |
| Ort | city, location, sublocation, state, country, countryCode |
| Urheber und Rechte | creator, creatorsJobTitle, copyrightNotice, credit, source, webStatementOfRights, rightsUsageTerms |
| Verwaltung | subjectCode, jobId, instructions, editStatus, category, supplementalCategories |
| Model und Property | modelAge, additionalModelInformation, modelReleaseId, propertyReleaseId |
| Kontakt | creatorContactExtendedAddress, creatorContactCity, creatorContactRegion, creatorContactPostalCode, creatorContactCountry, creatorContactWorkEmail, creatorContactWorkTelephone, creatorContactWorkUrl |
| Bildlieferant | imageSupplierId, imageSupplierName, imageSupplierImageId |
| Bildanalyse | blurhash, dominantColor, isGrayscale (true oder false) |
asset.exif
| Variable | Inhalt |
|---|---|
exif.dateTime | Aufnahmezeitpunkt als ISO 8601 oder null |
exif.fNumber, exif.exposureTime, exif.isoSpeedRatings, exif.focalLength, exif.orientation | Aufnahmewerte |
exif.model, exif.lensModel | Kamera und Objektiv |
exif.copyright, exif.colorProfile, exif.colorSpace | Weitere Texte |
exif.gps.latitude, exif.gps.longitude, exif.gps.altitude | Koordinaten als Zahl oder null |
exif.gps.timestamp | Zeitstempel als ISO 8601 oder null |
exif.gps.imgDirection, exif.gps.imgDirectionRef, exif.gps.mapDatum, exif.gps.processingMethod, exif.gps.hPositioningError | Weitere GPS-Angaben |
asset.tenantProps
Die Werte deiner benutzerdefinierten Metadaten aus dem Data Studio, ansprechbar über den Slug der Eigenschaft oder ihre Slot-ID, zum Beispiel asset.tenantProps.artikelnummer. Texte kommen als Text, Mehrfachauswahlen als Liste, Zahlen und Ja/Nein-Werte unverändert. Nicht gesetzte Eigenschaften sind leer.
page
Nur in Listen-Endpunkten.
| Variable | Inhalt |
|---|---|
page.index | Aktuelle Seite, beginnend bei 1 |
page.size | Assets pro Seite |
page.totalItems | Gesamtzahl der Assets im Feed |
page.totalPages | Anzahl der Seiten |
page.hasNext | true, wenn es eine weitere Seite gibt |
page.nextUrl | URL der nächsten Seite oder null |
page.previousUrl | URL der vorherigen Seite oder null |
Gültiges JSON erzeugen
Das externe System erwartet gültiges JSON. Diese Regeln helfen dir dabei:
- Texte immer in Anführungszeichen setzen:
"{{ asset.filename }}". Alle Textwerte sind bereits für JSON maskiert – Anführungszeichen oder Zeilenumbrüche im Bildtext machen dein JSON nicht kaputt. - Kommas in Schleifen: Nach dem letzten Eintrag darf kein Komma stehen. Schreibe
{% unless forloop.last %},{% endunless %}hinter jedes Element. - Fehlende Werte: Leere Texte werden als
""ausgegeben.page.nextUrl,exif.dateTimeund die GPS-Zahlen liefernnull, wenn sie fehlen – du kannst sie ohne Anführungszeichen ausgeben oder mit{% if %}abfangen. - Listen ausgeben: Baue Arrays mit einer Schleife, zum Beispiel
[{% for k in asset.iptc.keywords %}"{{ k }}"{% unless forloop.last %},{% endunless %}{% endfor %}]. - Ergebnis prüfen: Öffne die Feed-URL im Browser und lass das Ergebnis von einem JSON-Validator prüfen, bevor das externe System darauf zugreift.
Beispiele
Standard-Template eines Listen-Endpunkts
{
"version": 1,
"id": "{{ feed.id }}",
"title": "{{ feed.title }}",
"generatedAt": "{{ feed.generatedAt }}",
"nextPage": {% if page.nextUrl %}"{{ page.nextUrl }}"{% else %}null{% endif %},
"assets": [
{% for asset in assets %}
{
"id": "{{ asset.id }}",
"filename": "{{ asset.filename }}",
"contentType": "{{ asset.contentType }}",
"type": "{{ asset.fileType }}",
"updatedAt": "{{ asset.updatedAt }}",
"url": "{{ asset.url }}"
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
}Standard-Template eines Einzel-Asset-Endpunkts
{
"id": "{{ asset.id }}",
"filename": "{{ asset.filename }}",
"contentType": "{{ asset.contentType }}",
"type": "{{ asset.fileType }}",
"updatedAt": "{{ asset.updatedAt }}",
"url": "{{ asset.url }}"
}Erweitertes Beispiel mit Metadaten
{
"feed": "{{ feed.title }}",
"page": {
"index": {{ page.index }},
"total": {{ page.totalPages }},
"next": {% if page.nextUrl %}"{{ page.nextUrl }}"{% else %}null{% endif %}
},
"items": [
{% for asset in assets %}
{
"id": "{{ asset.id }}",
"assetId": {% if asset.assetId %}"{{ asset.assetId }}"{% else %}null{% endif %},
"name": "{{ asset.filename | string.replace ".jpg" "" }}",
"title": "{{ asset.iptc.title | object.default asset.filename }}",
"caption": "{{ asset.iptc.description | string.truncate 200 }}",
"credit": "{{ asset.iptc.credit }}",
"keywords": [{% for keyword in asset.iptc.keywords %}"{{ keyword }}"{% unless forloop.last %},{% endunless %}{% endfor %}],
"articleNumber": "{{ asset.tenantProps.artikelnummer }}",
"takenOn": {% if asset.exif.dateTime %}"{{ asset.exif.dateTime | date.parse | date.to_string "%Y-%m-%d" }}"{% else %}null{% endif %},
"location": {
"city": "{{ asset.iptc.city }}",
"latitude": {{ asset.exif.gps.latitude }},
"longitude": {{ asset.exif.gps.longitude }}
},
"image": "{{ asset.url }}",
"permalink": "{{ feed.url }}/assets/{{ asset.filename }}"
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
}APIs, Endpunkte und URL-Schema
So baust du die öffentliche Schnittstelle deines AssetFeeds: APIs mit eigener URL-Gültigkeit, Listen- und Einzel-Asset-Endpunkte, Platzhalter im URL-Schema, Antwortmodi und die reservierten Query-Parameter.
Datum und Uhrzeit formatieren
Zeitstempel wie updatedAt kommen als ISO-8601-Wert in UTC an. So gibst du im JSON-Template nur das Datum, nur die Uhrzeit oder ein lokalisiertes Format aus.