Docs / Language & functions
a9script authoring pack — structured data functions
The structured data functions, with their arguments and a line of real script each. Called by bare name; nothing is imported. A call marked pauses does real work outside the script: the run is saved, and continues on the next line with the result.
get(data, path) — immediate
Reads a value out of a shape by a path, without failing when part of the path is not there. get(order, "customer.address.city") answers the city, or nothing at all when the order carries no address — where order.customer.address.city would end the run.
data(object | array) — The shape to read from. Anything else — a number, or nothing at all — simply answers nothing.path(string) — Where to look: names joined by dots, positions in square brackets, as initems[0].sku. A name addresses a key of an object and[0]addresses an entry of an array. A path that is not written that way is refused, because no data can make it right.
Answers: The value at that place, or nothing when any step of the path is missing.
const city = get(input.event.body, "shipping.address.city");
log(city === undefined ? "no city was given" : city);
set(data, path, value) — immediate
Answers a copy of a shape with one place changed. What you pass in is never touched — which is how a script changes something in the payload it was handed, since that payload is read-only.
data(object | array) — The shape to derive from. Nothing at all is allowed too, and the answer is then just the shape the path describes.path(string) — Where to write — the same pathsgetreads. A step that does not exist yet is created: a name makes an object, a position makes an array. A step that holds a plain value, or one whose kind disagrees with the path, is refused by name rather than overwritten.value(any) — What to put there.
Answers: A new object or array. Everything the path did not touch is the very same value it was, not a copy of it, so changing one field of a large payload stays cheap. That sharing is also why a branch the answer still shares with the trigger’s payload cannot be assigned into — reach it with another set instead.
const enriched = set(input.event.body, "status", "received");
log("status is now " + enriched.status);
jsonPath(data, expression) — immediate
Collects every value a shape holds at some place, as a list — for when the interesting values sit at a depth you would otherwise write a loop per level to reach.
data(object | array) — The shape to search.expression(string) — Starts at$, the whole value..nameand['name']step into a key,[0]into a position,[*]takes every entry, and..namefinds that name at any depth. Anything else is refused rather than quietly matching nothing.
Answers: A list of the matching values, at most 10000 of them — an expression selecting more is refused rather than answered in part, so narrow the path or read the shape in smaller pieces. A descent answers shallow-first: a value’s own match comes back before matches found deeper inside it. A branch the shape holds in two places matches once per place. Nothing matched answers an empty list.
const skus = jsonPath(input.event.body, "$.items[*].sku");
log("the order names " + skus.length + " products");
parseCsv(text, options?) — pauses
Reads CSV text into a list of rows. Quoted fields, embedded commas, quotes and line breaks are all handled — this is a real CSV reader, not a split on commas.
text(string) — The CSV to read.options(object, optional) —{ delimiter, header }.delimiterdefaults to a comma;headerdefaults to true, meaning the first line NAMES the columns and each row comes back as an object. Set it to false to get each row as a list of cells instead. Any other option is refused by name.
Answers: A list of rows: objects keyed by column name, or lists of cells when there is no header. Every cell is TEXT — a CSV says nothing about types, and reading 01234 as a number would lose the leading zero of a postcode or an SKU. Use Number(cell) where you want a number.
const rows = parseCsv("sku,qty\nA-1,2\nB-2,1\n");
log("the file holds " + rows.length + " rows");
log("first SKU: " + rows[0].sku);
toCsv(rows, options?) — pauses
Writes a list of rows as CSV text, quoting whatever needs quoting. A cell that begins with =, +, - or @ is written as text, so a spreadsheet opening the file cannot be made to run it as a formula.
rows(array) — The rows: objects keyed by column name, or lists of cells. Text, numbers and booleans are all written; a missing value or null writes an empty cell.options(object, optional) —{ delimiter, header, columns, guardFormulas }.columnsnames the columns to write and their order — otherwise the keys of the first row are used, so give it when later rows may carry keys the first does not.guardFormulasdefaults to true and is the protection described above; turn it off only when the file is not going to be opened in a spreadsheet.
Answers: The CSV text, one line per row, ending in a line break.
const text = toCsv([
{ sku: "A-1", qty: 2 },
{ sku: "B-2", qty: 1 }
]);
log(text);
parseXml(text, options?) — pauses
Reads XML into an object tree. An element becomes a key, a repeated element becomes a list, and an attribute becomes a key with @ in front of it — so an attribute and a child element of the same name never collide.
text(string) — The XML to read. Text that is not well-formed is refused rather than half-read.options(object, optional) —{ attributePrefix, textKey, lists }.attributePrefixdefaults to@;textKeydefaults to#textand is where an element’s own text goes when it also carries attributes.listsnames the elements that are ALWAYS a list —["line"], or["order.line"]where the same name means something else at another depth — which is how a script that walks repeated data stops depending on how many the sender sent. Any other option is refused by name.
Answers: The tree. Every value is TEXT, for the reason parseCsv gives: XML carries no types, and reading 1.50 as a number would quietly drop the cents. One thing to watch: an element that appears ONCE is its value alone, not a one-entry list — so a document that happens to carry a single entry hands you its first character where you expected an entry. Name that element in lists and it is a list however many times it appears; without it, check Array.isArray first.
const tree = parseXml("<order id=\"o-1\"><line>A-1</line></order>", { lists: ["line"] });
log("order " + tree.order["@id"]);
log("first line: " + tree.order.line[0]);
toXml(value, options?) — pauses
Writes an object tree as XML. A key with @ in front of it becomes an attribute, a list becomes a repeated element, and everything else becomes an element.
value(object) — The tree to write.options(object, optional) —{ attributePrefix, textKey, indent }— the first two asparseXmltakes them, so a tree read with one prefix can be written back with the same one.indentdefaults to false; set it to true for XML a person is going to read.
Answers: The XML text.
const xml = toXml({
order: { "@id": input.event.body.id, total: "42.00" }
});
log(xml);