The XML-INTO opcode parses an XML document string and populates a qualified data structure by matching XML element names to subfield names. The %XML built-in function provides the document source and parsing options.

dcl-ds myData qualified;
  name varchar(30);
  city varchar(20);
  state char(2);
end-ds;

xml-into myData %xml(xmlString : 'case=any');

Each option in the %XML string is a single name=value token, and options are separated by spaces. Option names are case-insensitive. Spaces around = are not allowed - write allowmissing=yes, not allowmissing = yes. A spaced token is invalid syntax: in a literal options string it is rejected at compile time (TRN2401), and in a variable options string it fails at run time with status 00352. Supported subfield types: VARCHAR, CHAR, PACKED, ZONED, INT, UNS, FLOAT (4- and 8-byte), DATE, TIME, TIMESTAMP, and indicator (IND/N). A numeric value mapped into a FLOAT subfield is parsed to floating point (following the %FLOAT conversion rules). A value mapped into an indicator subfield sets it on or off: 1 (or a JSON true) sets the indicator *ON, and any other value (including 0 and a JSON false) sets it *OFF.

Triton RPG recognizes the %XML options below. Any other option name is rejected rather than silently ignored, since ignoring one would change how the document is parsed without telling you.

Option Values Purpose
case lower (default), upper, any, convert Element-name matching (see below).
trim all (default), none Whitespace handling for character targets.
allowmissing no (default), yes Tolerate a subfield with no matching element.
allowextra no (default), yes Tolerate an element with no matching subfield.
path slash-separated element path Locate the elements to map (array targets).
countprefix name prefix Name count subfields.
doc string (default), file Treat the first operand as the document text or an IFS file path.
ns remove Strip namespace prefixes from element names before matching.
nsprefix subfield name Capture a stripped namespace prefix into the named subfield.
datasubf subfield name Name the subfield that receives an element's own text when the element also has attributes.
ccsid ucs2, job, jobrun Parser CCSID (see doc=file). A numeric CCSID is invalid for %XML and is rejected as TRN2401.

When the options string is a literal, an unknown option name or an invalid value is rejected at compile time as TRN2401. When the options string is a variable (so the compiler cannot see its contents), the same validation runs at run time instead: an invalid or unknown option fails the operation with status 00352 (a distinct status from the 00353 reported for an element/subfield mismatch), trappable by MONITOR or the (E) extender.

case option - element-name matching

The case option controls how XML element names are matched against subfield names. RPG names are compared in upper case, so a document name is reduced to upper case before matching. The default is case=lower, which is case-discriminating, not case-insensitive:

case value Behaviour
lower (default) Document names are expected all lower case. A lowercase tag matches; a tag with any uppercase letter does not match.
upper Document names are expected all upper case. An uppercase tag matches; a tag with any lowercase letter does not.
any Document names are upper-cased before comparison - effectively case-insensitive.
convert Like any, but the name is first folded into a valid RPG name (characters illegal in an RPG name, such as a hyphen, become an underscore) before matching.

Because the default is case=lower, mixed-case or uppercase tags do not match a data structure that uses the default. The unmatched tag is treated as a surplus element and the subfield it failed to match as missing, so under the strict default the operation fails with status 00353. Add case=any to match regardless of case:

// '<myData><Name>...' fails under the default; matches with case=any
xml-into myData %xml(xmlString : 'case=any');

An invalid case value (anything other than lower, upper, any, or convert) is rejected at compile time as TRN2401.

allowmissing and allowextra options

By default XML-INTO is strict (allowmissing=no allowextra=no):

Relax either check through the %XML options string:

xml-into myData %xml(xmlString : 'case=any allowmissing=yes allowextra=yes');

trim option - whitespace handling

The trim option controls how whitespace inside an element's (or attribute's) character content is treated before the value is assigned to a CHAR or VARCHAR target. The default is trim=all.

trim value Behaviour
all (default) Leading and trailing whitespace is stripped, and every interior run of whitespace (spaces, tabs, CR, LF) collapses to a single blank.
none The element's character content is assigned verbatim, subject only to the target field length.
// Element text '  hello   world  '
xml-into d1 %xml(x : 'case=any');            // trim=all  -> 'hello world'
xml-into d1 %xml(x : 'case=any trim=none');  // verbatim  -> '  hello   world  '

Whitespace between elements is always ignored regardless of trim. Trimming applies to character/VARCHAR targets; numeric, date, and time targets are always effectively trimmed. The trim option behaves identically for a data-structure receiver and a standalone DIM array receiver. An invalid trim value (anything other than all or none) is rejected at compile time as TRN2401.

countprefix option - count subfields

countprefix=PREFIX names a set of count subfields inside the receiver data structure. A subfield whose name starts with PREFIX is not mapped from the document; instead it receives the count of its counted sibling - the subfield whose name is the count field's name with the prefix removed. For countprefix=num_, the subfield num_city is the count of the city subfield.

For a scalar counted element the count is 1 when the element is present and 0 when it is absent. countprefix also implies allowmissing for each counted subfield: an absent counted element is never an error - its count field records the absence (0) and the counted subfield keeps its previous value.

dcl-ds doc qualified;
  num_name int(10);     // count of <name>: 1 if present, 0 if absent
  name     char(10);
  num_city packed(5:0); // count of <city>
  city     char(10);
end-ds;

// '<doc><name>Grace</name></doc>'  (no <city> element)
xml-into doc %xml(x : 'case=any countprefix=num_');
// doc.num_name = 1, doc.name = 'Grace'
// doc.num_city = 0, doc.city unchanged (countprefix implies allowmissing)

