%XML
Document XML pour XML-INTO
Désigne le document XML (ici une chaîne en mémoire) que l'opération XML-INTO doit lire, avec les options qui règlent la correspondance avec les zones RPG.
Syntaxe
XML-INTO variable %XML(document : options)
XML-INTO %HANDLER(procédure : zone) %XML(document : options)
document- Le document XML : ici, une zone caractère qui contient le texte.
options- Une chaîne d'options séparées par des blancs :
case=any(ignore la casse des noms),allowmissing=yes(des éléments peuvent manquer),allowextra=yes(des éléments en trop sont ignorés),countprefix=nb_(renseigne le nombre d'éléments trouvés),path=(point de départ dans le document). - Résultat
- %XML ne renvoie rien : elle n'a de sens qu'en paramètre de XML-INTO. C'est XML-INTO qui remplit la structure de données, le tableau ou la zone indiquée.
À quoi ça sert
Lire un document XML à la main, c'est écrire un analyseur. Avec XML-INTO et %XML, on déclare une structure de données dont les sous-zones portent le nom des éléments et des attributs, et le compilateur fait le reste : conversion des nombres, tableaux pour les éléments répétés.
C'est le format d'échange de beaucoup de services web et de fichiers de partenaires, et le document arrive souvent déjà en mémoire (réponse HTTP, colonne SQL).
Exemple 1 : Lire une facture avec ses lignes
**free
// %XML : lire un document XML en memoire avec XML-INTO.
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-ds facture qualified;
numero char(10);
client varchar(30);
total packed(9:2);
nb_lignes int(10);
lignes likeds(ligne_t) dim(5);
end-ds;
dcl-ds ligne_t qualified template;
article varchar(20);
qte int(10);
prix packed(7:2);
end-ds;
dcl-s doc varchar(500);
dcl-s i int(10);
doc = '<facture><numero>F2026-0142</numero><client>Dupont SA</client>'
+ '<total>1250.50</total>'
+ '<lignes><article>Cable RJ45</article><qte>10</qte><prix>4.90</prix></lignes>'
+ '<lignes><article>Switch 8 ports</article><qte>2</qte><prix>600.75</prix></lignes>'
+ '</facture>';
xml-into facture %xml(doc : 'case=any allowmissing=yes countprefix=nb_');
snd-msg 'Numero : [' + %trim(facture.numero) + ']';
snd-msg 'Client : [' + facture.client + ']';
snd-msg 'Total : [' + %char(facture.total) + ']';
snd-msg 'Lignes : [' + %char(facture.nb_lignes) + ']';
for i = 1 to facture.nb_lignes;
snd-msg 'Ligne ' + %char(i) + ' : ' + facture.lignes(i).article
+ ' x ' + %char(facture.lignes(i).qte)
+ ' a ' + %char(facture.lignes(i).prix);
endfor;
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
Numero : [F2026-0142]
Client : [Dupont SA]
Total : [1250.50]
Lignes : [2]
Ligne 1 : Cable RJ45 x 10 a 4.90
Ligne 2 : Switch 8 ports x 2 a 600.75
Le document est une chaîne. La structure facture reprend les noms des éléments ; lignes est un tableau de 5 éléments, alors que le document n'en contient que 2. Deux options le permettent : allowmissing=yes accepte un tableau incomplet, et countprefix=nb_ range le nombre réel de lignes dans la sous-zone nb_lignes, que la boucle utilise.
Exemple 2 : Ce qui doit correspondre, et les options qui assouplissent
**free
// %XML : les options decident si le document doit correspondre exactement a la zone RPG.
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-ds client qualified;
id packed(5:0);
nom varchar(30);
end-ds;
dcl-s doc varchar(200);
// Un attribut (id) et deux elements (nom, ville) : la zone RPG n'a pas de sous-zone ville
doc = '<client id="100"><nom>Dupont SA</nom><ville>Montpellier</ville></client>';
// 1) Options par defaut : l'element ville en trop fait echouer XML-INTO
monitor;
xml-into client %xml(doc : 'case=any');
snd-msg 'Sans option : ok';
on-error;
snd-msg 'Sans option : erreur, statut ' + %char(%status);
endmon;
// 2) allowextra=yes : les elements sans sous-zone sont ignores
clear client;
xml-into client %xml(doc : 'case=any allowextra=yes');
snd-msg 'allowextra : id=[' + %char(client.id) + '] nom=[' + client.nom + ']';
// 3) Sans elle, la meme zone plante avec le message RNQ0353
clear client;
xml-into client %xml(doc : 'case=any');
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
Sans option : erreur, statut 353
allowextra : id=[100] nom=[Dupont SA]
Erreur : RNQ0353 - Le document XML ne correspond pas à la variable RPG (code raison 5 : élément en trop).
Le document contient un attribut id et deux éléments, mais la zone RPG n'a pas de sous-zone ville. Par défaut, c'est une erreur (statut 353 ; code raison 5 dans le message RNX0353). Avec allowextra=yes, l'attribut et le nom sont lus et l'élément en trop est ignoré. Le troisième essai, sans l'option et sans surveillance, arrête le programme avec le message RNQ0353.
Le piège
Par défaut, la correspondance doit être exacte, et le tableau doit être exactement plein. Un tableau de 5 éléments face à 2 éléments dans le document, c'est l'erreur RNX0353 avec le code raison 2 (trop peu d'éléments) ; un élément sans sous-zone, c'est le code raison 5. On règle le problème avec allowmissing=yes, allowextra=yes ou countprefix= selon le cas.
Le code raison est dans le second niveau du message RNX0353 : c'est lui qui dit quelle règle n'est pas respectée.