PBToolboxAI v4 ← Site

xml — n_pbt_xml #

← Component reference · Guide contents

Write an XML element by element — opens, attributes, text, closes — with the escaping done for you, then read one back with real XPath: of_load, of_get_value, of_get_attr, of_count. PowerBuilder 10 has no XML parser; this one relies on the WebView2 engine's DOMParser and document.evaluate, called synchronously.

▶ See it live — Demo application, XML tile: the results, the code behind them and this page, side by side.


At a glance #

The build side is pure PowerScript (assembling a string needs no engine). The read side opens a hidden page on the first query. The XPath is version 1.0, as the browser evaluates it. There is one document at a time: a read on a built document loads it into the page first, it becomes the loaded document, and the next of_start_element starts a new one. A read is refused while an element of the built document is still open (-4, empty or false, and is_last_error): close your elements first — nothing is lost. of_xml and of_pretty, which work on a copy, stay available.

Quick start #

// Local variables
n_pbt_xml  lnv_xml

// Create the object once, free it when done
lnv_xml = create n_pbt_xml

// Read : load, then query with XPath
lnv_xml.of_load(/*xml*/ '<order id="4152"><customer>Ada</customer></order>')
IF lnv_xml.of_is_valid() THEN
	MessageBox("Customer", lnv_xml.of_get_value(/*xpath*/ "/order/customer"))   // Ada
	MessageBox("Id", lnv_xml.of_get_attr(/*xpath*/ "/order", /*name*/ "id"))              // 4152
END IF

// Build : element by element, escaping done for you
lnv_xml.of_clear()
lnv_xml.of_start_element(/*name*/ "order")
lnv_xml.of_element_attr(/*name*/ "id", /*value*/ "4152")
lnv_xml.of_leaf(/*name*/ "customer", /*text*/ "Ada & Co")
lnv_xml.of_end_element()

// lnv_xml.of_xml() -><order id="4152"><customer>Ada &amp; Co</customer></order>

// Free the object
destroy lnv_xml

Properties #

PropertyTypeDefaultRole
is_last_errorstring""Why the last read returned empty, false, 0 or a negative code: a malformed document, an XPath the engine refused, an XPath that matched nothing, nothing loaded, the demo cap, a page that did not answer. Empty after a read that succeeded. The builder writes here too when it refuses an invalid element or attribute name
ipo_ownerpowerobjectnullThe visual object this helper works for: the licence is checked on its class. Set it before the first read; set later, it is handed over at the next read. Needed only in the demonstration application; a development or runtime key unlocks reading without it. Not unlocked, reading runs in demo mode — documents of 64 KB at most, changes included. Building does not need it: it is pure PowerScript

Build a document #

MemberRole
of_start_element (string as_name)Opens an element (elements nest: a pending start tag is closed first). An invalid name (empty, with a space, a forbidden character, more than one :, a : first or last) is refused: is_last_error says why, and the element is left out of the document with everything it holds
of_element_attr (string as_name, string as_value)Adds an attribute to the open element, its value escaped — line breaks and tabs included, so they read back as they were (before any child or text). An invalid name is refused (is_last_error); the same name given twice keeps ONE attribute, with the second value
of_element_text (string as_text)The text content of the current element, escaped; the control characters XML forbids are dropped (a single one made the whole document malformed)
of_end_element ( )Closes the current element (empty, it is self-closing <name/>)
of_leaf (string as_name, string as_text)A whole leaf element in one call — <name>text</name>, escaped
of_clear ( ) → longEmpties the document — built or loaded — to start another; a loaded document leaves the page too. Returns 0
of_xml ( ) → stringThe document as it stands: what you built or loaded (of_load), after any change — the single output, to send, store or save back. A built document needs no page (any element left open is closed) ; a loaded one is serialized by the engine. of_pretty is the indented form

Read a document (XPath) #