countprefix is honoured when the %XML options string is a literal. The count fields are also excluded from the allowextra surplus-element check, since they are never mapped.

Array targets

The receiver may be a standalone DIM array. Repeated XML elements fill successive array elements; the count actually present is capped at the array dimension, and positions beyond the count are left unchanged. Locate the repeated elements with the path option (slash-separated element names from the document root down to the repeated element):

dcl-s lines char(10) dim(5);
// '<lines><lines>AAA</lines><lines>BBB</lines><lines>CCC</lines></lines>'
xml-into lines %xml(xmlString : 'case=any path=lines/lines');
// lines(1)='AAA', lines(2)='BBB', lines(3)='CCC'; lines(4)/lines(5) unchanged

When no path option is given, the repeated elements are the direct children of the document root whose name matches the array variable.

Nested data structures

A subfield of the receiver may itself be a data structure, declared with LIKEDS of a template. Nested XML elements then map to qualified subfield names: a child element of the element that matched a data-structure subfield is matched against that nested structure's subfields, to any depth.

dcl-ds addrT qualified template;
  city char(20);
  state char(2);
end-ds;

dcl-ds personT qualified template;
  name char(20);
  addr likeds(addrT);     // subfield that is itself a data structure
end-ds;

dcl-ds root qualified;
  person likeds(personT);
end-ds;

// '<root><person><name>John Smith</name>
//   <addr><city>New York</city><state>NY</state></addr></person></root>'
xml-into root %xml(xmlString : 'case=any');
// root.person.name       = 'John Smith'
// root.person.addr.city  = 'New York'
// root.person.addr.state = 'NY'

The receiver and every nested structure must be QUALIFIED (a LIKEDS subfield is automatically qualified). Leaf subfields are accessed with full dot notation (root.person.addr.city). The case, trim, allowmissing, and allowextra options all apply to the leaf elements at every level: under the strict default a leaf with no matching element raises status 00353, and allowmissing=yes / allowextra=yes relax the missing-leaf and surplus-leaf checks respectively.

doc=file - read the document from an IFS file

With doc=file, the first %XML operand is the path of an Integrated File System stream file rather than the XML text itself. The file is read and parsed as if its contents had been supplied directly:

dcl-s path varchar(200);
path = '/home/me/customer.xml';
xml-into myData %xml(path : 'doc=file case=any');

The file is decoded as UTF-8; a leading byte-order mark is ignored. A missing or unreadable file fails the operation with status 00353.

The ccsid option (ucs2, job, or jobrun) selects the CCSID the parser works in; it does not change how the stream file is decoded. A numeric CCSID such as ccsid=37 is invalid for %XML and is rejected as TRN2401. Because a doc=file stream file is decoded as UTF-8, supply the document file in UTF-8.

Document type declarations (DTDs) and external entities

XML-INTO and %XML tolerate a <!DOCTYPE …> declaration but do not process the DTD, matching IBM i. A general entity reference in the document is left as its literal reference text - a field receiving &name; gets the five (or more) characters of the reference, never the entity's declared replacement and never an error. The five predefined entities (&amp;, &lt;, &gt;, &quot;, &apos;) and numeric character references (&#nn;, &#xhh;) are still resolved as usual.

Because entities are never expanded, a declared or external entity is delivered as literal reference text rather than resolved, so a document cannot use an entity to read a local file, reach a remote URL, or expand into an outsized value. A JVM whose XML configuration has been altered may behave differently; verify your JVM's XML settings before processing untrusted documents. Well-formed documents without a DTD - the normal case for XML-INTO input - are unaffected.

%HANDLER - event-driven handler form

The event-driven form, XML-INTO %HANDLER(handlerProc : commArea) %XML(doc : options), processes a document in pieces by calling a handler procedure repeatedly instead of populating a single receiver. It is used for documents with many repeated elements.

The handler procedure has a fixed interface: it returns a 4-byte integer and takes three parameters, in order:

dcl-pr HandleCompany int(10);
  commArea     likeds(myCommArea);          // 1: communication area, by reference
  company      char(32) dim(4) const;       // 2: array of mapped elements, CONST
  numElements  int(10) value;               // 3: count of valid elements this call
end-pr;

The handler returns 0 to continue to the next batch; returning a non-zero value stops the parse. The path option is required with this form - it identifies the repeated element whose occurrences are batched into the array:

dcl-s total int(10) inz(0);
xml-into %handler(HandleCompany : total)
         %xml(xmlDoc : 'path=Customers/Company case=any');

dcl-proc HandleCompany;
  dcl-pi *n int(10);
    commArea    int(10);
    company     char(32) dim(4) const;
    numElements int(10) value;
  end-pi;
  dcl-s i int(10);
  for i = 1 to numElements;
    // ... process company(i) ...
  endfor;
  commArea += numElements;   // accumulate into the by-reference comm area
  return 0;                  // continue
end-proc;

For seven <Company> elements and a DIM(4) array, the handler is called twice - first with four elements, then with three - and the communication area accumulates to seven.

Requirements: the handler procedure must declare exactly three parameters - a communication area, a DIM array (scalar or LIKEDS), and a count (TRN3014) - > and the path option is required (TRN3016). Both scalar and LIKEDS array elements are supported; a LIKEDS handler receives an RpgDataStructure array whose elements are populated from each repeated XML element's subfields.