Les types de données SOAP (ws)

De Wiki1000
(Redirigé depuis Developpement:WebService3)

{{#customtitle:Les types de données SOAP et les types de données 1000}}

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 :

<source lang='delphi'> // 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; </source>

Exemple :

Cet exemple montre comment appelé un service exécutant l’écho d’un paramètre binaire :

Le service :

<source lang='delphi'> 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; </source>

Le proxy local du service :

<source lang='delphi'> 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; </source>

L’appel du service en utilisant des chaînes de caractères :

<source lang='delphi'> //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; </source>

L’appel du service en utilisant des TStringList

<source lang='delphi'> //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; </source>

Même exemple en utilisant une compression :

<source lang='delphi'> //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; </source>

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é :

<source lang='delphi'> //Défini un énuméré. EchoNum = (cstEcho1, cstEcho2) ;

//Procedure echoEnum(input:EchoEnum; out output:EchoEnum); begin

 output := input;

end; </source>

Pour appeler ce service, (le service a été importé en utilisant un préfixe de paquet IC) :

<source lang='delphi'> 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; </source>

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 :

<source lang='delphi'> //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; </source>

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:

<source lang='delphi'> 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 ; </source>

Pour appeler ce service :

<source lang='delphi'> 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; </source>

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

<source lang='delphi'> 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;

</source>

Pour appeler ce service :

<source lang='delphi'> 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; </source>

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 :

{{#images:image20.png|Web_Services_1000}}

<source lang='delphi'> 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. </source>

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 :

{{#images:image21.png|Web_Services_1000}}

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 :

<source lang='delphi'> 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; </source>

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é.

{{#if:Web Services (ws)|

{{#if:Web Services (ws)|— Web Services (ws) |}} — Développement DSM

|

{{#if:|— [[{{{1}}}]] |}} — Développement DSM

}}