MemberRole
of_load (string as_xml) → longLoads an XML text (parsed by the engine's DOMParser). Returns 0 when well-formed, -5 when empty, -4 when not valid XML (or, in demo mode, too long once its entities are expanded), -2 when the hidden page did not answer. On a negative code NOTHING stays: neither a previously loaded document nor a half-built one
of_is_valid ( ) → booleanTrue when a well-formed document is held — loaded with of_load, or built (it is then loaded into the page, as any read does). False when there is nothing, or when the built document is malformed or still has an element open
of_exists (string as_xpath) → booleanTrue when the XPath matches at least one node. An expression that yields a VALUE (count(//line)) is not a node: read it with of_get_value
of_get_value (string as_xpath) → stringThe text content of the FIRST matching node. Empty when none (is_last_error says so). An XPath that yields a VALUE gives it as text: count(//line) → 3, sum(//@qty), boolean(//paid) → true
of_get_attr (string as_xpath, string as_name) → stringThe value of attribute as_name on the first matching node. Empty if absent
of_count (string as_xpath) → longHow many nodes the XPath selects. 0 when none (an XPath count is never negative); is_last_error says when the XPath itself was refused
of_name (string as_xpath) → stringThe tag name of the first matching node. Empty if none
of_pretty ( ) → stringThe document as it stands — built or loaded, what of_xml returns — re-indented for reading. Nothing is lost: CDATA, comments, processing instructions and the XML declaration are kept, and an element holding text is written on one line, as it is. Empty when there is nothing, or when a built document is not well-formed

Several nodes, and the document #

MemberRole
of_get_values (string as_xpath, ref string as_values[]) → longThe text content of EVERY matching node, into as_values; returns the count. Where of_get_value returns only the first
of_get_attrs (string as_xpath, string as_name, ref string as_values[]) → longThe value of attribute as_name on EVERY matching node (the id of every row); returns the count
of_get_attr_names (string as_xpath, ref string as_names[]) → longThe names of every attribute on the first matching node (enumeration); returns the count

Modify a document #

The write side changes the loaded tree in place; of_xml returns the result. Each targets the FIRST matching node (except of_remove, which takes them all), returns 0 on success, -4 when nothing matched or the change was refused (is_last_error says why), -5 on an empty argument, -2 when the page did not answer.

MemberRole
of_set_value (string as_xpath, string as_text) → longSets the text of the first matching node (its children are replaced)
of_set_attr (string as_xpath, string as_name, string as_value) → longSets (or adds) an attribute on the first matching node. A prefixed name (xsi:type) goes into the namespace its prefix has at that node (or one bound by of_register_namespace); a prefix bound nowhere is refused (-4, is_last_error)
of_remove_attr (string as_xpath, string as_name) → longRemoves an attribute from the first matching node
of_add_child (string as_xpath, string as_tag, string as_text) → longAppends a child element <as_tag>as_text</as_tag> under the first matching node (the child inherits the parent's namespace and prefix; a prefixed tag p:item takes the namespace of its prefix, or -4 when it is bound nowhere). -4 also when the node cannot have children (an attribute, a text) or the tag is not a valid name
of_add_xml (string as_xpath, string as_fragment) → longGrafts an already-formed XML fragment (one or several sibling elements) under the first matching node — what of_add_child cannot do, taking only a tag and text. The fragment must be well-formed; it inherits the namespaces in force at its target, like of_add_child (<line> under <order xmlns='urn:acme'> is in urn:acme, <s:Item/> under a SOAP s:Body declares nothing)
of_remove (string as_xpath) → longRemoves EVERY matching node (a row, a stale branch, an attribute). Returns how many were really removed (>= 0; on 0, is_last_error says why), -4 when nothing is loaded, -5 on an empty XPath, -2 when the page did not answer. The root element stays: of_clear empties a document

Namespaces #

A prefixed XPath (SOAP, an enterprise API) resolves on its own a prefix the document declares (xmlns:s="…", on any element). ⚠️ The most frequent trap: the DEFAULT namespace. <order xmlns="urn:acme"><customer>… has no prefix, and yet /order/customer matches NOTHING: XPath 1.0 has no default namespace. Bind a prefix of your choice with of_register_namespace("a", "urn:acme") and write /a:order/a:customer. of_clear_namespaces forgets the bindings. There is no XSLT: Chromium is removing it from the engine (transform on the server side or in PowerScript). There is no XSD schema validation: the engine has none.

MemberRole
of_register_namespace (string as_prefix, string as_uri) → longBinds a prefix to its namespace URI so a prefixed XPath resolves. Returns 0, -5 on an empty prefix or URI, -2 when the page did not answer. Bindings last until of_clear_namespaces or of_reset
of_clear_namespaces ( ) → longForgets every registered prefix: a prefix the document does not declare matches nothing again until it is bound. Returns 0, -2 when the page did not answer
// A SOAP response : the prefix declared by the document resolves on its own
lnv_xml.of_load(/*xml*/ ls_soap)
ls_ok = lnv_xml.of_get_value(/*xpath*/ "//s:Body/result/status")

// A DEFAULT namespace (xmlns="urn:acme") : bind a prefix of your choice
lnv_xml.of_load(/*xml*/ '<order xmlns="urn:acme"><customer>Ada</customer></order>')
lnv_xml.of_register_namespace(/*prefix*/ "a", /*uri*/ "urn:acme")
ls_name = lnv_xml.of_get_value(/*xpath*/ "/a:order/a:customer")   // Ada

// Mutate, then read the result back
lnv_xml.of_set_value(/*xpath*/ "/a:order/a:customer", /*text*/ "Grace")
lnv_xml.of_add_child(/*xpath*/ "/a:order", /*tag*/ "total", /*text*/ "690.50")

// Graft a whole fragment at once (of_add_child adds only one tag)
lnv_xml.of_add_xml(/*xpath*/ "/a:order", /*fragment*/ '<lines xmlns="urn:acme"><line sku="1">A</line><line sku="2">B</line></lines>')
ls_out = lnv_xml.of_xml()

The hidden page #

MemberRole
of_open ( ) → longCreates the hidden page (read side). Optional — the first read does it. Returns the handle (> 0) or -2 when it could not be created (is_last_error says why)
of_is_open ( ) → booleanTrue once the hidden page exists
of_close ( )Releases the hidden page. The build side keeps working without it, and a loaded document is not lost: it is kept as text, and the next read loads it back. Done for you on destruction
of_reset ( )Back to the defaults: the builder is emptied, the read state cleared

Scope. XML is read through the engine (DOMParser, XPath): n_pbt_xml opens a hidden page on the first read, unlike json, which parses in pure PowerScript. To display an XML as a collapsible tree, see xmltree.

← Component reference · Guide contents