Aller au contenu

%DATA

Document pour DATA-INTO et DATA-GEN

Désigne le document (JSON par exemple) que DATA-INTO doit lire ou que DATA-GEN doit produire, avec les options de correspondance avec les zones RPG.

Syntaxe

DATA-INTO variable %DATA(document : options) %PARSER(analyseur : options-analyseur)
DATA-GEN variable %DATA(zone-résultat : options) %GEN(générateur : options-générateur)
document
Pour DATA-INTO : la zone caractère qui contient le texte à lire. Pour DATA-GEN : la zone qui recevra le texte produit.
options
Une chaîne d'options séparées par des blancs. Celles de XML-INTO valent aussi ici : case=any, allowmissing=yes, allowextra=yes, countprefix=.
Résultat
%DATA ne renvoie rien. Elle se place derrière DATA-INTO ou DATA-GEN, qui exigent aussi un analyseur (%PARSER) ou un générateur (%GEN) : le RPG ne sait pas lire le JSON tout seul.

À quoi ça sert

DATA-INTO est l'équivalent de XML-INTO pour tout format autre que XML : le compilateur fait la correspondance entre la structure RPG et le document, un analyseur externe lit le format. Sur cette machine, l'analyseur est YAJLINTO, livré avec YAJL dans la bibliothèque YAJLLIB. Un JSON reçu d'un service web se retrouve ainsi dans une structure de données en une instruction.

Exemple 1 : Lire une commande JSON

**free
// %DATA et %PARSER : lire un document JSON en memoire avec DATA-INTO et YAJL.
ctl-opt dftactgrp(*no) actgrp(*new);

dcl-pr qcmdexc extpgm('QCMDEXC');
  commande char(200) const;
  longueur packed(15:5) const;
end-pr;

dcl-ds ligne_t qualified template;
  article varchar(20);
  qte     int(10);
end-ds;

dcl-ds commande qualified;
  numero  int(10);
  client  varchar(30);
  total   packed(9:2);
  urgent  ind;
  nb_lignes int(10);
  lignes  likeds(ligne_t) dim(5);
end-ds;

dcl-s json varchar(500);
dcl-s cmd  char(200);
dcl-s i    int(10);

// L'analyseur YAJLINTO s'appuie sur le programme de service YAJL : sa bibliotheque doit etre
// dans la liste des bibliotheques du travail ; si elle y est deja, l'erreur est ignoree
monitor;
  cmd = 'ADDLIBLE YAJLLIB';
  qcmdexc(cmd : %len(%trim(cmd)));
on-error;
endmon;

json = '{"numero":4521,"client":"Dupont SA","total":1250.50,"urgent":true,'
     + '"lignes":[{"article":"Cable RJ45","qte":10},{"article":"Switch","qte":2}]}';

data-into commande %data(json : 'case=any allowmissing=yes countprefix=nb_')
                   %parser('YAJLLIB/YAJLINTO');

snd-msg 'Numero : [' + %char(commande.numero) + ']';
snd-msg 'Client : [' + commande.client + ']';
snd-msg 'Total  : [' + %char(commande.total) + ']';
snd-msg 'Urgent : [' + commande.urgent + ']';
for i = 1 to commande.nb_lignes;
  snd-msg 'Ligne ' + %char(i) + ' : ' + commande.lignes(i).article
        + ' x ' + %char(commande.lignes(i).qte);
endfor;

*inlr = *on;

Résultat réel, compilé et exécuté sur IBM i 7.5 :

Numero : [4521]
Client : [Dupont SA]
Total  : [1250.50]
Urgent : [1]
Ligne 1 : Cable RJ45 x 10
Ligne 2 : Switch x 2

Le JSON contient un objet avec des nombres, un texte, un booléen et un tableau d'objets. %DATA porte le document et les options (countprefix=nb_ range le nombre de lignes dans nb_lignes), %PARSER désigne l'analyseur YAJL. Le booléen true arrive comme 1 dans une zone indicateur. L'appel ADDLIBLE YAJLLIB en début de programme met la bibliothèque du programme de service YAJL dans la liste des bibliothèques.

Exemple 2 : Clés manquantes ou en trop

**free
// %DATA : le document doit-il correspondre exactement a la zone RPG ?
// Tout se joue dans les options.
ctl-opt dftactgrp(*no) actgrp(*new);

dcl-pr qcmdexc extpgm('QCMDEXC');
  commande char(200) const;
  longueur packed(15:5) const;
end-pr;

dcl-ds client qualified;
  numero packed(5:0);
  nom    varchar(30);
  ville  varchar(30);
end-ds;

dcl-s json varchar(200);
dcl-s cmd  char(200);

monitor;
  cmd = 'ADDLIBLE YAJLLIB';
  qcmdexc(cmd : %len(%trim(cmd)));
on-error;
endmon;

// Le document n'a pas de "ville"
json = '{"numero":100,"nom":"Dupont SA"}';

// 1) Options minimales : la cle absente fait echouer DATA-INTO
monitor;
  data-into client %data(json : 'case=any') %parser('YAJLLIB/YAJLINTO');
  snd-msg 'Sans option : ok';
on-error;
  snd-msg 'Sans option : erreur, statut ' + %char(%status);
endmon;

// 2) allowmissing=yes : la sous-zone absente garde sa valeur
client.ville = 'inconnue';
data-into client %data(json : 'case=any allowmissing=yes') %parser('YAJLLIB/YAJLINTO');
snd-msg 'allowmissing : nom=[' + client.nom + '] ville=[' + client.ville + ']';

// 3) Une cle en trop dans le document
json = '{"numero":100,"nom":"Dupont SA","ville":"Lyon","tel":"0467000000"}';
monitor;
  data-into client %data(json : 'case=any') %parser('YAJLLIB/YAJLINTO');
  snd-msg 'Cle en trop : ok, ville=[' + client.ville + ']';
on-error;
  snd-msg 'Cle en trop : erreur, statut ' + %char(%status);
endmon;

// 4) allowextra=yes : les cles sans sous-zone sont ignorees
data-into client %data(json : 'case=any allowextra=yes') %parser('YAJLLIB/YAJLINTO');
snd-msg 'allowextra   : numero=[' + %char(client.numero) + '] ville=[' + client.ville + ']';

*inlr = *on;

Résultat réel, compilé et exécuté sur IBM i 7.5 :

Sans option : erreur, statut 356
allowmissing : nom=[Dupont SA] ville=[inconnue]
Cle en trop : erreur, statut 356
allowextra   : numero=[100] ville=[Lyon]

Le document n'a pas de clé ville : avec les seules options case=any, DATA-INTO échoue (statut 356). Avec allowmissing=yes, la sous-zone absente garde sa valeur d'avant. Dans le dernier cas, le document a une clé tel que la structure n'a pas : échec (statut 356) sans allowextra=yes, lecture normale avec.

Le piège

Sans YAJLLIB dans la liste des bibliothèques, rien ne marche. Le programme se compile bien, mais l'exécution s'arrête sur MCH3401 (« Impossible d'adresser objet YAJL ») dès le DATA-INTO, car l'analyseur ne trouve pas son programme de service. Sur un poste de production, on met la bibliothèque dans la description de travail.

Deuxième piège : comme pour XML-INTO, une clé absente ou en trop provoque une erreur (statut 356, message RNX0356) tant qu'on n'a pas indiqué allowmissing=yes ou allowextra=yes.