XML
You can automatically include dynamically-generated content into your campaign by specifying a URL to a remote XML file. The contents of this file will automatically be made available to your email template where you can access it using XPath expressions.
When you provide the remote XML URL:
-
Ensure the URL is a valid HTTP or HTTPS URL.
Tip: it is possible to use 3rd party software to merge multiple feeds together. - Make sure your file is smaller than 10 MB. Anything larger than this will cause issues within the platform.
-
Make sure your XML file doesn’t specify a namespace with an xmlns declaration. This isn’t supported by our system for security reasons.
Processing XML
These are the methods that can be called on xml nodes and their function.
Method | Function |
---|---|
findnodes | Returns a node (or node list) |
findvalue |
Returns a string value Tip: If findvalue represents several tags, then the output will be text formatted with space. It is therefore advisable to only use findvalue for single tags. |
find | Does the 'right thing' and returns either text or a node |
getAttribute | Gets the attribute string |
To process the XML you need an Xpath expression. The three main methods are expanded upon here with examples.
findnodes
Accepts and XPath expression and returns a node (or node list).
Example, to return all book nodes:
[* books = remote.xml.findnodes('//book') *]
Returns catalog:
[* content = remote.xml.findnodes('/catalog') *]
findvalue
Accepts and XPath expression and returns a string value. Best used for single tags.
Example, to return a string value:
[* book = remote.xml.findvalue("//book[@id='bk002']") *]
[* title = book.findvalue('title') *]
Returns:
[* remote.xml.findvalue('/book') | html *]
Note: The findvalue code is case sensitive, in the above example it would be looking for 'title' within the feed, if your feed uses 'Title' then this won't work. You must use the exact name within the findvalue code.
find
Does the "right thing" and returns either text or a node.
Example, to return all book nodes:
[* remote.xml.find('//book') *]
Example XML
<?xml version="1.0"?>
<catalog>
<book id="bk001">
<author>Tolstoy, Leo</author>
<title>War and Peace</title>
<genre>Classics</genre>
<price>11.99</price>
</book>
<book id="bk002">
<author>Murakami, Haruki</author>
<title>Norwegian Woods</title>
<genre>Bildungsroman</genre>
<price>8.99</price>
</book>
<book id="bk003">
<author>Nesbo, Jo</author>
<title>The Bat</title>
<genre>Crime</genre>
<price>6.99</price>
</book>
</catalog>
Iterating
You can use a FOREACH loop to act upon each item within your data, as well as use NEXT and LAST as flow control for the loop.
FOREACH
Example of looping over nodes, to return chosen tags:
[* books = remote.xml.findnodes('//book') *]
[* FOREACH book IN books *]
[* title = book.findvalue('title') *]
[* price = book.findvalue('price') *]
[* id = book.getAttribute('id') *]
<h1>[* title *]</h1>
<p> [* price *], [* id *]</p>
[* END *]
NEXT
Example of using NEXT within a FOREACH loop, to skip a record if a condition is met:
[* books = remote.xml.findnodes('//book') *]
[* FOREACH book IN books *]
[* title = book.findvalue('title') *]
[* price = book.findvalue('price') *]
[* genre = book.findvalue('genre') *]
[* id = book.getAttribute('id') *]
[* NEXT IF genre == 'Crime' *]
<h1>[* title *]</h1>
<p> [* price *], [* id *]</p>
[* END *]
LAST
Example of using LAST within a FOREACH loop, to escape FOREACH loop when condition is met:
[* books = remote.xml.findnodes('//book') *]
[* FOREACH book IN books *]
[* title = book.findvalue('title') *]
[* price = book.findvalue('price') *]
[* genre = book.findvalue('genre') *]
[* id = book.getAttribute('id') *]
[* LAST IF genre == 'Bildungsroman' *]
<h1>[* title *]</h1>
<p> [* price *], [* id *]</p>
[* END *]
Using persist to speed up complex templates
The persist mechanism can be used to store the result of complex processing between iterations, which reduces repeated work and speeds up launches. This can significantly improve performance for some campaign types, most notably those that refer to external XML files, as it avoids the need to run potentially slow XPATH queries to find the content from the XML feed for every contact on the list.
Typically, the processing work is placed at the top of the template, so it will run if there’s no persisted data. Here’s an example that extracts articles from an RSS feed:
[*
# Create articles array if we don't already have it
UNLESS persist.news_articles;
# Declare an empty array to store articles
news_articles = [];
# Iterate throught items in the RSS feed
FOREACH item IN remote.xml.findnodes('//rss/channel/item');
# Create a hash for the values required for each article
article = {
title = item.findnodes('title').textContent,
description = item.findnodes('description').textContent,
url = item.findnodes('link').textContent,
pubdate = item.findnodes('pubDate').textContent,
};
# Add article to array
news_articles.push(article);
END;
# Store articles array for future iterations
persist.news_articles = news_articles;
END;
*]
Then later in the template, to output the articles, you can iterate the array created earlier:
[*FOREACH article IN persist.news_articles*]
<h1><a href="[*article.url | html*]">[*article.title | html*]</a></h1>
<p>[*article.description | html*]</p>
<p><a href="[*article.url | html*]">Link</a></p>
<p>[*article.pubdate | html*]</p><br><br><br>
[*END*]
Caution: Be careful to not include any contact-specific or PII (personally identifiable information) data in any of the persisted data structures, as this may lead to data being sent to the wrong contact.