Runtime verified Differs from official docs Test scripts included

Syntax

BuildRowSetFromXML(xmlData, xpathExpression, returnEmptyOnError)  →  rowset
3 arguments — exactly

Parameters

Name Type Required Description
xmlData string Yes The XML payload to parse
xpathExpression string Yes XPath expression selecting the nodes to turn into rows
returnEmptyOnError boolean | number Yes Pass 1 or true for an empty rowset when the payload or path cannot be parsed

Example

%%[
  VAR @xml, @rows
  SET @xml = '<root><Flight origin="IND">100.00</Flight></root>'
  SET @rows = BuildRowSetFromXML(@xml, "//Flight", 1)
]%%
Rows: %%=v(RowCount(@rows))=%% origin: %%=v(Field(Row(@rows, 1), "origin_att", 0))=%%

Renders Rows: 1 origin: IND.

Because untrusted input yields an empty rowset rather than a value you can test, guard the read with RowCount, and use the three-argument Field form for attribute columns that some nodes may not carry:

%%[
  VAR @rows, @row
  SET @rows = BuildRowSetFromXML(@payload, "//Flight", 1)
  IF RowCount(@rows) > 0 THEN
    SET @row = Row(@rows, 1)
]%%
    Price: %%=v(Field(@row, "Value", 0))=%% carrier: %%=v(Field(@row, "carrier_att", 0))=%%
%%[ ENDIF ]%%

Return value

rowset — one row per node the XPath expression matched.

There is no closed set of sentinel values: an unparsable payload, an empty string, an unset variable, an empty root element and an XPath that matches nothing all produce a rowset of zero rows when the third argument is 1, so RowCount is the only value worth branching on.

Behaviour

An XPath selecting sibling elements gives one row per matched element. //Flight over three Flight elements produced three rows.

Each row carries a Value column and an Xml column. Value holds the element’s own text and Xml holds its inner markup — for a plain element both read the same (200.00), but where the element has a child node Value read 500 while Xml also carried the child markup.

Every attribute seen on any matched node becomes a <name>_att column. The second flight read IND from origin_att and UAL from carrier_att. Lookup is case-insensitive: Origin_att returned the same value as origin_att. A node missing an attribute its siblings carry reads empty in that column rather than aborting — use the three-argument Field form for it.

Ordinals run Value, then Xml, then the attribute columns. On the second flight, ordinals 15 gave 200.00, 200.00, IND, LAX and UAL.

An XPath selecting an attribute node gives one row. /root/Flight[1]/@origin produced a single row whose Value and ordinal 1 both read IND.

Rowsets are read 1-based. Row(rowset, 0) and Field(row, 0) both abort the page, and the two-argument Field form on a missing column aborts too — prefer the three-argument form with 0 for anything that may be absent. RowCount never aborts and returns 0 for an empty rowset.

Either capitalisation resolves to the same function. BuildRowsetFromXML with a lowercase s produced identical results.

The third argument is inverted

Call Result
BuildRowSetFromXML("<root><a>1</a>", "//a", 1) rowset with zero rows
BuildRowSetFromXML("<root><a>1</a>", "//a", 0) HTTP 422, page aborted

The boolean literal true behaves exactly like the number 1. Read the argument as return empty on error: pass 1 (or true) for any payload you do not control, then branch on RowCount. The full finding is on Differs from official docs, and the JSON sibling behaves identically.

