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 & Co</customer></order>
// Free the object
destroy lnv_xml
Properties #
| Property | Type | Default | Role |
|---|---|---|---|
is_last_error | string | "" | 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_owner | powerobject | null | The 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 #
| Member | Role |
|---|---|
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 ( ) → long | Empties the document — built or loaded — to start another; a loaded document leaves the page too. Returns 0 |
of_xml ( ) → string | The 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) #
| Member | Role |
|---|---|
of_load (string as_xml) → long | Loads 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 ( ) → boolean | True 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) → boolean | True 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) → string | The 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) → string | The value of attribute as_name on the first matching node. Empty if absent |
of_count (string as_xpath) → long | How 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) → string | The tag name of the first matching node. Empty if none |
of_pretty ( ) → string | The 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 #
| Member | Role |
|---|---|
of_get_values (string as_xpath, ref string as_values[]) → long | The 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[]) → long | The 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[]) → long | The 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.
| Member | Role |
|---|---|
of_set_value (string as_xpath, string as_text) → long | Sets the text of the first matching node (its children are replaced) |
of_set_attr (string as_xpath, string as_name, string as_value) → long | Sets (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) → long | Removes an attribute from the first matching node |
of_add_child (string as_xpath, string as_tag, string as_text) → long | Appends 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) → long | Grafts 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) → long | Removes 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.
| Member | Role |
|---|---|
of_register_namespace (string as_prefix, string as_uri) → long | Binds 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 ( ) → long | Forgets 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 #
| Member | Role |
|---|---|
of_open ( ) → long | Creates 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 ( ) → boolean | True 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_xmlopens a hidden page on the first read, unlike json, which parses in pure PowerScript. To display an XML as a collapsible tree, see xmltree.