Les types de données SOAP et les types de données 1000
Sommaire |
Les types de données SOAP et Ligne 1000
L’exemple précédent utilisait un service très simple échangeant une chaine de caractère, les types utilisables dans les WS peuvent être plus compliqués et s’appuient sur les types définis par les Schémas XML
Les principaux types simples
SOAP | Ligne 1000 | Commentaire |
---|---|---|
string | String | |
int | Integer | |
decimal | Currency | Le type Decimal peut comporter jusqu’à 128 décimales, la ligne 1000 ne permet que 4 décimales |
float, double | Double | |
dateTime | TDateTime | |
date | Date | |
time | Time | |
boolean | Boolean | |
base64Binary | String | Une chaine encodée contenant les données binaires |
Les types SOAP sont reconnus comme des types de données natifs par 1000
Les types binaires
SOAP permet d’échanger des éléments binaires encodé soit en base 64 (base64Binary) soit en hexadécimal (hexBinary).
Pour permettre de manipuler ces données binaires des propriétés base64Binary et hexBinary ont été ajouté au type de données binaires du Framework (TfwPicture, TfwBinary, TfwMemo), et de nouveaux objets techniques ont été introduis :
C’est un type générique permettant de contenir des données binaires.
Exemple d’utilisation :
// Procedure SendClaim; // var inst:TACwsiattachement; body:TACClaimDetailType; content: TBinaryContent; begin // Objet gestionnaire de binaire content := TBinaryContent.Create; // Chargement de l’objet avec une image content.LoadFromFile('d:\Mes documents\Mes images\white house.jpg'); // Descriptif de l’image attendu par le service body := ClassManager.CreateInstance('TACClaimDetailType'); body.Name := 'White house'; // inst := ClassManager.CreateInstance('TACwsiattachement'); inst.sendClaim(body,content.base64Binary); end;
Exemple :
Cet exemple montre comment appelé un service exécutant l’écho d’un paramètre binaire :
Le service :
Type InteropService = Class(TServiceLocal) Procedure echoBase64(const input:base64Binary; out output:base64Binary); end; Procedure InteropService.echoBase64(const input:base64Binary; out output:base64Binary); begin output := input; end;
Le proxy local du service :
Type ICinteropservice = Class(TServiceDistant) public Procedure echoBase64(const input:base64Binary; out output:base64Binary); end; Procedure ICinteropservice.echoBase64(const input:base64Binary; out output:base64Binary); begin //Remote procedure, do not code end;
L’appel du service en utilisant des chaînes de caractères :
//Procedure echoBase64; var inst:ICinteropService; sin:string; sout:base64binary; contin,contout:TBinaryContent; begin inst := ClassManager.CreateInstance('ICinteropService'); // Une chaine a transmettre sin := 'coucou'; // Converti la chaine en utilisant un BinaryContent // contin := TBinaryContent.Create; contin.AsString := sin; // appel du service // sout contient la réponse encodée inst.echoBase64(contin.base64Binary,sout); // converti la réponse // contout := TBinaryContent.Create; contout.base64Binary := sout; // compare le résultat // if sin<>contout.AsString then showMessage('echoBase64 failed') else showMessage('echoBase64 OK'); end;
L’appel du service en utilisant des TStringList
//Procedure echoBase64_2; var inst:ICinteropService; lsin,lsout:TStringList; sout: base64binary; begin inst := ClassManager.CreateInstance('ICinteropService'); lsin := TStringList.Create; lsin.LoadFromFile('c:\myText-1.txt'); inst.echoBase64(lsin.base64binary,sout); lsout := TStringlist.Create; lsout.base64Binary := sout; lsout.SaveToFile('c:\myText-2.txt'); end;
Même exemple en utilisant une compression :
//Procedure echoBase64_3; var inst:ICinteropService; lsin,lsout:TStringList; sout: base64Binary; begin inst := ClassManager.CreateInstance('ICinteropService'); lsin := TStringList.Create; lsin.LoadFromFile('c:\myText-1.txt'); inst.echoBase64(lsin.getAsBase64Binary(b64ZlibNoSig),sout); lsout := TStringlist.Create; lsout.setAsBase64Binary(sout,b64ZLibNoSig); lsout.SaveToFile('c:\myText-3.txt'); end;
Les énumérés
Le protocole SOAP permet de définir des énumérations de valeurs pour préciser les valeurs possibles d’un paramètre de message.
Un énuméré SOAP correspond à un énuméré Ligne 1000.
Par exemple pour définir un service effectuant l’écho d’un énuméré :
//Défini un énuméré. EchoNum = (cstEcho1, cstEcho2) ; //Procedure echoEnum(input:EchoEnum; out output:EchoEnum); begin output := input; end;
Pour appeler ce service, (le service a été importé en utilisant un préfixe de paquet IC) :
ICEchoNum = (cstEcho1,cstEcho2) ; // Procedure echoEnum; // var inst:ICinteropService; sin,sout:ICEchoEnum; begin sin := cstEcho1; inst := ClassManager.CreateInstance('ICinteropService'); inst.echoEnum(cstEcho1,sout); if sin<>sout then showMessage('echoEnum failed') else showMessage('echoEnum OK'); end;
Les valeurs énumérées sont transmises par leur constantes et non par leur index, l’appel précédent génère le message suivant :
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <echoEnum xmlns="http://www.sage.com/fr/line1000/InterpServicePackage"> <input>cstEcho1</input> </echoEnum> </soap:Body> </soap:Envelope>
Les types complexes (structures)
Le protocole SOAP permet d’échanger des types complexes sous forme de structure.
Une structure SOAP correspond à une classe Ligne 1000.
Appel d’un service effectuant l’écho d’une structure :
//Procedure echoStruct; // var inst:DSService; structin,structout:DSSOAPStruct; begin // La structure est une classe 1000 structin := ClassManager.CreateInstance('DSSOAPStruct'); structin.varString := 'coucou'; structin.varInt := 1; structin.varFloat := 1.0; // appel du service inst := ClassManager.CreateInstance('DSService'); // l’instance en retour est créée par le framework inst.echoStruct(structin,structout); showMessage('Result:'+structout.varString); end;
Les extensions de type complexe.
Une extension de type complexe permet d’enrichir un type complexe préalablement défini, la correspondance en Ligne 1000 est une classe héritant de la classe enrichie.
Note : Lors de la publication d’un service toutes les classes dérivées d’une classe publiée sont publiées. |
Les tableaux et références.
Le protocole SOAP permet d’échanger des ensembles de données sous forme de tableaux. L’implémentation de 1000 distingue les tableaux de type simple des tableaux de structure.
Scénarii | Correspondance ligne 1000 |
---|---|
Tableau de type simple (t1) passé en paramètre | Tableau ouvert du type simple (t1) dans les paramètres de l’opération
const p : Array of t1 ; |
Tableau de structure (s1) en paramètre | Liste d’objet de la classe représentant la structure (s1) dans les paramètres de l’opération
const p : s1List ; |
Tableau de structure (s1) dans une structure (s2) | Rôle liste vers la structure (s1) du tableau dans la classe de la structure (s2) |
Type complexe (s1) référençant un type complexe (s2) | Rôle référence vers la structure (s1) dans la classe de la structure (s2) |
Tableau de type simple (t1) dans une structure (s2). | Rôle liste vers une classe wrapper du type simple (t1) dans la classe de la structure (s2) |
Tableau de type simple passé en paramètre.
Exemple:
Procedure echoArrayOfString(const input:Array of string; out output:Array of string); var idx:Integer; begin // Implémentation du service // Copie le tableau élément par élément. // for idx:=0 to length(input)-1 do output[idx] := input[idx]; End ;
Pour appeler ce service :
Procedure echoArrayOfString ; var inst:ICinteropService; sin,sout:Array of string; idx:Integer; s1,s2:string; begin inst := ICinteropService.Create; // Initialise le tableau par une constante sin := ['Ah','que','coucou']; // Appel du service, sout sera initialisée // par la couche soap inst.echoArrayOfString(sin,sout); // Verification // s1 := ''; for idx:=0 to length(sin)-1 do s1 := s1+sin[idx]; s2 := ''; for idx:=0 to length(sout)-1 do s2 := s2+sout[idx]; if s1<>s2 then showMessage('echoArrayString failed :'+s1+':'+s2) else showMessage('echoArrayString OK'); end;
Tableau de type complexe passé en paramètre.
Un type complexe étant une classe un tableau de type complexe est une liste d’objet de cette classe
Par exemple pour échanger une liste de SOAPStruct
Type InteropStruct1 = Class(TitObject) public InteropStruct2List: CompositionList of InteropStruct2; InteropStruct2Ref: InteropStruct2; InteropStruct3Ref: InteropStruct3; oidInteropStruct2Ref: string; oidInteropStruct3Ref: string; unCode1: string; end; InteropStruct2 = Class(TitObject) public InteropStruct1Ref: InteropStruct1; oidInteropStruct1Ref: string; unCode2: string; end; InteropStruct3 = Class(TitObject) public unCode3: string; end; InteropService = Class(Service) public Procedure echoStruct1Array(const input:InteropStruct1List; out output:InteropStruct1List); end;
Pour appeler ce service :
Procedure echoStructArray; var inst:ICinteropService; sin,sout:ICInteropStruct1List; s1i,s1o:ICInteropStruct1; idx:Integer; s2:ICInteropStruct2; begin inst := ICinteropService.Create; // Force l’appel distant inst.LoopMode := mlmNone; // Création de la liste d’appel. // sin := ClassManager.CreateObjectList('ICInteropStruct1'); // s1i := ICInteropStruct1.Create; s1i.unCode1 := 'code1'; for idx:=0 to 1 do begin s2 := ICInteropStruct2.Create; s2.unCode2 := 'code1-'+inttostr(idx); s1i.InteropStruct2List.AddRef(s2); end; sin.AddRef(s1i); // Appel du service // la liste sout sera initialisée // par la couche soap inst.echoStruct1Array(sin,sout); // Vérification // if sin.Count<>sout.Count then begin showMessage('echoStruct1Array failed, struct count'); Exit; end; // Test du premier élément s1i := sin.Refs[0]; s1o := sout.Refs[0]; if s1i.unCode1<>s1o.unCode1 then begin showMessage('echoStruct1Array failed, struc1.unCode1'); Exit; end; // // if s1i.InteropStruct2List.Count<>s1o.InteropStruct2List.Count then begin showMessage('echoStruct1Array failed, struc1.struct2.count'); Exit; end; // Test de la liste imbriquée // for idx:=0 to s1i.InteropStruct2List.Count-1 do if (s1i.InteropStruct2List.Refs[idx].unCode2<>s1o.InteropStruct2List.Refs[idx].unCode2) then begin showMessage('echoStruct1Array failed, struc1.struct2[].unCode2'); Exit; end; showMessage('echoStruct1 OK'); end;
Tableau de structure dans une structure.
Un type complexe SOAP peut contenir des tableaux, 1000 modélise un tableau contenu dans un type complexe par un rôle liste composition.
Voir l’exemple précédent.
Type complexe référençant un élément de type complexe.
Un type complexe SOAP peut référencer un élément de type complexe, 1000 modélise l’élément comme une référence composition référençant le type complexe.
Tableau de type simple dans une structure.
La ligne 1000 ne permet pas de manipuler nativement des tableaux de type simple comme attribut de classes. Pour permettre l’échange de ces tableaux elle définit des classes Wrapper de types simples puis utilise des tableaux sur ces classes.
Exemple :
Déclaration du service :
unit InterpServicePackage interface Type InteropService = Class(TitObject) public Procedure echoStruct5(const input:InteropStruct5; out output:InteropStruct5); end; InteropStringWrapper = Class(TitObject) // cette classe a l’option Wrapper de type simple coché dans ses propriétés soap public data : string; // les classes wrapper doivent avoir un attribut data end; Interopstruct5 = Class(TitObject) public unArrayOfString: List of InteropStringWrapper; unCode: string; end; Implementation {InteropService} Procedure InteropService.echoStruct5(const input:InteropStruct5; out output:InteropStruct5); begin output := input; end; end.
Lorsque ce service est publié en WSDL, la classe wrapper n’apparait pas et un élément de type tableau est publié :
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.sage.com/fr/line1000/InterpServicePackage" xmlns:tns="http://www.sage.com/fr/line1000/InterpServicePackage"> <wsdl:types> <xsd:schema elementFormDefault="qualified" targetNamespace="http://www.sage.com/fr/line1000/InterpServicePackage" xmlns="http://www.w3.org/2001/XMLSchema"> <element name="echoStruct5"> <complexType> <sequence> <element name="input" type="tns:Interopstruct5" minOccurs="0" maxOccurs="1"/> </sequence> </complexType> </element> <complexType name="Interopstruct5"> <sequence> <element name="unArrayOfString" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/> <element name="unCode" type="xsd:string"/> </sequence> </complexType> <element name="echoStruct5Response"> <complexType> <sequence> <element name="output" type="tns:Interopstruct5" minOccurs="0" maxOccurs="1"/> </sequence> </complexType> </element> </xsd:schema> </wsdl:types> <wsdl:message name="echoStruct5"> <wsdl:part name="parameters" element="tns:echoStruct5"/> </wsdl:message> <wsdl:message name="echoStruct5Response"> <wsdl:part name="parameters" element="tns:echoStruct5Response"/> </wsdl:message> <wsdl:portType name="interopservicePortType"> <wsdl:operation name="echoStruct5"> <wsdl:input message="tns:echoStruct5" name="echoStruct5"/> <wsdl:output message="tns:echoStruct5Response" name="echoStruct5Respond"/> </wsdl:operation> </wsdl:portType>
L’import du wsdl de ce service génère le modèle suivant :
L’élément unArrayOfString, tableau de type simple, génère une classe wrapper et une référence liste sur cette classe.
L’appel de la fonction echStruct5 avec le passage des éléments du tableau :
Procedure TestInteropService.echoStruct5; var inst:ICinteropService; sin,sout:ICInteropStruct5; wrp:ICWrapperOfString; idx:Integer; ss,stag: string; begin inst := ICinteropService.Create; sin := ICInteropStruct5.Create; sin.unCode := 'code5'; for idx:=0 to 1 do begin wrp := ICWrapperOfString.Create; wrp.data := 'data-'+inttostr(idx); sin.unArrayOfString.AddRef(wrp); end; inst.echoStruct5(sin,sout); ss := ''; stag := #13; for idx:=0 to sout.unArrayOfString.Count-1 do begin ss := ss+stag+sout.unArrayOfString.refs[idx].data; end; showMessage('echoStruct5 :'+ss); end;
Ce qui génère le message suivant :
<?xml version="1.0" encoding="UTF-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soap:Body> <echoStruct5 xmlns="http://www.sage.com/fr/line1000/InterpServicePackage"> <input> <unArrayOfString>data-0</unArrayOfString> <unArrayOfString>data-1</unArrayOfString> <unCode>code5</unCode> </input> </echoStruct5> </soap:Body> </soap:Envelope>
Publication des rôles des classes utilisées comme type complexe soap.
Lorsqu’ une classe, utilisée dans un service, est publiée dans un descriptif de service (WSDL) elle y inclut tous les rôles référençant une classe du paquet de service. Un rôle référençant une classe à l’extérieur du paquet de service ne sera pas publié.