Show test script
%%[
  VAR @b, @xml, @unset, @rows, @r2, @r3
  SET @b = RequestParameter("b")
  SET @xml = '<root><Flight origin="IND" dest="NYC" carrier="UAL">100.00</Flight><Flight origin="IND" dest="LAX" carrier="UAL">200.00</Flight><Flight origin="IND" dest="SEA">500<PerBagSurcharge>25</PerBagSurcharge></Flight></root>'

  /* control branch: proves the page itself renders */
  IF @b == "zzz" THEN
    OutputLine(Concat("CTRL=[alive]"))
  ENDIF

  /* one row per matched element, Value and Xml columns */
  IF @b == "safe" THEN
    SET @rows = BuildRowSetFromXML(@xml, "//Flight", 1)
    SET @r2 = Row(@rows, 2)
    OutputLine(Concat("x1 rc=[", RowCount(@rows), "]"))
    OutputLine(Concat("x1 r2 Value=[", Field(@r2, "Value", 0), "] Xml=[", Field(@r2, "Xml", 0), "]"))
  ENDIF

  /* attribute columns, case-insensitive lookup, ordinal order */
  IF @b == "attributes" THEN
    SET @rows = BuildRowSetFromXML(@xml, "//Flight", 1)
    SET @r2 = Row(@rows, 2)
    OutputLine(Concat("x2 origin_att=[", Field(@r2, "origin_att", 0), "] Origin_att=[", Field(@r2, "Origin_att", 0), "] carrier_att=[", Field(@r2, "carrier_att", 0), "]"))
    OutputLine(Concat("x2 ord1=[", Field(@r2, 1), "] ord2=[", Field(@r2, 2), "] ord3=[", Field(@r2, 3), "] ord4=[", Field(@r2, 4), "] ord5=[", Field(@r2, 5), "]"))
  ENDIF

  /* a node lacking an attribute the siblings carry, plus a child element */
  IF @b == "missingattr" THEN
    SET @rows = BuildRowSetFromXML(@xml, "//Flight", 1)
    SET @r3 = Row(@rows, 3)
    OutputLine(Concat("x3 r3 Value=[", Field(@r3, "Value", 0), "] Xml=[", Field(@r3, "Xml", 0), "]"))
    OutputLine(Concat("x3 r3 carrier_att=[", Field(@r3, "carrier_att", 0), "]"))
  ENDIF

  /* an XPath selecting an attribute node */
  IF @b == "attrpath" THEN
    SET @rows = BuildRowSetFromXML(@xml, "/root/Flight[1]/@origin", 1)
    OutputLine(Concat("x4 attr-xpath rc=[", RowCount(@rows), "] v=[", Field(Row(@rows, 1), "Value", 0), "] ord1=[", Field(Row(@rows, 1), 1), "]"))
  ENDIF

  /* everything that yields zero rows while the third argument is 1 */
  IF @b == "empties" THEN
    OutputLine(Concat("x6 malformed-flag1 rc=[", RowCount(BuildRowSetFromXML("<root><a>1</a>", "//a", 1)), "]"))
    OutputLine(Concat("x8 emptystring-flag1 rc=[", RowCount(BuildRowSetFromXML("", "//Flight", 1)), "]"))
    OutputLine(Concat("x5 emptyroot rc=[", RowCount(BuildRowSetFromXML("<root></root>", "//Flight", 1)), "]"))
    OutputLine(Concat("x13 unsetvar rc=[", RowCount(BuildRowSetFromXML(@unset, "//Flight", 1)), "]"))
    OutputLine(Concat("x9 nomatch rc=[", RowCount(BuildRowSetFromXML(@xml, "//NoSuchNode", 1)), "]"))
  ENDIF

  /* the boolean literal true behaves like the number 1 */
  IF @b == "boolflag" THEN
    OutputLine(Concat("x12 boolliteral rc=[", RowCount(BuildRowSetFromXML(@xml, "//Flight", true)), "]"))
  ENDIF

  /* the alternate capitalisation resolves to the same function */
  IF @b == "casing" THEN
    OutputLine(Concat("x11 lowercase-s-casing rc=[", RowCount(BuildRowsetFromXML(@xml, "//Flight", 1)), "]"))
  ENDIF

  /* fetch ?b=abortflag0 - HTTP 422, nothing renders */
  IF @b == "abortflag0" THEN
    OutputLine(Concat("--- x7 start ---"))
    SET @rows = BuildRowSetFromXML("<root><a>1</a>", "//a", 0)
    OutputLine(Concat("x7 rc=[", RowCount(@rows), "]"))
  ENDIF
]%%

Availability

Platform Available
Marketing Cloud Engagement Yes
Marketing Cloud Next No

See also