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):
- Every subfield of the data structure must have a matching element in the XML document; a missing element raises a runtime error.
- Every element in the XML document must have a matching subfield; a surplus element raises a runtime error.
Relax either check through the %XML options string:
xml-into myData %xml(xmlString : 'case=any allowmissing=yes allowextra=yes');
allowmissing=yes- a subfield with no matching element is left unchanged (it keeps whatever value it held before the operation).allowextra=yes- an element with no matching subfield is silently ignored.
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 (&, <, >, ", ')
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;
- commArea - your own state, passed by reference. The parser passes it through unchanged on every call so the handler can accumulate results across calls.
- The array - the next batch of mapped elements, passed
CONST. ItsDIM(n)determines the batch size: the parser fills up to n elements per call. - numElements - the count of valid elements in this call, passed by value. It is never zero and is less than n only on the final call.
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
DIMarray (scalar orLIKEDS), and a count (TRN3014) - > and thepathoption is required (TRN3016). Both scalar andLIKEDSarray elements are supported; aLIKEDShandler receives anRpgDataStructurearray whose elements are populated from each repeated XML element's subfields.