xml — n_pbt_xml #
← Référence des composants · Sommaire du guide
Écrire un XML élément par élément — ouvertures, attributs, texte, fermetures — avec l'échappement fait pour vous, puis en relire un avec du vrai XPath :
of_load,of_get_value,of_get_attr,of_count, et des nombres et des dates lus pareil sur tout poste (of_get_decimal,of_get_date). PowerBuilder a PBDOM (un DOM sans XPath, une extension PBNI à déployer) etXMLParseString; celui-ci donne du XPath 1.0 et les espaces de noms sans rien à déployer, en s'appuyant sur le DOMParser et document.evaluate du moteur WebView2, appelés synchronement.
▶ Le voir en vrai — Application de démonstration, tuile XML : les résultats, le code qui les produit et cette page, côte à côte.
En bref #
Le build est du PowerScript pur (assembler une chaîne ne demande aucun moteur). La lecture ouvre une page cachée à la première requête. Le XPath est la version 1.0, telle que le navigateur l'évalue. Il n'y a qu'un document à la fois : une lecture sur un document bâti le charge d'abord dans la page, il devient le document chargé, et le of_start_element suivant en commence un nouveau. Une lecture est refusée tant qu'un élément du document bâti est encore ouvert (-4, vide ou false, et of_get_last_error()) : fermez d'abord vos éléments — rien n'est perdu. of_xml et of_pretty, qui travaillent sur une copie, restent permis.
Démarrage rapide #
// 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
Propriétés #
| Propriété | Type | Défaut | Rôle |
|---|---|---|---|
ipo_owner | powerobject | null | L'objet visuel pour lequel cet utilitaire travaille : la licence se vérifie sur sa classe. À poser avant la première lecture ; posé plus tard, il est transmis à la lecture suivante. Nécessaire seulement dans l'application de démonstration ; une clé de développement ou d'exécution débride la lecture sans lui. Non débridée, la lecture est en mode démo — documents de 65 536 caractères au plus, modifications comprises ; of_is_valid est faux et of_pretty vide pour un document bâti plus long. La construction n'en a pas besoin : elle est en PowerScript pur |
Construire un document #
| Membre | Rôle |
|---|---|
of_get_last_error ( ) → string | Pourquoi la dernière lecture a rendu vide, false, 0 ou un code négatif : document mal formé, XPath refusé par le moteur, XPath sans correspondance, rien de chargé, plafond démo, page qui n'a pas répondu. Vide après une lecture réussie. Le builder y écrit aussi quand il refuse un nom d'élément ou d'attribut invalide |
of_start_element (string as_name) | Ouvre un élément (les éléments s'imbriquent : un tag ouvert est fermé d'abord). Un nom invalide (vide, avec une espace, un caractère interdit, plus d'un :, un : en tête ou en fin) est refusé : of_get_last_error() dit pourquoi, et l'élément est laissé hors du document avec tout ce qu'il contient ; un second élément au sommet, une fois la racine fermée, l'est aussi : un document n'a qu'une racine |
of_element_attr (string as_name, string as_value) | Ajoute un attribut à l'élément ouvert, valeur échappée — sauts de ligne et tabulations compris, pour qu'ils se relisent tels quels (avant tout enfant ou texte). Un nom invalide est refusé (of_get_last_error()) ; le même nom donné deux fois garde UN seul attribut, avec la seconde valeur. Donné après un enfant ou un texte, ou sans élément ouvert, il est laissé de côté et of_get_last_error() le dit. Surcharges : décimal, date, date-heure, booléen, écrits comme par of_leaf |
of_element_text (string as_text) | Le contenu texte de l'élément courant, échappé ; les caractères de contrôle que XML interdit sont retirés (un seul rendait tout le document mal formé). Sans élément ouvert, le texte est laissé de côté et of_get_last_error() le dit |
of_end_element ( ) | Ferme l'élément courant (vide, il est auto-fermant <name/>) |
of_leaf (string as_name, string as_text) | Un élément feuille en un appel — <name>texte</name>, échappé. Surcharges : un décimal, une date, une date-heure ou un booléen, écrits comme XML Schema les écrit quel que soit le poste — 690.5 (un point), 2026-10-31, 2026-10-31T14:30:00, true ; un NULL écrit un élément vide |
of_clear ( ) → long | Vide le document — bâti ou chargé — pour en commencer un autre ; un document chargé quitte aussi la page. Rend 0 |
of_xml ( ) → string | Le document tel quel : ce que vous avez bâti ou chargé (of_load), après toute modification — la sortie unique, à envoyer, stocker ou réécrire. Un document bâti n'a pas besoin de page (tout élément resté ouvert y est fermé) ; un document chargé est sérialisé par le moteur. of_pretty en donne la forme indentée |
Lire un document (XPath) #
| Membre | Rôle |
|---|---|
of_load (string as_xml) → long | Charge un texte XML (analysé par le DOMParser du moteur). Rend 0 si bien formé, -5 si vide, -4 si XML invalide (ou, en mode démo, trop long une fois ses entités développées), -2 si la page cachée n'a pas répondu. Sur un code négatif RIEN ne reste : ni un document chargé avant, ni un document à moitié bâti. Chaque appel à la page attend 5 secondes au plus : un document de plusieurs dizaines de Mo, surtout dans une application 32 bits, peut rendre -2 |
of_is_valid ( ) → boolean | Vrai quand un document bien formé est tenu — chargé par of_load, ou bâti (il est alors chargé dans la page, comme par toute lecture). Faux s'il n'y a rien, ou si le document bâti est mal formé ou a encore un élément ouvert — et, en mode démo, pour un document bâti de plus de 65 536 caractères |
of_exists (string as_xpath) → boolean | Vrai quand l'XPath correspond à au moins un nœud. Une expression qui rend une VALEUR (count(//line)) n'est pas un nœud : lisez-la par of_get_value |
of_get_value (string as_xpath) → string | Le contenu texte du PREMIER nœud correspondant. Vide si aucun (of_get_last_error() le dit). Un XPath qui rend une VALEUR la rend en texte : count(//line) → 3, sum(//@qty), boolean(//paid) → true. / rend le texte du document entier ; une expression qui n'est pas un nombre (un sum() sur un texte) rend vide avec sa raison, jamais NaN. Un nombre, une date, un booléen se lisent pareil sur tout poste par of_get_number, of_get_decimal, of_get_date… |
of_get_attr (string as_xpath, string as_name) → string | La valeur de l'attribut as_name du premier nœud correspondant. Vide si le nœud ou l'attribut est absent — of_get_last_error() dit alors « no node matched » ou « no such attribute » ; un attribut écrit vide (a="") rend vide sans raison. Un nom vide est refusé de même |
of_count (string as_xpath) → long | Combien de nœuds l'XPath sélectionne. 0 si aucun ; -4 si rien n'est chargé, si un élément du document bâti est encore ouvert ou si le moteur a refusé l'XPath, -2 si la page n'a pas répondu ; of_get_last_error() dit lequel |
of_name (string as_xpath) → string | Le nom de balise du premier nœud correspondant. Vide si aucun |
of_pretty ( ) → string | Le document tel quel — bâti ou chargé, ce que rend of_xml — ré-indenté pour la lecture. CDATA, commentaires, instructions de traitement et déclaration XML sont gardés, et un élément qui contient du texte s'écrit sur une ligne, tel quel ; ce qui est ré-indenté, ce sont les blancs ENTRE éléments — sauf dans un élément marqué xml:space="preserve", écrit tel quel. Vide s'il n'y a rien, si un document bâti n'est pas bien formé, et en mode démo pour un document de plus de 65 536 caractères |
Nombres, dates et booléens #
XML Schema écrit un nombre avec un point et une date en ISO 8601, quel que soit le pays. Dec("690.50") ou Date("2026-10-31") dépendent, eux, des réglages régionaux du poste. Ces lecteurs vérifient la valeur contre son type et la rendent pareille sur tout poste ; une valeur absente, vide ou d'un autre type rend NULL, et of_get_last_error() dit pourquoi. Un XPath qui rend une valeur (sum(//qty)) marche aussi. Pour écrire : of_leaf, of_element_attr et of_set_value prennent un décimal, une date, une date-heure ou un booléen.
| Membre | Rôle |
|---|---|
of_get_number (string as_xpath) → double | Le nombre en double : 690.50, -1.5E3, +.5 ; blancs autour ignorés ; 12 kg, 1,5, INF sont refusés |
of_get_decimal (string as_xpath) → decimal | Le nombre en decimal, chaque chiffre gardé (un montant ne perd jamais ses centimes) ; refusé au-delà des 28 chiffres d'un decimal |
of_get_longlong (string as_xpath) → longlong | L'entier en longlong, chaque chiffre gardé (un identifiant au-delà de 2^53 aussi) ; 2.5 est refusé, pas tronqué |
of_get_boolean (string as_xpath) → boolean | true ou 1 → true, false ou 0 → false ; tout le reste (yes, Y) rend NULL |
of_get_date (string as_xpath) → date | 2026-10-31, ou la date d'une date-heure ; un fuseau (Z, +02:00) est accepté sans être appliqué. Refusé : un jour qui n'existe pas (2026-02-30), une année hors 1000 à 3000 |
of_get_datetime (string as_xpath) → datetime | 2026-10-10T14:30:00, avec ou sans fraction de seconde, 2026-10-10 14:30, ou une date seule (minuit) ; l'heure est celle ÉCRITE : un fuseau n'est pas appliqué, comme n_pbt_json le lit |
// Local variables
n_pbt_xml lnv_xml
decimal ldc_total
date ld_due
// Create the object once, free it when done
lnv_xml = create n_pbt_xml
// Write : a point and ISO 8601, whatever the regional settings
lnv_xml.of_start_element(/*name*/ "invoice")
lnv_xml.of_element_attr(/*name*/ "paid", /*value*/ false)
lnv_xml.of_leaf(/*name*/ "total", /*value*/ 690.50)
lnv_xml.of_leaf(/*name*/ "due", /*value*/ Date(2026, 10, 31))
lnv_xml.of_end_element()
// lnv_xml.of_xml() -> <invoice paid="false"><total>690.5</total><due>2026-10-31</due></invoice>
// Read them back as values you can compute with
ldc_total = lnv_xml.of_get_decimal(/*xpath*/ "/invoice/total")
ld_due = lnv_xml.of_get_date(/*xpath*/ "/invoice/due")
// A value that is not of its type is NULL : is_last_error says why
IF IsNull(ldc_total) THEN MessageBox("Total", lnv_xml.of_get_last_error())
// Free the object
destroy lnv_xml
Plusieurs nœuds, et le document #
| Membre | Rôle |
|---|---|
of_get_values (string as_xpath, ref string as_values[]) → long | Le contenu texte de CHAQUE nœud correspondant, dans as_values ; rend le compte, 0 si aucun, -4 si rien n'est chargé ou si l'XPath est refusé, -2 si la page n'a pas répondu. Là où of_get_value ne rend que le premier |
of_get_attrs (string as_xpath, string as_name, ref string as_values[]) → long | La valeur de l'attribut as_name sur CHAQUE nœud correspondant (l'id de chaque ligne), vide pour un nœud qui ne l'a pas ; rend le compte, 0 si aucun, -5 sur un nom vide, -4 si rien n'est chargé ou si l'XPath est refusé, -2 si la page n'a pas répondu |
of_get_attr_names (string as_xpath, ref string as_names[]) → long | Les noms de tous les attributs du premier nœud correspondant (énumération) ; rend le compte, 0 si le nœud n'en a pas ou si aucun nœud ne correspond (of_get_last_error() le dit), -4 si rien n'est chargé, si l'XPath est refusé ou si le nœud n'est pas un élément, -2 si la page n'a pas répondu |
Modifier un document #
Le côté écriture change l'arbre chargé sur place ; of_xml en rend le résultat. Chacune vise le PREMIER nœud correspondant (sauf of_remove, qui les prend tous), rend 0 au succès, -4 si rien ne correspond ou si le changement est refusé (of_get_last_error() dit pourquoi), -5 sur un argument vide, -2 si la page n'a pas répondu. Ce qu'une modification écrit suit la règle du builder : les caractères de contrôle que XML interdit sont retirés, et un CR s'écrit de sorte qu'il se relise.
| Membre | Rôle |
|---|---|
of_set_value (string as_xpath, string as_text) → long | Fixe le texte du premier nœud correspondant (ses enfants sont remplacés). Surcharges : décimal, date, date-heure, booléen, écrits comme par of_leaf |
of_set_attr (string as_xpath, string as_name, string as_value) → long | Fixe (ou ajoute) un attribut sur le premier nœud correspondant. Un nom préfixé (xsi:type) va dans l'espace de noms de son préfixe à ce nœud (ou lié par of_register_namespace) ; un préfixe lié nulle part est refusé (-4, of_get_last_error()) |
of_remove_attr (string as_xpath, string as_name) → long | Retire un attribut du premier nœud correspondant ; -4 et « no such attribute » si le nœud ne l'a pas |
of_add_child (string as_xpath, string as_tag, string as_text) → long | Ajoute un élément enfant <as_tag>as_text</as_tag> sous le premier nœud correspondant (l'enfant hérite de l'espace de noms et du préfixe du parent ; une balise préfixée p:item prend l'espace de noms de son préfixe, ou -4 s'il n'est lié nulle part). -4 aussi quand le nœud ne peut pas avoir d'enfant (un attribut, un texte) ou que la balise n'est pas un nom valide |
of_add_xml (string as_xpath, string as_fragment) → long | Greffe un fragment XML déjà formé (un ou plusieurs éléments frères) sous le premier nœud correspondant — ce que of_add_child ne fait pas, ne prenant qu'une balise et un texte. Le fragment doit être bien formé ; il hérite des espaces de noms en vigueur à sa cible, comme of_add_child (<line> sous <order xmlns='urn:acme'> est dans urn:acme, <s:Item/> sous un s:Body SOAP n'a rien à déclarer) ; un préfixe lié par of_register_namespace, ou déclaré ailleurs dans le document, n'a rien à déclarer non plus |
of_remove (string as_xpath) → long | Retire TOUS les nœuds correspondants (une ligne, une branche périmée, un attribut). Rend combien ont été réellement retirés (>= 0 ; sur 0, of_get_last_error() dit pourquoi), -4 si rien n'est chargé, -5 sur un XPath vide, -2 si la page n'a pas répondu. L'élément racine reste : of_clear vide un document |
Espaces de noms #
Un XPath préfixé (SOAP, une API d'entreprise) résout tout seul un préfixe que le document déclare (xmlns:s="…", sur n'importe quel élément). ⚠️ Le piège le plus fréquent : l'espace de noms PAR DÉFAUT. <order xmlns="urn:acme"><customer>… n'a aucun préfixe, et pourtant /order/customer ne correspond à RIEN : XPath 1.0 n'a pas d'espace de noms par défaut. Liez un préfixe de votre choix par of_register_namespace("a", "urn:acme") et écrivez /a:order/a:customer. of_clear_namespaces oublie les liaisons. Il n'y a pas de XSLT : Chromium le retire du moteur (transformez côté serveur, ou par MSXML en OLE : Msxml2.DOMDocument.6.0, transformNode). Il n'y a pas de validation par schéma XSD : le moteur n'en a pas.
| Membre | Rôle |
|---|---|
of_register_namespace (string as_prefix, string as_uri) → long | Lie un préfixe à l'URI de son espace de noms, pour qu'un XPath préfixé résolve. Rend 0, -5 sur un préfixe ou une URI vide, -2 si la page n'a pas répondu. Les liaisons durent jusqu'à of_clear_namespaces ou of_reset — of_close ne les perd pas |
of_clear_namespaces ( ) → long | Oublie tous les préfixes enregistrés : un préfixe que le document ne déclare pas ne correspond plus à rien tant qu'il n'est pas relié. Rend 0, -2 si la page n'a pas répondu. Aucune page n'est ouverte pour cela |
// Local variables
n_pbt_xml lnv_xml
string ls_soap, ls_ok, ls_name, ls_out
// ls_soap holds the response of a web service
lnv_xml = create n_pbt_xml
// 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()
// Free the object
destroy lnv_xml
La page cachée #
| Membre | Rôle |
|---|---|
of_open ( ) → long | Crée la page cachée (côté lecture). Facultatif — la première lecture le fait. Rend le handle (> 0) ou -2 si elle n'a pas pu être créée (of_get_last_error() dit pourquoi) |
of_is_open ( ) → boolean | Vrai dès que la page cachée existe |
of_close ( ) → long | Libère la page cachée. Le build continue sans elle, et un document chargé n'est pas perdu : il est gardé en texte, et la lecture suivante le recharge, avec les préfixes liés par of_register_namespace. Fait pour vous à la destruction Rend 0 une fois fait. |
of_reset ( ) → long | Retour aux valeurs par défaut : le builder vidé, le document chargé lâché, les préfixes enregistrés oubliés, of_get_last_error() vidé. La page cachée reste ouverte ; of_close la libère Rend 0 une fois fait. |
Portée. Le XML se lit par le moteur (DOMParser, XPath) :
n_pbt_xmlouvre une page cachée à la première lecture, contrairement à json, qui s'analyse en PowerScript pur. Pour afficher un XML en arbre repliable, voyez xmltree.